/* SRMM Copyright 2000-2005 Miranda ICQ/IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. 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; either version 2 of the License, or (at your option) any later version. 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define _USE_32BIT_TIME_T #include "commonheaders.h" #pragma hdrstop #include #define TIMERID_MSGSEND 0 #define TIMERID_FLASHWND 1 #define TIMERID_TYPE 2 #define TIMEOUT_FLASHWND 900 #define TIMEOUT_TYPEOFF 10000 //send type off after 10 seconds of inactivity #define SB_CHAR_WIDTH 45; #define VALID_AVATAR(x) (x==PA_FORMAT_PNG||x==PA_FORMAT_JPEG||x==PA_FORMAT_ICON||x==PA_FORMAT_BMP||x==PA_FORMAT_GIF) #if defined(_UNICODE) #define SEND_FLAGS PREF_UNICODE #else #define SEND_FLAGS 0 #endif extern HCURSOR hCurSplitNS, hCurSplitWE, hCurHyperlinkHand; extern HANDLE hHookWinEvt; extern struct CREOleCallback reOleCallback; extern HINSTANCE g_hInst; static void UpdateReadChars(HWND hwndDlg, HWND hwndStatus); static WNDPROC OldMessageEditProc, OldSplitterProc; static const UINT infoLineControls[] = { IDC_PROTOCOL, IDC_NAME }; static const UINT buttonLineControls[] = { IDC_ADD, IDC_USERMENU, IDC_DETAILS, IDC_HISTORY }; static const UINT sendControls[] = { IDC_MESSAGE }; static void NotifyLocalWinEvent(HANDLE hContact, HWND hwnd, unsigned int type) { MessageWindowEventData mwe = { 0 }; if (hContact==NULL || hwnd==NULL) return; mwe.cbSize = sizeof(mwe); mwe.hContact = hContact; mwe.hwndWindow = hwnd; mwe.szModule = SRMMMOD; mwe.uType = type; mwe.uFlags = MSG_WINDOW_UFLAG_MSG_BOTH; NotifyEventHooks(hHookWinEvt, 0, (LPARAM)&mwe); } static char *MsgServiceName(HANDLE hContact) { #ifdef _UNICODE char szServiceName[100]; char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); if (szProto == NULL) return PSS_MESSAGE; mir_snprintf(szServiceName, SIZEOF(szServiceName), "%s%sW", szProto, PSS_MESSAGE); if (ServiceExists(szServiceName)) return PSS_MESSAGE "W"; #endif return PSS_MESSAGE; } #if defined(_UNICODE) static int RTL_Detect(WCHAR *pszwText) { WORD *infoTypeC2; int i; int iLen = lstrlenW(pszwText); infoTypeC2 = (WORD *)malloc(sizeof(WORD) * (iLen + 2)); if(infoTypeC2) { ZeroMemory(infoTypeC2, sizeof(WORD) * (iLen + 2)); GetStringTypeW(CT_CTYPE2, pszwText, iLen, infoTypeC2); for(i = 0; i < iLen; i++) { if(infoTypeC2[i] == C2_RIGHTTOLEFT) { free(infoTypeC2); //_DebugTraceA("RTL text found"); return 1; } } free(infoTypeC2); //_DebugTraceA("NO RTL text detected"); } return 0; } #endif // mod from tabsrmm static void AddToFileList(char ***pppFiles,int *totalCount,const char *szFilename) { *pppFiles=(char**)realloc(*pppFiles,(++*totalCount+1)*sizeof(char*)); (*pppFiles)[*totalCount]=NULL; (*pppFiles)[*totalCount-1]=_strdup(szFilename); if(GetFileAttributesA(szFilename)&FILE_ATTRIBUTE_DIRECTORY) { WIN32_FIND_DATAA fd; HANDLE hFind; char szPath[MAX_PATH]; lstrcpyA(szPath,szFilename); lstrcatA(szPath,"\\*"); if(hFind=FindFirstFileA(szPath,&fd)) { do { if(!lstrcmpA(fd.cFileName,".") || !lstrcmpA(fd.cFileName,"..")) continue; lstrcpyA(szPath,szFilename); lstrcatA(szPath,"\\"); lstrcatA(szPath,fd.cFileName); AddToFileList(pppFiles,totalCount,szPath); } while(FindNextFileA(hFind,&fd)); FindClose(hFind); } } } static void ShowMultipleControls(HWND hwndDlg, const UINT * controls, int cControls, int state) { int i; for (i = 0; i < cControls; i++) ShowWindow(GetDlgItem(hwndDlg, controls[i]), state); } static void SetDialogToType(HWND hwndDlg) { struct MessageWindowData *dat; WINDOWPLACEMENT pl = { 0 }; dat = (struct MessageWindowData *) GetWindowLong(hwndDlg, GWL_USERDATA); if (dat->hContact) { ShowMultipleControls(hwndDlg, infoLineControls, SIZEOF(infoLineControls), (g_dat->flags&SMF_SHOWINFO) ? SW_SHOW : SW_HIDE); } else ShowMultipleControls(hwndDlg, infoLineControls, SIZEOF(infoLineControls), SW_HIDE); if (dat->hContact) { ShowMultipleControls(hwndDlg, buttonLineControls, SIZEOF(buttonLineControls), (g_dat->flags&SMF_SHOWBTNS) ? SW_SHOW : SW_HIDE); if (!DBGetContactSettingByte(dat->hContact, "CList", "NotOnList", 0)) ShowWindow(GetDlgItem(hwndDlg, IDC_ADD), SW_HIDE); } else { ShowMultipleControls(hwndDlg, buttonLineControls, SIZEOF(buttonLineControls), SW_HIDE); } ShowMultipleControls(hwndDlg, sendControls, SIZEOF(sendControls), SW_SHOW); if (!dat->hwndStatus) { dat->hwndStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0, hwndDlg, NULL, g_hInst, NULL); SendMessage(dat->hwndStatus, SB_SETMINHEIGHT, GetSystemMetrics(SM_CYSMICON), 0); } if (DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_CHARCOUNT, SRMSGDEFSET_CHARCOUNT)) { RECT rc; int statwidths[2]; GetWindowRect(dat->hwndStatus, &rc); statwidths[0] = rc.right - rc.left - SB_CHAR_WIDTH; statwidths[1] = -1; SendMessage(dat->hwndStatus, SB_SETPARTS, 2, (LPARAM) statwidths); } else { int statwidths[] = { -1 }; SendMessage(dat->hwndStatus, SB_SETPARTS, 1, (LPARAM) statwidths); } UpdateReadChars(hwndDlg, dat->hwndStatus); ShowWindow(GetDlgItem(hwndDlg, IDCANCEL), SW_HIDE); ShowWindow(GetDlgItem(hwndDlg, IDC_SPLITTER), SW_SHOW); ShowWindow(GetDlgItem(hwndDlg, IDOK), (g_dat->flags&SMF_SENDBTN) ? SW_SHOW : SW_HIDE); EnableWindow(GetDlgItem(hwndDlg, IDOK), GetWindowTextLength(GetDlgItem(hwndDlg, IDC_MESSAGE))?TRUE:FALSE); if (dat->avatarPic==0||!(g_dat->flags&SMF_AVATAR)) ShowWindow(GetDlgItem(hwndDlg, IDC_AVATAR), SW_HIDE); SendMessage(hwndDlg, DM_UPDATETITLE, 0, 0); SendMessage(hwndDlg, WM_SIZE, 0, 0); pl.length = sizeof(pl); GetWindowPlacement(hwndDlg, &pl); if (!IsWindowVisible(hwndDlg)) pl.showCmd = SW_HIDE; SetWindowPlacement(hwndDlg, &pl); //in case size is smaller than new minimum } struct SavedMessageData { UINT message; WPARAM wParam; LPARAM lParam; DWORD keyStates; //use MOD_ defines from RegisterHotKey() }; struct MsgEditSubclassData { DWORD lastEnterTime; struct SavedMessageData *keyboardMsgQueue; int msgQueueCount; }; static void SaveKeyboardMessage(struct MsgEditSubclassData *dat, UINT message, WPARAM wParam, LPARAM lParam) { dat->keyboardMsgQueue = (struct SavedMessageData *) realloc(dat->keyboardMsgQueue, sizeof(struct SavedMessageData) * (dat->msgQueueCount + 1)); dat->keyboardMsgQueue[dat->msgQueueCount].message = message; dat->keyboardMsgQueue[dat->msgQueueCount].wParam = wParam; dat->keyboardMsgQueue[dat->msgQueueCount].lParam = lParam; dat->keyboardMsgQueue[dat->msgQueueCount].keyStates = (GetKeyState(VK_SHIFT) & 0x8000 ? MOD_SHIFT : 0) | (GetKeyState(VK_CONTROL) & 0x8000 ? MOD_CONTROL : 0) | (GetKeyState(VK_MENU) & 0x8000 ? MOD_ALT : 0); dat->msgQueueCount++; } #define EM_REPLAYSAVEDKEYSTROKES (WM_USER+0x100) #define EM_SUBCLASSED (WM_USER+0x101) #define EM_UNSUBCLASSED (WM_USER+0x102) #define ENTERCLICKTIME 1000 //max time in ms during which a double-tap on enter will cause a send #define EDITMSGQUEUE_PASSTHRUCLIPBOARD //if set the typing queue won't capture ctrl-C etc because people might want to use them on the read only text //todo: decide if this should be set or not static LRESULT CALLBACK MessageEditSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { struct MsgEditSubclassData *dat; struct MessageWindowData *pdat; pdat=(struct MessageWindowData *)GetWindowLong(GetParent(hwnd),GWL_USERDATA); dat = (struct MsgEditSubclassData *) GetWindowLong(hwnd, GWL_USERDATA); switch (msg) { case WM_DROPFILES: SendMessage(GetParent(hwnd), WM_DROPFILES, (WPARAM)wParam, (LPARAM)lParam); break; case EM_SUBCLASSED: dat = (struct MsgEditSubclassData *) malloc(sizeof(struct MsgEditSubclassData)); SetWindowLong(hwnd, GWL_USERDATA, (LONG) dat); dat->lastEnterTime = 0; dat->keyboardMsgQueue = NULL; dat->msgQueueCount = 0; return 0; case EM_SETREADONLY: if (wParam) { if (dat->keyboardMsgQueue) free(dat->keyboardMsgQueue); dat->keyboardMsgQueue = NULL; dat->msgQueueCount = 0; } break; case EM_REPLAYSAVEDKEYSTROKES: { int i; BYTE keyStateArray[256], originalKeyStateArray[256]; MSG msg; while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } GetKeyboardState(originalKeyStateArray); GetKeyboardState(keyStateArray); for (i = 0; i < dat->msgQueueCount; i++) { keyStateArray[VK_SHIFT] = dat->keyboardMsgQueue[i].keyStates & MOD_SHIFT ? 0x80 : 0; keyStateArray[VK_CONTROL] = dat->keyboardMsgQueue[i].keyStates & MOD_CONTROL ? 0x80 : 0; keyStateArray[VK_MENU] = dat->keyboardMsgQueue[i].keyStates & MOD_ALT ? 0x80 : 0; SetKeyboardState(keyStateArray); PostMessage(hwnd, dat->keyboardMsgQueue[i].message, dat->keyboardMsgQueue[i].wParam, dat->keyboardMsgQueue[i].lParam); while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } if (dat->keyboardMsgQueue) free(dat->keyboardMsgQueue); dat->keyboardMsgQueue = NULL; dat->msgQueueCount = 0; SetKeyboardState(originalKeyStateArray); return 0; } case WM_CHAR: if (GetWindowLong(hwnd, GWL_STYLE) & ES_READONLY) break; //for saved msg queue the keyup/keydowns generate wm_chars themselves if (wParam == '\n' || wParam == '\r') { if (((GetKeyState(VK_CONTROL) & 0x8000) != 0) ^ (0 != DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_SENDONENTER, SRMSGDEFSET_SENDONENTER))) { PostMessage(GetParent(hwnd), WM_COMMAND, IDOK, 0); return 0; } if (DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_SENDONDBLENTER, SRMSGDEFSET_SENDONDBLENTER)) { if (dat->lastEnterTime + ENTERCLICKTIME < GetTickCount()) dat->lastEnterTime = GetTickCount(); else { SendMessage(hwnd, WM_CHAR, '\b', 0); PostMessage(GetParent(hwnd), WM_COMMAND, IDOK, 0); return 0; } } } else dat->lastEnterTime = 0; if (wParam == 1 && GetKeyState(VK_CONTROL) & 0x8000) { //ctrl-a SendMessage(hwnd, EM_SETSEL, 0, -1); return 0; } if (wParam == 23 && GetKeyState(VK_CONTROL) & 0x8000) { // ctrl-w SendMessage(GetParent(hwnd), WM_CLOSE, 0, 0); return 0; } if (wParam == 127 && GetKeyState(VK_CONTROL) & 0x8000) { //ctrl-backspace DWORD start, end; TCHAR *text; int textLen; SendMessage(hwnd, EM_GETSEL, (WPARAM) & end, (LPARAM) (PDWORD) NULL); SendMessage(hwnd, WM_KEYDOWN, VK_LEFT, 0); SendMessage(hwnd, EM_GETSEL, (WPARAM) & start, (LPARAM) (PDWORD) NULL); textLen = GetWindowTextLength(hwnd); text = (TCHAR *) malloc(sizeof(TCHAR) * (textLen + 1)); GetWindowText(hwnd, text, textLen + 1); MoveMemory(text + start, text + end, sizeof(TCHAR) * (textLen + 1 - end)); SetWindowText(hwnd, text); free(text); SendMessage(hwnd, EM_SETSEL, start, start); SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), EN_CHANGE), (LPARAM) hwnd); return 0; } break; case WM_KEYUP: if (GetWindowLong(hwnd, GWL_STYLE) & ES_READONLY) { int i; //mustn't allow keyups for which there is no keydown for (i = 0; i < dat->msgQueueCount; i++) if (dat->keyboardMsgQueue[i].message == WM_KEYDOWN && dat->keyboardMsgQueue[i].wParam == wParam) break; if (i == dat->msgQueueCount) break; #ifdef EDITMSGQUEUE_PASSTHRUCLIPBOARD if (GetKeyState(VK_CONTROL) & 0x8000) { if (wParam == 'X' || wParam == 'C' || wParam == 'V' || wParam == VK_INSERT) break; } if (GetKeyState(VK_SHIFT) & 0x8000) { if (wParam == VK_INSERT || wParam == VK_DELETE) break; } #endif SaveKeyboardMessage(dat, msg, wParam, lParam); return 0; } break; case WM_KEYDOWN: if (GetWindowLong(hwnd, GWL_STYLE) & ES_READONLY) { #ifdef EDITMSGQUEUE_PASSTHRUCLIPBOARD if (GetKeyState(VK_CONTROL) & 0x8000) { if (wParam == 'X' || wParam == 'C' || wParam == 'V' || wParam == VK_INSERT) break; } if (GetKeyState(VK_SHIFT) & 0x8000) { if (wParam == VK_INSERT || wParam == VK_DELETE) break; } #endif SaveKeyboardMessage(dat, msg, wParam, lParam); return 0; } if (wParam == VK_UP && (GetKeyState(VK_CONTROL) & 0x8000) && DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_CTRLSUPPORT, SRMSGDEFSET_CTRLSUPPORT) && !DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_AUTOCLOSE, SRMSGDEFSET_AUTOCLOSE)) { if (pdat->cmdList) { if (!pdat->cmdListCurrent) { pdat->cmdListCurrent = tcmdlist_last(pdat->cmdList); SendMessage(hwnd, WM_SETREDRAW, FALSE, 0); SetWindowText(hwnd, pdat->cmdListCurrent->szCmd); SendMessage(hwnd, EM_SCROLLCARET, 0,0); SendMessage(hwnd, WM_SETREDRAW, TRUE, 0); SendMessage(hwnd, EM_SETSEL, 0, -1); } else if (pdat->cmdListCurrent->prev) { pdat->cmdListCurrent = pdat->cmdListCurrent->prev; SendMessage(hwnd, WM_SETREDRAW, FALSE, 0); SetWindowText(hwnd, pdat->cmdListCurrent->szCmd); SendMessage(hwnd, EM_SCROLLCARET, 0,0); SendMessage(hwnd, WM_SETREDRAW, TRUE, 0); SendMessage(hwnd, EM_SETSEL, 0, -1); } } EnableWindow(GetDlgItem(GetParent(hwnd), IDOK), GetWindowTextLength(GetDlgItem(GetParent(hwnd), IDC_MESSAGE)) != 0); UpdateReadChars(GetParent(hwnd), pdat->hwndStatus); } else if (wParam == VK_DOWN && (GetKeyState(VK_CONTROL) & 0x8000) && DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_CTRLSUPPORT, SRMSGDEFSET_CTRLSUPPORT) && !DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_AUTOCLOSE, SRMSGDEFSET_AUTOCLOSE)) { if (pdat->cmdList) { if (!pdat->cmdListCurrent) pdat->cmdListCurrent = tcmdlist_last(pdat->cmdList); if (pdat->cmdListCurrent->next) { pdat->cmdListCurrent = pdat->cmdListCurrent->next; SendMessage(hwnd, WM_SETREDRAW, FALSE, 0); SetWindowText(hwnd, pdat->cmdListCurrent->szCmd); SendMessage(hwnd, EM_SCROLLCARET, 0,0); SendMessage(hwnd, WM_SETREDRAW, TRUE, 0); SendMessage(hwnd, EM_SETSEL, 0, -1); } else { pdat->cmdListCurrent = 0; SetWindowTextA(hwnd, ""); } } EnableWindow(GetDlgItem(GetParent(hwnd), IDOK), GetWindowTextLength(GetDlgItem(GetParent(hwnd), IDC_MESSAGE)) != 0); UpdateReadChars(GetParent(hwnd), pdat->hwndStatus); } if (wParam == VK_RETURN) break; //fall through case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: case WM_MOUSEWHEEL: case WM_KILLFOCUS: dat->lastEnterTime = 0; break; case WM_SYSCHAR: dat->lastEnterTime = 0; if ((wParam == 's' || wParam == 'S') && GetKeyState(VK_MENU) & 0x8000) { if (GetWindowLong(hwnd, GWL_STYLE) & ES_READONLY) SaveKeyboardMessage(dat, msg, wParam, lParam); else PostMessage(GetParent(hwnd), WM_COMMAND, IDOK, 0); return 0; } break; case EM_UNSUBCLASSED: if (dat->keyboardMsgQueue) free(dat->keyboardMsgQueue); free(dat); return 0; } return CallWindowProc(OldMessageEditProc, hwnd, msg, wParam, lParam); } static LRESULT CALLBACK SplitterSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_NCHITTEST: return HTCLIENT; case WM_SETCURSOR: { RECT rc; GetClientRect(hwnd, &rc); SetCursor(rc.right > rc.bottom ? hCurSplitNS : hCurSplitWE); return TRUE; } case WM_LBUTTONDOWN: SetCapture(hwnd); return 0; case WM_MOUSEMOVE: if (GetCapture() == hwnd) { RECT rc; GetClientRect(hwnd, &rc); SendMessage(GetParent(hwnd), DM_SPLITTERMOVED, rc.right > rc.bottom ? (short) HIWORD(GetMessagePos()) + rc.bottom / 2 : (short) LOWORD(GetMessagePos()) + rc.right / 2, (LPARAM) hwnd); } return 0; case WM_LBUTTONUP: ReleaseCapture(); return 0; } return CallWindowProc(OldSplitterProc, hwnd, msg, wParam, lParam); } static int MessageDialogResize(HWND hwndDlg, LPARAM lParam, UTILRESIZECONTROL * urc) { struct MessageWindowData *dat = (struct MessageWindowData *) lParam; if (!(g_dat->flags&SMF_SHOWINFO) && !(g_dat->flags&SMF_SHOWBTNS)) { int i; for (i = 0; i < SIZEOF(buttonLineControls); i++) if (buttonLineControls[i] == urc->wId) OffsetRect(&urc->rcItem, 0, -dat->lineHeight); } switch (urc->wId) { case IDC_NAME: { int len; HWND h; h = GetDlgItem(hwndDlg, IDC_NAME); len = GetWindowTextLength(h); if (len > 0) { HDC hdc; SIZE textSize; TCHAR buf[256]; HFONT hFont; GetWindowText(h, buf, SIZEOF(buf)); hdc = GetDC(h); hFont = SelectObject(hdc, (HFONT) SendMessage(GetDlgItem(hwndDlg, IDOK), WM_GETFONT, 0, 0)); GetTextExtentPoint32(hdc, buf, lstrlen(buf), &textSize); urc->rcItem.right = urc->rcItem.left + textSize.cx + 10; if ((g_dat->flags&SMF_SHOWBTNS) && urc->rcItem.right > urc->dlgNewSize.cx - dat->nLabelRight) urc->rcItem.right = urc->dlgNewSize.cx - dat->nLabelRight; SelectObject(hdc, hFont); ReleaseDC(h, hdc); } } case IDC_PROTOCOL: return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; case IDC_ADD: case IDC_USERMENU: case IDC_DETAILS: case IDC_HISTORY: return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP; case IDC_LOG: if (!(g_dat->flags&SMF_SHOWINFO) && !(g_dat->flags&SMF_SHOWBTNS)) urc->rcItem.top -= dat->lineHeight; urc->rcItem.bottom -= dat->splitterPos - dat->originalSplitterPos; return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; case IDC_SPLITTER: urc->rcItem.top -= dat->splitterPos - dat->originalSplitterPos; urc->rcItem.bottom -= dat->splitterPos - dat->originalSplitterPos; return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; case IDC_MESSAGE: { if (!(g_dat->flags&SMF_SENDBTN)) urc->rcItem.right = urc->dlgNewSize.cx - urc->rcItem.left; if ((g_dat->flags&SMF_AVATAR)&&dat->avatarPic) { urc->rcItem.left = dat->avatarWidth+4; } urc->rcItem.top -= dat->splitterPos - dat->originalSplitterPos; if (!(g_dat->flags&SMF_SENDBTN)) return RD_ANCHORX_CUSTOM | RD_ANCHORY_BOTTOM; return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; } case IDCANCEL: case IDOK: urc->rcItem.top -= dat->splitterPos - dat->originalSplitterPos; return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; case IDC_AVATAR: urc->rcItem.top=urc->rcItem.bottom-(dat->avatarHeight + 2); urc->rcItem.right=urc->rcItem.left+(dat->avatarWidth + 2); return RD_ANCHORX_LEFT|RD_ANCHORY_BOTTOM; } return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; } static void UpdateReadChars(HWND hwndDlg, HWND hwndStatus) { if (hwndStatus && SendMessage(hwndStatus, SB_GETPARTS, 0, 0) == 2) { TCHAR buf[128]; int len = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_MESSAGE)); mir_sntprintf(buf, SIZEOF(buf), _T("%d"), len); SendMessage(hwndStatus, SB_SETTEXT, 1, (LPARAM) buf); } } void ShowAvatar(HWND hwndDlg, struct MessageWindowData *dat) { DBVARIANT dbv; if (dat->avatarPic) { DeleteObject(dat->avatarPic); dat->avatarPic=0; } if (!DBGetContactSetting(dat->hContact, SRMMMOD, SRMSGSET_AVATAR, &dbv)) { if(dbv.pszVal) { HANDLE hFile; hFile = CreateFileA(dbv.pszVal, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile!=INVALID_HANDLE_VALUE) { dat->avatarPic=(HBITMAP)CallService(MS_UTILS_LOADBITMAP,0,(LPARAM)dbv.pszVal); CloseHandle(hFile); } } DBFreeVariant(&dbv); } SendMessage(hwndDlg, DM_UPDATESIZEBAR, 0, 0); SendMessage(hwndDlg, DM_AVATARSIZECHANGE, 0, 0); } static void NotifyTyping(struct MessageWindowData *dat, int mode) { DWORD protoStatus; DWORD protoCaps; DWORD typeCaps; if (!dat->hContact) return; // Don't send to protocols who don't support typing // Don't send to users who are unchecked in the typing notification options // Don't send to protocols that are offline // Don't send to users who are not visible and // Don't send to users who are not on the visible list when you are in invisible mode. if (!DBGetContactSettingByte(dat->hContact, SRMMMOD, SRMSGSET_TYPING, DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_TYPINGNEW, SRMSGDEFSET_TYPINGNEW))) return; if (!dat->szProto) return; protoStatus = CallProtoService(dat->szProto, PS_GETSTATUS, 0, 0); protoCaps = CallProtoService(dat->szProto, PS_GETCAPS, PFLAGNUM_1, 0); typeCaps = CallProtoService(dat->szProto, PS_GETCAPS, PFLAGNUM_4, 0); if (!(typeCaps & PF4_SUPPORTTYPING)) return; if (protoStatus < ID_STATUS_ONLINE) return; if (protoCaps & PF1_VISLIST && DBGetContactSettingWord(dat->hContact, dat->szProto, "ApparentMode", 0) == ID_STATUS_OFFLINE) return; if (protoCaps & PF1_INVISLIST && protoStatus == ID_STATUS_INVISIBLE && DBGetContactSettingWord(dat->hContact, dat->szProto, "ApparentMode", 0) != ID_STATUS_ONLINE) return; if (DBGetContactSettingByte(dat->hContact, "CList", "NotOnList", 0) && !DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_TYPINGUNKNOWN, SRMSGDEFSET_TYPINGUNKNOWN)) return; // End user check dat->nTypeMode = mode; CallService(MS_PROTO_SELFISTYPING, (WPARAM) dat->hContact, dat->nTypeMode); } BOOL CALLBACK DlgProcMessage(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { struct MessageWindowData *dat; dat = (struct MessageWindowData *) GetWindowLong(hwndDlg, GWL_USERDATA); switch (msg) { case WM_INITDIALOG: { struct NewMessageWindowLParam *newData = (struct NewMessageWindowLParam *) lParam; TranslateDialogDefault(hwndDlg); dat = (struct MessageWindowData *) calloc(sizeof(struct MessageWindowData),1); SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) dat); { dat->hContact = newData->hContact; NotifyLocalWinEvent(dat->hContact, hwndDlg, MSG_WINDOW_EVT_OPENING); if (newData->szInitialText) { int len; #if defined(_UNICODE) if(newData->isWchar) SetDlgItemText(hwndDlg, IDC_MESSAGE, (TCHAR *)newData->szInitialText); else SetDlgItemTextA(hwndDlg, IDC_MESSAGE, newData->szInitialText); #else SetDlgItemTextA(hwndDlg, IDC_MESSAGE, newData->szInitialText); #endif len = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_MESSAGE)); PostMessage(GetDlgItem(hwndDlg, IDC_MESSAGE), EM_SETSEL, len, len); } } dat->szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) dat->hContact, 0); RichUtil_SubClass(GetDlgItem(hwndDlg, IDC_LOG)); { // avatar stuff dat->avatarPic = 0; dat->avatarWidth = 0; dat->avatarHeight = 0; dat->limitAvatarH = DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_LIMITAVHEIGHT, SRMSGDEFSET_LIMITAVHEIGHT)?DBGetContactSettingDword(NULL, SRMMMOD, SRMSGSET_AVHEIGHT, SRMSGDEFSET_AVHEIGHT):0; } if (dat->hContact && dat->szProto != NULL) dat->wStatus = DBGetContactSettingWord(dat->hContact, dat->szProto, "Status", ID_STATUS_OFFLINE); else dat->wStatus = ID_STATUS_OFFLINE; dat->wOldStatus = dat->wStatus; dat->hSendId = NULL; dat->hBkgBrush = NULL; dat->hDbEventFirst = NULL; dat->sendBuffer = NULL; dat->splitterPos = (int) DBGetContactSettingDword(DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_SAVEPERCONTACT, SRMSGDEFSET_SAVEPERCONTACT)?dat->hContact:NULL, SRMMMOD, "splitterPos", (DWORD) - 1); dat->windowWasCascaded = 0; dat->nFlash = 0; dat->nTypeSecs = 0; dat->nLastTyping = 0; dat->showTyping = 0; dat->cmdList = 0; dat->cmdListCurrent = 0; dat->nTypeMode = PROTOTYPE_SELFTYPING_OFF; SetTimer(hwndDlg, TIMERID_TYPE, 1000, NULL); dat->lastMessage = 0; dat->lastEventType = -1; dat->nFlashMax = DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_FLASHCOUNT, SRMSGDEFSET_FLASHCOUNT); { RECT rc, rc2; GetWindowRect(GetDlgItem(hwndDlg, IDC_USERMENU), &rc); GetWindowRect(hwndDlg, &rc2); dat->nLabelRight = rc2.right - rc.left; } { RECT rc; POINT pt; GetWindowRect(GetDlgItem(hwndDlg, IDC_SPLITTER), &rc); pt.y = (rc.top + rc.bottom) / 2; pt.x = 0; ScreenToClient(hwndDlg, &pt); dat->originalSplitterPos = pt.y; if (dat->splitterPos == -1) dat->splitterPos = dat->originalSplitterPos;// + 60; GetWindowRect(GetDlgItem(hwndDlg, IDC_ADD), &rc); dat->lineHeight = rc.bottom - rc.top + 3; } WindowList_Add(g_dat->hMessageWindowList, hwndDlg, dat->hContact); GetWindowRect(GetDlgItem(hwndDlg, IDC_MESSAGE), &dat->minEditInit); SendMessage(hwndDlg, DM_UPDATESIZEBAR, 0, 0); dat->hwndStatus = NULL; SendDlgItemMessage(hwndDlg, IDC_ADD, BM_SETIMAGE, IMAGE_ICON, (LPARAM) g_dat->hIcons[SMF_ICON_ADD]); SendDlgItemMessage(hwndDlg, IDC_DETAILS, BM_SETIMAGE, IMAGE_ICON, (LPARAM) g_dat->hIcons[SMF_ICON_USERDETAIL]); SendDlgItemMessage(hwndDlg, IDC_HISTORY, BM_SETIMAGE, IMAGE_ICON, (LPARAM) g_dat->hIcons[SMF_ICON_HISTORY]); SendDlgItemMessage(hwndDlg, IDC_USERMENU, BM_SETIMAGE, IMAGE_ICON, (LPARAM) g_dat->hIcons[SMF_ICON_ARROW]); // Make them flat buttons { int i; SendMessage(GetDlgItem(hwndDlg, IDC_NAME), BUTTONSETASFLATBTN, 0, 0); for (i = 0; i < SIZEOF(buttonLineControls); i++) SendMessage(GetDlgItem(hwndDlg, buttonLineControls[i]), BUTTONSETASFLATBTN, 0, 0); } SendMessage(GetDlgItem(hwndDlg, IDC_ADD), BUTTONADDTOOLTIP, (WPARAM) Translate("Add Contact Permanently to List"), 0); SendMessage(GetDlgItem(hwndDlg, IDC_USERMENU), BUTTONADDTOOLTIP, (WPARAM) Translate("User Menu"), 0); SendMessage(GetDlgItem(hwndDlg, IDC_DETAILS), BUTTONADDTOOLTIP, (WPARAM) Translate("View User's Details"), 0); SendMessage(GetDlgItem(hwndDlg, IDC_HISTORY), BUTTONADDTOOLTIP, (WPARAM) Translate("View User's History"), 0); EnableWindow(GetDlgItem(hwndDlg, IDC_PROTOCOL), FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_AVATAR), FALSE); SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETOLECALLBACK, 0, (LPARAM) & reOleCallback); SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_LINK); /* duh, how come we didnt use this from the start? */ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_AUTOURLDETECT, (WPARAM) TRUE, 0); if (dat->hContact) { if (dat->szProto) { int nMax; nMax = CallProtoService(dat->szProto, PS_GETCAPS, PFLAG_MAXLENOFMESSAGE, (LPARAM) dat->hContact); if (nMax) SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_LIMITTEXT, (WPARAM) nMax, 0); /* get around a lame bug in the Windows template resource code where richedits are limited to 0x7FFF */ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_LIMITTEXT, (WPARAM) sizeof(TCHAR) * 0x7FFFFFFF, 0); } } OldMessageEditProc = (WNDPROC) SetWindowLong(GetDlgItem(hwndDlg, IDC_MESSAGE), GWL_WNDPROC, (LONG) MessageEditSubclassProc); SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SUBCLASSED, 0, 0); OldSplitterProc = (WNDPROC) SetWindowLong(GetDlgItem(hwndDlg, IDC_SPLITTER), GWL_WNDPROC, (LONG) SplitterSubclassProc); if (dat->hContact) { int historyMode = DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_LOADHISTORY, SRMSGDEFSET_LOADHISTORY); // This finds the first message to display, it works like shit dat->hDbEventFirst = (HANDLE) CallService(MS_DB_EVENT_FINDFIRSTUNREAD, (WPARAM) dat->hContact, 0); switch (historyMode) { case LOADHISTORY_COUNT: { int i; HANDLE hPrevEvent; DBEVENTINFO dbei = { 0 }; dbei.cbSize = sizeof(dbei); for (i = DBGetContactSettingWord(NULL, SRMMMOD, SRMSGSET_LOADCOUNT, SRMSGDEFSET_LOADCOUNT); i > 0; i--) { if (dat->hDbEventFirst == NULL) hPrevEvent = (HANDLE) CallService(MS_DB_EVENT_FINDLAST, (WPARAM) dat->hContact, 0); else hPrevEvent = (HANDLE) CallService(MS_DB_EVENT_FINDPREV, (WPARAM) dat->hDbEventFirst, 0); if (hPrevEvent == NULL) break; dbei.cbBlob = 0; dat->hDbEventFirst = hPrevEvent; CallService(MS_DB_EVENT_GET, (WPARAM) dat->hDbEventFirst, (LPARAM) & dbei); if (!DbEventIsShown(&dbei, dat)) i++; } break; } case LOADHISTORY_TIME: { HANDLE hPrevEvent; DBEVENTINFO dbei = { 0 }; DWORD firstTime; dbei.cbSize = sizeof(dbei); if (dat->hDbEventFirst == NULL) dbei.timestamp = time(NULL); else CallService(MS_DB_EVENT_GET, (WPARAM) dat->hDbEventFirst, (LPARAM) & dbei); firstTime = dbei.timestamp - 60 * DBGetContactSettingWord(NULL, SRMMMOD, SRMSGSET_LOADTIME, SRMSGDEFSET_LOADTIME); for (;;) { if (dat->hDbEventFirst == NULL) hPrevEvent = (HANDLE) CallService(MS_DB_EVENT_FINDLAST, (WPARAM) dat->hContact, 0); else hPrevEvent = (HANDLE) CallService(MS_DB_EVENT_FINDPREV, (WPARAM) dat->hDbEventFirst, 0); if (hPrevEvent == NULL) break; dbei.cbBlob = 0; CallService(MS_DB_EVENT_GET, (WPARAM) hPrevEvent, (LPARAM) & dbei); if (dbei.timestamp < firstTime) break; dat->hDbEventFirst = hPrevEvent; } break; } } } SendMessage(hwndDlg, DM_OPTIONSAPPLIED, 1, 0); { int savePerContact = DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_SAVEPERCONTACT, SRMSGDEFSET_SAVEPERCONTACT); if (Utils_RestoreWindowPosition(hwndDlg, savePerContact ? dat->hContact : NULL, SRMMMOD, "")) { if (savePerContact) { if (Utils_RestoreWindowPositionNoMove(hwndDlg, NULL, SRMMMOD, "")) SetWindowPos(hwndDlg, 0, 0, 0, 450, 300, SWP_NOZORDER | SWP_NOMOVE); } else SetWindowPos(hwndDlg, 0, 0, 0, 450, 300, SWP_NOZORDER | SWP_NOMOVE); } if (!savePerContact && DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_CASCADE, SRMSGDEFSET_CASCADE)) WindowList_Broadcast(g_dat->hMessageWindowList, DM_CASCADENEWWINDOW, (WPARAM) hwndDlg, (LPARAM) & dat->windowWasCascaded); } { DBEVENTINFO dbei = { 0 }; HANDLE hdbEvent; dbei.cbSize = sizeof(dbei); hdbEvent = (HANDLE) CallService(MS_DB_EVENT_FINDLAST, (WPARAM) dat->hContact, 0); if (hdbEvent) { do { ZeroMemory(&dbei, sizeof(dbei)); dbei.cbSize = sizeof(dbei); CallService(MS_DB_EVENT_GET, (WPARAM) hdbEvent, (LPARAM) & dbei); if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & DBEF_SENT)) { dat->lastMessage = dbei.timestamp; SendMessage(hwndDlg, DM_UPDATELASTMESSAGE, 0, 0); break; } } while (hdbEvent = (HANDLE) CallService(MS_DB_EVENT_FINDPREV, (WPARAM) hdbEvent, 0)); } } SendMessage(hwndDlg, DM_GETAVATAR, 0, 0); ShowWindow(hwndDlg, SW_SHOWNORMAL); NotifyLocalWinEvent(dat->hContact, hwndDlg, MSG_WINDOW_EVT_OPEN); return TRUE; } case WM_CONTEXTMENU: { if (dat->hwndStatus && dat->hwndStatus == (HWND) wParam) { POINT pt; HMENU hMenu = (HMENU) CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM) dat->hContact, 0); GetCursorPos(&pt); TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, hwndDlg, NULL); DestroyMenu(hMenu); } break; } // Mod from tabsrmm case WM_DROPFILES: { if (dat->szProto==NULL) break; if (!(CallProtoService(dat->szProto, PS_GETCAPS, PFLAGNUM_1,0)&PF1_FILESEND)) break; if (dat->wStatus==ID_STATUS_OFFLINE) break; if (dat->hContact!=NULL) { HDROP hDrop; char **ppFiles=NULL; char szFilename[MAX_PATH]; int fileCount,totalCount=0,i; hDrop=(HDROP)wParam; fileCount=DragQueryFile(hDrop,-1,NULL,0); ppFiles=NULL; for(i=0;ihContact, (LPARAM)ppFiles); for(i=0;ppFiles[i];i++) free(ppFiles[i]); free(ppFiles); } break; } case HM_AVATARACK: { ACKDATA *pAck = (ACKDATA *)lParam; PROTO_AVATAR_INFORMATION *pai = (PROTO_AVATAR_INFORMATION *)pAck->hProcess; HWND hwnd = 0; if (pAck->hContact!=dat->hContact) return 0; if (pAck->type != ACKTYPE_AVATAR) return 0; if (pAck->result == ACKRESULT_STATUS) { SendMessage(hwndDlg, DM_GETAVATAR, 0, 0); break; } if (pai==NULL) return 0; if (pAck->result == ACKRESULT_SUCCESS) { if (pai->filename&&strlen(pai->filename)&&VALID_AVATAR(pai->format)) { DBWriteContactSettingString(dat->hContact, SRMMMOD, SRMSGSET_AVATAR, pai->filename); ShowAvatar(hwndDlg, dat); } } else if (pAck->result == ACKRESULT_FAILED) { DBWriteContactSettingString(dat->hContact, SRMMMOD, SRMSGSET_AVATAR, ""); ShowAvatar(hwndDlg, dat); } break; } case DM_AVATARCALCSIZE: { BITMAP bminfo; if (dat->avatarPic==0||!(g_dat->flags&SMF_AVATAR)) { dat->avatarWidth=50; dat->avatarHeight=50; ShowWindow(GetDlgItem(hwndDlg, IDC_AVATAR), SW_HIDE); return 0; } GetObject(dat->avatarPic, sizeof(bminfo), &bminfo); dat->avatarWidth=bminfo.bmWidth+2; dat->avatarHeight=bminfo.bmHeight+2; if (dat->limitAvatarH&&dat->avatarHeight>dat->limitAvatarH) { double aspect = 0; aspect = (double)dat->limitAvatarH / (double)bminfo.bmHeight; dat->avatarWidth = (int)(bminfo.bmWidth * aspect + 2); dat->avatarHeight = dat->limitAvatarH + 2; } ShowWindow(GetDlgItem(hwndDlg, IDC_AVATAR), SW_SHOW); break; } case DM_UPDATESIZEBAR: { dat->minEditBoxSize.cx = dat->minEditInit.right - dat->minEditInit.left; dat->minEditBoxSize.cy = dat->minEditInit.bottom - dat->minEditInit.top; if (g_dat->flags&SMF_AVATAR) { SendMessage(hwndDlg, DM_AVATARCALCSIZE, 0, 0); if(dat->avatarPic) { if (dat->minEditBoxSize.cy<=dat->avatarHeight) dat->minEditBoxSize.cy = dat->avatarHeight; } } break; } case DM_AVATARSIZECHANGE: { RECT rc; GetWindowRect(GetDlgItem(hwndDlg, IDC_MESSAGE), &rc); if (rc.bottom-rc.topminEditBoxSize.cy) { SendMessage(hwndDlg, DM_SPLITTERMOVED, rc.top-(rc.bottom-rc.top-dat->minEditBoxSize.cy-4), (LPARAM) GetDlgItem(hwndDlg, IDC_SPLITTER)); } SendMessage(hwndDlg, WM_SIZE, 0, 0); SendMessage(hwndDlg, DM_SCROLLLOGTOBOTTOM, 0, 0); break; } case DM_GETAVATAR: { PROTO_AVATAR_INFORMATION pai; int caps = 0, result; SetWindowLong(hwndDlg, DWL_MSGRESULT, 0); if (!(g_dat->flags&SMF_AVATAR)||!(CallProtoService(dat->szProto, PS_GETCAPS, PFLAGNUM_4, 0)&PF4_AVATARS)) { SendMessage(hwndDlg, DM_UPDATESIZEBAR, 0, 0); SendMessage(hwndDlg, DM_AVATARSIZECHANGE, 0, 0); SetWindowLong(hwndDlg, DWL_MSGRESULT, 1); return 0; } if(DBGetContactSettingWord(dat->hContact, dat->szProto, "Status", ID_STATUS_OFFLINE) == ID_STATUS_OFFLINE) { ShowAvatar(hwndDlg, dat); SetWindowLong(hwndDlg, DWL_MSGRESULT, 1); return 0; } ZeroMemory((void *)&pai, sizeof(pai)); pai.cbSize = sizeof(pai); pai.hContact = dat->hContact; pai.format = PA_FORMAT_UNKNOWN; strcpy(pai.filename, ""); result = CallProtoService(dat->szProto, PS_GETAVATARINFO, GAIF_FORCE, (LPARAM)&pai); if (result==GAIR_SUCCESS) { if (VALID_AVATAR(pai.format)) DBWriteContactSettingString(dat->hContact, SRMMMOD, SRMSGSET_AVATAR, pai.filename); else DBWriteContactSettingString(dat->hContact, SRMMMOD, SRMSGSET_AVATAR, ""); ShowAvatar(hwndDlg, dat); } else if (result == GAIR_NOAVATAR) { DBWriteContactSettingString(dat->hContact, SRMMMOD, SRMSGSET_AVATAR, ""); ShowAvatar(hwndDlg, dat); } SetWindowLong(hwndDlg, DWL_MSGRESULT, 1); break; } case DM_TYPING: { dat->nTypeSecs = (int) lParam > 0 ? (int) lParam : 0; break; } case DM_UPDATEWINICON: { if (DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_STATUSICON, SRMSGDEFSET_STATUSICON)) { WORD wStatus; if (dat->szProto) { wStatus = DBGetContactSettingWord(dat->hContact, dat->szProto, "Status", ID_STATUS_OFFLINE); SendMessage(hwndDlg, WM_SETICON, (WPARAM) ICON_BIG, (LPARAM) LoadSkinnedProtoIcon(dat->szProto, wStatus)); break; } } SendMessage(hwndDlg, WM_SETICON, (WPARAM) ICON_BIG, (LPARAM) LoadSkinnedIcon(SKINICON_EVENT_MESSAGE)); break; } case DM_USERNAMETOCLIP: { CONTACTINFO ci; char buf[128]; HGLOBAL hData; buf[0] = 0; if(dat->hContact) { ZeroMemory(&ci, sizeof(ci)); ci.cbSize = sizeof(ci); ci.hContact = dat->hContact; ci.szProto = dat->szProto; ci.dwFlag = CNF_UNIQUEID; if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM) & ci)) { switch (ci.type) { case CNFT_ASCIIZ: mir_snprintf(buf, SIZEOF(buf), "%s", ci.pszVal); miranda_sys_free(ci.pszVal); break; case CNFT_DWORD: mir_snprintf(buf, SIZEOF(buf), "%u", ci.dVal); break; } } if (!OpenClipboard(hwndDlg) || !lstrlenA(buf)) break; EmptyClipboard(); hData = GlobalAlloc(GMEM_MOVEABLE, lstrlenA(buf) + 1); lstrcpyA(GlobalLock(hData), buf); GlobalUnlock(hData); SetClipboardData(CF_TEXT, hData); CloseClipboard(); } break; } case DM_UPDATELASTMESSAGE: { if (!dat->hwndStatus || dat->nTypeSecs) break; if (dat->lastMessage) { DBTIMETOSTRINGT dbtts; TCHAR date[64], time[64], fmt[128]; dbtts.szFormat = _T("d"); dbtts.cbDest = SIZEOF(date); dbtts.szDest = date; CallService(MS_DB_TIME_TIMESTAMPTOSTRINGT, dat->lastMessage, (LPARAM) & dbtts); dbtts.szFormat = _T("t"); dbtts.cbDest = SIZEOF(time); dbtts.szDest = time; CallService(MS_DB_TIME_TIMESTAMPTOSTRINGT, dat->lastMessage, (LPARAM) & dbtts); mir_sntprintf(fmt, SIZEOF(fmt), TranslateT("Last message received on %s at %s."), date, time); SendMessage(dat->hwndStatus, SB_SETTEXT, 0, (LPARAM) fmt); SendMessage(dat->hwndStatus, SB_SETICON, 0, (LPARAM) NULL); } else { SendMessage(dat->hwndStatus, SB_SETTEXT, 0, (LPARAM) _T("")); SendMessage(dat->hwndStatus, SB_SETICON, 0, (LPARAM) NULL); } break; } case DM_OPTIONSAPPLIED: SetDialogToType(hwndDlg); if (dat->hBkgBrush) DeleteObject(dat->hBkgBrush); { COLORREF colour = DBGetContactSettingDword(NULL, SRMMMOD, SRMSGSET_BKGCOLOUR, SRMSGDEFSET_BKGCOLOUR); dat->hBkgBrush = CreateSolidBrush(colour); SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETBKGNDCOLOR, 0, colour); } { // avatar stuff dat->avatarPic = 0; dat->limitAvatarH = 0; if (CallProtoService(dat->szProto, PS_GETCAPS, PFLAGNUM_4, 0)&PF4_AVATARS) { dat->limitAvatarH = DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_LIMITAVHEIGHT, SRMSGDEFSET_LIMITAVHEIGHT)?DBGetContactSettingDword(NULL, SRMMMOD, SRMSGSET_AVHEIGHT, SRMSGDEFSET_AVHEIGHT):0; } if (!wParam) SendMessage(hwndDlg, DM_GETAVATAR, 0, 0); } InvalidateRect(GetDlgItem(hwndDlg, IDC_MESSAGE), NULL, FALSE); { HFONT hFont; LOGFONT lf; hFont = (HFONT) SendDlgItemMessage(hwndDlg, IDC_MESSAGE, WM_GETFONT, 0, 0); if (hFont != NULL && hFont != (HFONT) SendDlgItemMessage(hwndDlg, IDOK, WM_GETFONT, 0, 0)) DeleteObject(hFont); LoadMsgDlgFont(MSGFONTID_MESSAGEAREA, &lf, NULL); hFont = CreateFontIndirect(&lf); SendDlgItemMessage(hwndDlg, IDC_MESSAGE, WM_SETFONT, (WPARAM) hFont, MAKELPARAM(TRUE, 0)); } /* * configure message history for proper RTL formatting */ { PARAFORMAT2 pf2; ZeroMemory((void *)&pf2, sizeof(pf2)); pf2.cbSize = sizeof(pf2); pf2.wEffects = PFE_RTLPARA; pf2.dwMask = PFM_RTLPARA; SetDlgItemText(hwndDlg, IDC_LOG, _T("")); SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETPARAFORMAT, 0, (LPARAM)&pf2); pf2.wEffects = 0; SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETPARAFORMAT, 0, (LPARAM)&pf2); SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETLANGOPTIONS, 0, (LPARAM) SendDlgItemMessage(hwndDlg, IDC_LOG, EM_GETLANGOPTIONS, 0, 0) & ~IMF_AUTOKEYBOARD); } SendMessage(hwndDlg, DM_REMAKELOG, 0, 0); SendMessage(hwndDlg, DM_UPDATEWINICON, 0, 0); break; case DM_UPDATETITLE: { TCHAR newtitle[256], oldtitle[256], *szStatus; TCHAR *contactName, *pszNewTitleEnd; DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *) wParam; pszNewTitleEnd = _T("Message Session"); if (dat->hContact) { if (dat->szProto) { CONTACTINFO ci; char buf[128]; int statusIcon = DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_STATUSICON, SRMSGDEFSET_STATUSICON); buf[0] = 0; dat->wStatus = DBGetContactSettingWord(dat->hContact, dat->szProto, "Status", ID_STATUS_OFFLINE); contactName = ( TCHAR* )CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) dat->hContact, GCDNF_TCHAR); ZeroMemory(&ci, sizeof(ci)); ci.cbSize = sizeof(ci); ci.hContact = dat->hContact; ci.szProto = dat->szProto; ci.dwFlag = CNF_UNIQUEID; if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM) & ci)) { switch (ci.type) { case CNFT_ASCIIZ: mir_snprintf(buf, SIZEOF(buf), "%s", ci.pszVal); miranda_sys_free(ci.pszVal); break; case CNFT_DWORD: mir_snprintf(buf, SIZEOF(buf), "%u", ci.dVal); break; } } if ( buf[0] ) SetDlgItemTextA(hwndDlg, IDC_NAME, buf ); else SetDlgItemText(hwndDlg, IDC_NAME, contactName); szStatus = (TCHAR*)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, dat->szProto == NULL ? ID_STATUS_OFFLINE : DBGetContactSettingWord(dat->hContact, dat->szProto, "Status", ID_STATUS_OFFLINE), GCMDF_TCHAR); if (statusIcon) mir_sntprintf(newtitle, SIZEOF(newtitle), _T("%s - %s"), contactName, TranslateTS(pszNewTitleEnd)); else mir_sntprintf(newtitle, SIZEOF(newtitle), _T("%s (%s): %s"), contactName, szStatus, TranslateTS(pszNewTitleEnd)); if (!cws || (!strcmp(cws->szModule, dat->szProto) && !strcmp(cws->szSetting, "Status"))) { InvalidateRect(GetDlgItem(hwndDlg, IDC_PROTOCOL), NULL, TRUE); if (statusIcon) { SendMessage(hwndDlg, DM_UPDATEWINICON, 0, 0); } } // log if ((dat->wStatus != dat->wOldStatus || lParam != 0) && DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_SHOWSTATUSCH, SRMSGDEFSET_SHOWSTATUSCH)) { DBEVENTINFO dbei; TCHAR buffer[200]; HANDLE hNewEvent; int iLen; TCHAR *szOldStatus = (TCHAR*)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, (WPARAM) dat->wOldStatus, GCMDF_TCHAR); TCHAR *szNewStatus = (TCHAR*)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, (WPARAM) dat->wStatus, GCMDF_TCHAR); if (dat->wStatus == ID_STATUS_OFFLINE) { iLen = mir_sntprintf(buffer, SIZEOF(buffer), TranslateT("signed off (was %s)"), szOldStatus); SendMessage(hwndDlg, DM_TYPING, 0, 0); } else if (dat->wOldStatus == ID_STATUS_OFFLINE) iLen = mir_sntprintf(buffer, SIZEOF(buffer), TranslateT("signed on (%s)"), szNewStatus); else iLen = mir_sntprintf(buffer, SIZEOF(buffer), TranslateT("is now %s (was %s)"), szNewStatus, szOldStatus); { char* blob = ( char* )alloca( 1000 ); #if defined( _UNICODE ) int ansiLen = WideCharToMultiByte(CP_ACP, 0, buffer, -1, blob, 1000, 0, 0); memcpy( blob+ansiLen, buffer, sizeof(TCHAR)*(iLen+1)); dbei.cbBlob = ansiLen + sizeof(TCHAR)*(iLen+1); #else int wLen = MultiByteToWideChar(CP_ACP, 0, buffer, -1, NULL, 0 ); memcpy( blob, buffer, iLen+1 ); MultiByteToWideChar(CP_ACP, 0, buffer, -1, (WCHAR*)&blob[iLen+1], wLen+1 ); dbei.cbBlob = iLen+1 + sizeof(WCHAR)*wLen; #endif dbei.cbSize = sizeof(dbei); dbei.pBlob = (PBYTE) blob; dbei.eventType = EVENTTYPE_STATUSCHANGE; dbei.flags = 0; dbei.timestamp = time(NULL); dbei.szModule = dat->szProto; hNewEvent = (HANDLE) CallService(MS_DB_EVENT_ADD, (WPARAM) dat->hContact, (LPARAM) & dbei); if (dat->hDbEventFirst == NULL) { dat->hDbEventFirst = hNewEvent; SendMessage(hwndDlg, DM_REMAKELOG, 0, 0); } } } dat->wOldStatus = dat->wStatus; } } else lstrcpyn(newtitle, pszNewTitleEnd, SIZEOF(newtitle)); GetWindowText(hwndDlg, oldtitle, SIZEOF(oldtitle)); if ( lstrcmp(newtitle, oldtitle )) { //swt() flickers even if the title hasn't actually changed SetWindowText(hwndDlg, newtitle); SendMessage(hwndDlg, WM_SIZE, 0, 0); } break; } case DM_GETWINDOWSTATE: { UINT state = 0; state |= MSG_WINDOW_STATE_EXISTS; if (IsWindowVisible(hwndDlg)) state |= MSG_WINDOW_STATE_VISIBLE; if (GetForegroundWindow()==hwndDlg) state |= MSG_WINDOW_STATE_FOCUS; if (IsIconic(hwndDlg)) state |= MSG_WINDOW_STATE_ICONIC; SetWindowLong(hwndDlg, DWL_MSGRESULT, state); return TRUE; } case DM_CASCADENEWWINDOW: if ((HWND) wParam == hwndDlg) break; { RECT rcThis, rcNew; GetWindowRect(hwndDlg, &rcThis); GetWindowRect((HWND) wParam, &rcNew); if (abs(rcThis.left - rcNew.left) < 3 && abs(rcThis.top - rcNew.top) < 3) { int offset = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME); SetWindowPos((HWND) wParam, 0, rcNew.left + offset, rcNew.top + offset, 0, 0, SWP_NOZORDER | SWP_NOSIZE); *(int *) lParam = 1; } } break; case WM_ACTIVATE: if (LOWORD(wParam) != WA_ACTIVE) break; //fall through case WM_MOUSEACTIVATE: if (KillTimer(hwndDlg, TIMERID_FLASHWND)) FlashWindow(hwndDlg, FALSE); break; case WM_SETFOCUS: SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); break; case WM_GETMINMAXINFO: { MINMAXINFO *mmi = (MINMAXINFO *) lParam; RECT rcWindow, rcLog; GetWindowRect(hwndDlg, &rcWindow); GetWindowRect(GetDlgItem(hwndDlg, IDC_LOG), &rcLog); mmi->ptMinTrackSize.x = rcWindow.right - rcWindow.left - ((rcLog.right - rcLog.left) - dat->minEditBoxSize.cx); mmi->ptMinTrackSize.y = rcWindow.bottom - rcWindow.top - ((rcLog.bottom - rcLog.top) - dat->minEditBoxSize.cy); return 0; } case WM_SIZE: { UTILRESIZEDIALOG urd; if (IsIconic(hwndDlg)) break; if (dat->hwndStatus) { SendMessage(dat->hwndStatus, WM_SIZE, 0, 0); if (SendMessage(dat->hwndStatus, SB_GETPARTS, 0, 0) == 2) { int statwidths[2]; RECT rc; GetWindowRect(dat->hwndStatus, &rc); statwidths[0] = rc.right - rc.left - SB_CHAR_WIDTH; statwidths[1] = -1; SendMessage(dat->hwndStatus, SB_SETPARTS, 2, (LPARAM) statwidths); } } ZeroMemory(&urd, sizeof(urd)); urd.cbSize = sizeof(urd); urd.hInstance = g_hInst; urd.hwndDlg = hwndDlg; urd.lParam = (LPARAM) dat; urd.lpTemplate = MAKEINTRESOURCEA(IDD_MSG); urd.pfnResizer = MessageDialogResize; CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM) & urd); // The statusbar sometimes draws over these 2 controls so // redraw them if (dat->hwndStatus) { RedrawWindow(GetDlgItem(hwndDlg, IDOK), NULL, NULL, RDW_INVALIDATE); RedrawWindow(GetDlgItem(hwndDlg, IDC_MESSAGE), NULL, NULL, RDW_INVALIDATE); } if ((g_dat->flags&SMF_AVATAR)&&dat->avatarPic) RedrawWindow(GetDlgItem(hwndDlg, IDC_AVATAR), NULL, NULL, RDW_INVALIDATE); break; } case DM_SPLITTERMOVED: { POINT pt; RECT rc; RECT rcLog; GetWindowRect(GetDlgItem(hwndDlg, IDC_LOG), &rcLog); if ((HWND) lParam == GetDlgItem(hwndDlg, IDC_SPLITTER)) { int oldSplitterY; GetClientRect(hwndDlg, &rc); pt.x = 0; pt.y = wParam; ScreenToClient(hwndDlg, &pt); oldSplitterY = dat->splitterPos; dat->splitterPos = rc.bottom - pt.y + 23; GetWindowRect(GetDlgItem(hwndDlg, IDC_MESSAGE), &rc); if (rc.bottom - rc.top + (dat->splitterPos - oldSplitterY) < dat->minEditBoxSize.cy) dat->splitterPos = oldSplitterY + dat->minEditBoxSize.cy - (rc.bottom - rc.top); if (rcLog.bottom - rcLog.top - (dat->splitterPos - oldSplitterY) < dat->minEditBoxSize.cy) dat->splitterPos = oldSplitterY - dat->minEditBoxSize.cy + (rcLog.bottom - rcLog.top); } SendMessage(hwndDlg, WM_SIZE, 0, 0); break; } case DM_REMAKELOG: dat->lastEventType = -1; StreamInEvents(hwndDlg, dat->hDbEventFirst, -1, 0); break; case DM_APPENDTOLOG: //takes wParam=hDbEvent StreamInEvents(hwndDlg, (HANDLE) wParam, 1, 1); break; case DM_SCROLLLOGTOBOTTOM: { SCROLLINFO si = { 0 }; if ((GetWindowLong(GetDlgItem(hwndDlg, IDC_LOG), GWL_STYLE) & WS_VSCROLL) == 0) break; si.cbSize = sizeof(si); si.fMask = SIF_PAGE | SIF_RANGE; GetScrollInfo(GetDlgItem(hwndDlg, IDC_LOG), SB_VERT, &si); si.fMask = SIF_POS; si.nPos = si.nMax - si.nPage + 1; SetScrollInfo(GetDlgItem(hwndDlg, IDC_LOG), SB_VERT, &si, TRUE); PostMessage(GetDlgItem(hwndDlg, IDC_LOG), WM_VSCROLL, MAKEWPARAM(SB_BOTTOM, 0), 0); break; } case HM_DBEVENTADDED: if ((HANDLE) wParam != dat->hContact) break; { DBEVENTINFO dbei = { 0 }; dbei.cbSize = sizeof(dbei); dbei.cbBlob = 0; CallService(MS_DB_EVENT_GET, lParam, (LPARAM) & dbei); if (dat->hDbEventFirst == NULL) dat->hDbEventFirst = (HANDLE) lParam; if (dbei.eventType == EVENTTYPE_MESSAGE && (dbei.flags & DBEF_READ)) break; if (DbEventIsShown(&dbei, dat)) { if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT))) { if (GetForegroundWindow()==hwndDlg) SkinPlaySound("RecvMsgActive"); else SkinPlaySound("RecvMsgInactive"); } if (dbei.eventType == EVENTTYPE_MESSAGE && dat->hwndStatus && !(dbei.flags & (DBEF_SENT))) { dat->lastMessage = dbei.timestamp; SendMessage(hwndDlg, DM_UPDATELASTMESSAGE, 0, 0); } if ((HANDLE) lParam != dat->hDbEventFirst && (HANDLE) CallService(MS_DB_EVENT_FINDNEXT, lParam, 0) == NULL) SendMessage(hwndDlg, DM_APPENDTOLOG, lParam, 0); else SendMessage(hwndDlg, DM_REMAKELOG, 0, 0); if ((GetActiveWindow() != hwndDlg || GetForegroundWindow() != hwndDlg) && !(dbei.flags & DBEF_SENT) && dbei.eventType != EVENTTYPE_STATUSCHANGE) { SetTimer(hwndDlg, TIMERID_FLASHWND, TIMEOUT_FLASHWND, NULL); } } break; } case WM_TIMER: if (wParam == TIMERID_MSGSEND) { KillTimer(hwndDlg, wParam); ShowWindow(hwndDlg, SW_SHOWNORMAL); EnableWindow(hwndDlg, FALSE); CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_MSGSENDERROR), hwndDlg, ErrorDlgProc, (LPARAM) strdup( Translate("The message send timed out."))); } else if (wParam == TIMERID_FLASHWND) { FlashWindow(hwndDlg, TRUE); if (dat->nFlash > dat->nFlashMax) { KillTimer(hwndDlg, TIMERID_FLASHWND); FlashWindow(hwndDlg, FALSE); dat->nFlash = 0; } dat->nFlash++; } else if (wParam == TIMERID_TYPE) { if (dat->nTypeMode == PROTOTYPE_SELFTYPING_ON && GetTickCount() - dat->nLastTyping > TIMEOUT_TYPEOFF) { NotifyTyping(dat, PROTOTYPE_SELFTYPING_OFF); } if (dat->showTyping) { if (dat->nTypeSecs) { dat->nTypeSecs--; if (GetForegroundWindow() == hwndDlg) SendMessage(hwndDlg, DM_UPDATEWINICON, 0, 0); } else { SendMessage(hwndDlg, DM_UPDATELASTMESSAGE, 0, 0); if (g_dat->flags&SMF_SHOWTYPINGWIN) SendMessage(hwndDlg, DM_UPDATEWINICON, 0, 0); dat->showTyping = 0; } } else { if (dat->nTypeSecs) { TCHAR szBuf[256]; TCHAR *szContactName = ( TCHAR* ) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) dat->hContact, GCDNF_TCHAR); mir_sntprintf(szBuf, SIZEOF(szBuf), TranslateT("%s is typing a message..."), szContactName); dat->nTypeSecs--; SendMessage(dat->hwndStatus, SB_SETTEXT, 0, (LPARAM) szBuf); SendMessage(dat->hwndStatus, SB_SETICON, 0, (LPARAM) g_dat->hIcons[SMF_ICON_TYPING]); if ((g_dat->flags&SMF_SHOWTYPINGWIN) && GetForegroundWindow() != hwndDlg) SendMessage(hwndDlg, WM_SETICON, (WPARAM) ICON_BIG, (LPARAM) g_dat->hIcons[SMF_ICON_TYPING]); dat->showTyping = 1; } } } break; case DM_ERRORDECIDED: EnableWindow(hwndDlg, TRUE); switch (wParam) { case MSGERROR_CANCEL: EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE); SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETREADONLY, FALSE, 0); SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); break; case MSGERROR_RETRY: { if (dat->hSendId == NULL && dat->hContact == NULL) return 0; dat->hSendId = (HANDLE) CallContactService(dat->hContact, MsgServiceName(dat->hContact), SEND_FLAGS, (LPARAM) dat->sendBuffer); } SetTimer(hwndDlg, TIMERID_MSGSEND, DBGetContactSettingDword(NULL, SRMMMOD, SRMSGSET_MSGTIMEOUT, SRMSGDEFSET_MSGTIMEOUT), NULL); break; } break; case WM_CTLCOLOREDIT: { COLORREF colour; if ((HWND) lParam != GetDlgItem(hwndDlg, IDC_MESSAGE)) break; LoadMsgDlgFont(MSGFONTID_MESSAGEAREA, NULL, &colour); SetTextColor((HDC) wParam, colour); SetBkColor((HDC) wParam, DBGetContactSettingDword(NULL, SRMMMOD, SRMSGSET_BKGCOLOUR, SRMSGDEFSET_BKGCOLOUR)); return (BOOL) dat->hBkgBrush; } case WM_MEASUREITEM: return CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam); case WM_DRAWITEM: { LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT) lParam; if (dis->hwndItem == GetDlgItem(hwndDlg, IDC_PROTOCOL)) { if (dat->szProto) { HICON hIcon; int dwStatus; dwStatus = DBGetContactSettingWord(dat->hContact, dat->szProto, "Status", ID_STATUS_OFFLINE); hIcon = LoadSkinnedProtoIcon(dat->szProto, dwStatus); if (hIcon) { if (DBGetContactSettingDword(dat->hContact, dat->szProto, "IdleTS", 0)) { HIMAGELIST hImageList; hImageList = ImageList_Create(GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON), IsWinVerXPPlus()? ILC_COLOR32 | ILC_MASK : ILC_COLOR16 | ILC_MASK, 1, 0); ImageList_AddIcon(hImageList, hIcon); ImageList_DrawEx(hImageList, 0, dis->hDC, dis->rcItem.left, dis->rcItem.top, 0, 0, CLR_NONE, CLR_NONE, ILD_SELECTED); ImageList_RemoveAll(hImageList); ImageList_Destroy(hImageList); } else DrawIconEx(dis->hDC, dis->rcItem.left, dis->rcItem.top, hIcon, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0, NULL, DI_NORMAL); } } } else if (dis->hwndItem == GetDlgItem(hwndDlg, IDC_AVATAR) && dat->avatarPic && (g_dat->flags&SMF_AVATAR)) { BITMAP bminfo; HPEN hPen, hOldPen; hPen = CreatePen(PS_SOLID, 1, RGB(0,0,0)); hOldPen = SelectObject(dis->hDC, hPen); Rectangle(dis->hDC, 0, 0, dat->avatarWidth, dat->avatarHeight); SelectObject(dis->hDC,hOldPen); DeleteObject(hPen); GetObject(dat->avatarPic, sizeof(bminfo), &bminfo); { HDC hdcMem = CreateCompatibleDC(dis->hDC); HBITMAP hbmMem = (HBITMAP)SelectObject(hdcMem, dat->avatarPic); { double aspect = 0, w = 0; aspect = (double)dat->limitAvatarH / (double)bminfo.bmHeight; w = (double)bminfo.bmWidth * aspect; SetStretchBltMode(dis->hDC, HALFTONE); StretchBlt(dis->hDC, 1, 1, dat->avatarWidth-2, dat->avatarHeight-2, hdcMem, 0, 0, bminfo.bmWidth, bminfo.bmHeight, SRCCOPY); } SelectObject(hdcMem,hbmMem); DeleteDC(hdcMem); } } return CallService(MS_CLIST_MENUDRAWITEM, wParam, lParam); } case WM_COMMAND: if (CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(wParam), MPCF_CONTACTMENU), (LPARAM) dat->hContact)) break; switch (LOWORD(wParam)) { case IDOK: if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDOK))) break; { int flags = SEND_FLAGS; //this is a 'send' button int bufSize = GetWindowTextLengthA(GetDlgItem(hwndDlg, IDC_MESSAGE)) + 1; dat->sendBuffer = (char *) realloc(dat->sendBuffer, bufSize * (sizeof(TCHAR) + 1)); dat->bIsRtl = 0; GetDlgItemTextA(hwndDlg, IDC_MESSAGE, dat->sendBuffer, bufSize); #if defined( _UNICODE ) // all that crap with temporary buffers is related to the bug #0001466 (empty messages // on x64 machines). GetDlgItemTextW should use the 2-byte aligned buffer { WCHAR* temp = ( WCHAR* )alloca( bufSize * sizeof( TCHAR )); GetDlgItemTextW(hwndDlg, IDC_MESSAGE, temp, bufSize); memcpy(( TCHAR*)&dat->sendBuffer[bufSize], temp, bufSize * sizeof( TCHAR )); if ( RTL_Detect( temp )) { flags |= PREF_RTL; dat->bIsRtl = 1; } } #endif if (dat->sendBuffer[0] == 0) break; #if defined( _UNICODE ) dat->cmdList = tcmdlist_append(dat->cmdList, (TCHAR *) & dat->sendBuffer[bufSize]); #else dat->cmdList = tcmdlist_append(dat->cmdList, dat->sendBuffer); #endif dat->cmdListCurrent = 0; if (dat->nTypeMode == PROTOTYPE_SELFTYPING_ON) { NotifyTyping(dat, PROTOTYPE_SELFTYPING_OFF); } if (dat->hContact == NULL) break; //never happens dat->sendCount = 1; dat->hSendId = (HANDLE) CallContactService(dat->hContact, MsgServiceName(dat->hContact), flags, (LPARAM) dat->sendBuffer); EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE); SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETREADONLY, TRUE, 0); //create a timeout timer SetTimer(hwndDlg, TIMERID_MSGSEND, DBGetContactSettingDword(NULL, SRMMMOD, SRMSGSET_MSGTIMEOUT, SRMSGDEFSET_MSGTIMEOUT), NULL); if (DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_AUTOMIN, SRMSGDEFSET_AUTOMIN)) ShowWindow(hwndDlg, SW_MINIMIZE); } return TRUE; case IDCANCEL: DestroyWindow(hwndDlg); return TRUE; case IDC_USERMENU: case IDC_NAME: { if(GetKeyState(VK_SHIFT) & 0x8000) { // copy user name SendMessage(hwndDlg, DM_USERNAMETOCLIP, 0, 0); } else { RECT rc; HMENU hMenu = (HMENU) CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM) dat->hContact, 0); GetWindowRect(GetDlgItem(hwndDlg, LOWORD(wParam)), &rc); TrackPopupMenu(hMenu, 0, rc.left, rc.bottom, 0, hwndDlg, NULL); DestroyMenu(hMenu); } } break; case IDC_HISTORY: CallService(MS_HISTORY_SHOWCONTACTHISTORY, (WPARAM) dat->hContact, 0); break; case IDC_DETAILS: CallService(MS_USERINFO_SHOWDIALOG, (WPARAM) dat->hContact, 0); break; case IDC_ADD: { ADDCONTACTSTRUCT acs = { 0 }; acs.handle = dat->hContact; acs.handleType = HANDLE_CONTACT; acs.szProto = 0; CallService(MS_ADDCONTACT_SHOW, (WPARAM) hwndDlg, (LPARAM) & acs); } if (!DBGetContactSettingByte(dat->hContact, "CList", "NotOnList", 0)) { ShowWindow(GetDlgItem(hwndDlg, IDC_ADD), FALSE); } break; case IDC_MESSAGE: if (HIWORD(wParam) == EN_CHANGE) { int len = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_MESSAGE)); UpdateReadChars(hwndDlg, dat->hwndStatus); EnableWindow(GetDlgItem(hwndDlg, IDOK), len != 0); if (!(GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_SHIFT) & 0x8000)) { dat->nLastTyping = GetTickCount(); if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_MESSAGE))) { if (dat->nTypeMode == PROTOTYPE_SELFTYPING_OFF) { NotifyTyping(dat, PROTOTYPE_SELFTYPING_ON); } } else { if (dat->nTypeMode == PROTOTYPE_SELFTYPING_ON) { NotifyTyping(dat, PROTOTYPE_SELFTYPING_OFF); } } } } break; } break; case WM_NOTIFY: switch (((NMHDR *) lParam)->idFrom) { case IDC_LOG: switch (((NMHDR *) lParam)->code) { case EN_MSGFILTER: switch (((MSGFILTER *) lParam)->msg) { case WM_LBUTTONDOWN: { HCURSOR hCur = GetCursor(); if (hCur == LoadCursor(NULL, IDC_SIZENS) || hCur == LoadCursor(NULL, IDC_SIZEWE) || hCur == LoadCursor(NULL, IDC_SIZENESW) || hCur == LoadCursor(NULL, IDC_SIZENWSE)) { SetWindowLong(hwndDlg, DWL_MSGRESULT, TRUE); return TRUE; } break; } case WM_MOUSEMOVE: { HCURSOR hCur = GetCursor(); if (hCur == LoadCursor(NULL, IDC_SIZENS) || hCur == LoadCursor(NULL, IDC_SIZEWE) || hCur == LoadCursor(NULL, IDC_SIZENESW) || hCur == LoadCursor(NULL, IDC_SIZENWSE)) SetCursor(LoadCursor(NULL, IDC_ARROW)); break; } case WM_RBUTTONUP: { HMENU hMenu, hSubMenu; POINT pt; CHARRANGE sel, all = { 0, -1 }; hMenu = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_CONTEXT)); hSubMenu = GetSubMenu(hMenu, 0); CallService(MS_LANGPACK_TRANSLATEMENU, (WPARAM) hSubMenu, 0); SendMessage(((NMHDR *) lParam)->hwndFrom, EM_EXGETSEL, 0, (LPARAM) & sel); if (sel.cpMin == sel.cpMax) EnableMenuItem(hSubMenu, IDM_COPY, MF_BYCOMMAND | MF_GRAYED); pt.x = (short) LOWORD(((ENLINK *) lParam)->lParam); pt.y = (short) HIWORD(((ENLINK *) lParam)->lParam); ClientToScreen(((NMHDR *) lParam)->hwndFrom, &pt); switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL)) { case IDM_COPY: SendMessage(((NMHDR *) lParam)->hwndFrom, WM_COPY, 0, 0); break; case IDM_COPYALL: SendMessage(((NMHDR *) lParam)->hwndFrom, EM_EXSETSEL, 0, (LPARAM) & all); SendMessage(((NMHDR *) lParam)->hwndFrom, WM_COPY, 0, 0); SendMessage(((NMHDR *) lParam)->hwndFrom, EM_EXSETSEL, 0, (LPARAM) & sel); break; case IDM_SELECTALL: SendMessage(((NMHDR *) lParam)->hwndFrom, EM_EXSETSEL, 0, (LPARAM) & all); break; case IDM_CLEAR: SetDlgItemText(hwndDlg, IDC_LOG, _T("")); dat->hDbEventFirst = NULL; break; } DestroyMenu(hMenu); SetWindowLong(hwndDlg, DWL_MSGRESULT, TRUE); return TRUE; } } break; case EN_LINK: switch (((ENLINK *) lParam)->msg) { case WM_SETCURSOR: SetCursor(hCurHyperlinkHand); SetWindowLong(hwndDlg, DWL_MSGRESULT, TRUE); return TRUE; case WM_RBUTTONDOWN: case WM_LBUTTONUP: { TEXTRANGEA tr; CHARRANGE sel; SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXGETSEL, 0, (LPARAM) & sel); if (sel.cpMin != sel.cpMax) break; tr.chrg = ((ENLINK *) lParam)->chrg; tr.lpstrText = malloc(tr.chrg.cpMax - tr.chrg.cpMin + 8); SendDlgItemMessageA(hwndDlg, IDC_LOG, EM_GETTEXTRANGE, 0, (LPARAM) & tr); if (strchr(tr.lpstrText, '@') != NULL && strchr(tr.lpstrText, ':') == NULL && strchr(tr.lpstrText, '/') == NULL) { MoveMemory(tr.lpstrText + 7, tr.lpstrText, tr.chrg.cpMax - tr.chrg.cpMin + 1); CopyMemory(tr.lpstrText, "mailto:", 7); } if (((ENLINK *) lParam)->msg == WM_RBUTTONDOWN) { HMENU hMenu, hSubMenu; POINT pt; hMenu = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_CONTEXT)); hSubMenu = GetSubMenu(hMenu, 1); CallService(MS_LANGPACK_TRANSLATEMENU, (WPARAM) hSubMenu, 0); pt.x = (short) LOWORD(((ENLINK *) lParam)->lParam); pt.y = (short) HIWORD(((ENLINK *) lParam)->lParam); ClientToScreen(((NMHDR *) lParam)->hwndFrom, &pt); switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL)) { case IDM_OPENNEW: CallService(MS_UTILS_OPENURL, 1, (LPARAM) tr.lpstrText); break; case IDM_OPENEXISTING: CallService(MS_UTILS_OPENURL, 0, (LPARAM) tr.lpstrText); break; case IDM_COPYLINK: { HGLOBAL hData; if (!OpenClipboard(hwndDlg)) break; EmptyClipboard(); hData = GlobalAlloc(GMEM_MOVEABLE, lstrlenA(tr.lpstrText) + 1); lstrcpyA(GlobalLock(hData), tr.lpstrText); GlobalUnlock(hData); SetClipboardData(CF_TEXT, hData); CloseClipboard(); break; } } free(tr.lpstrText); DestroyMenu(hMenu); SetWindowLong(hwndDlg, DWL_MSGRESULT, TRUE); return TRUE; } else { CallService(MS_UTILS_OPENURL, 1, (LPARAM) tr.lpstrText); SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); } free(tr.lpstrText); break; } } break; } break; } break; case HM_EVENTSENT: { ACKDATA *ack = (ACKDATA *) lParam; DBEVENTINFO dbei = { 0 }; HANDLE hNewEvent; if (ack->type != ACKTYPE_MESSAGE) break; if (dat->sendCount==0) break; switch (ack->result) { case ACKRESULT_FAILED: KillTimer(hwndDlg, TIMERID_MSGSEND); ShowWindow(hwndDlg, SW_SHOWNORMAL); EnableWindow(hwndDlg, FALSE); CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_MSGSENDERROR), hwndDlg, ErrorDlgProc, (ack->lParam == 0) ? 0 : (LPARAM) strdup((char *) ack->lParam)); return 0; } if (dat->sendBuffer==NULL) return 0; dbei.cbSize = sizeof(dbei); dbei.eventType = EVENTTYPE_MESSAGE; dbei.flags = DBEF_SENT + (( dat->bIsRtl ) ? DBEF_RTL : 0 ); dbei.szModule = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) dat->hContact, 0); dbei.timestamp = time(NULL); dbei.cbBlob = lstrlenA(dat->sendBuffer) + 1; #if defined( _UNICODE ) dbei.cbBlob *= sizeof(TCHAR) + 1; #endif dbei.pBlob = (PBYTE) dat->sendBuffer; hNewEvent = (HANDLE) CallService(MS_DB_EVENT_ADD, (WPARAM) dat->hContact, (LPARAM) & dbei); SkinPlaySound("SendMsg"); if (dat->hDbEventFirst == NULL) { dat->hDbEventFirst = hNewEvent; SendMessage(hwndDlg, DM_REMAKELOG, 0, 0); } dat->hSendId = NULL; { int len; //all messages sent dat->sendCount = 0; KillTimer(hwndDlg, TIMERID_MSGSEND); SetDlgItemText(hwndDlg, IDC_MESSAGE, _T("")); EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE); SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETREADONLY, FALSE, 0); SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_REPLAYSAVEDKEYSTROKES, 0, 0); if (GetForegroundWindow() == hwndDlg) SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); len = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_MESSAGE)); UpdateReadChars(hwndDlg, dat->hwndStatus); EnableWindow(GetDlgItem(hwndDlg, IDOK), len != 0); if (DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_AUTOCLOSE, SRMSGDEFSET_AUTOCLOSE)) DestroyWindow(hwndDlg); } break; } case WM_DESTROY: NotifyLocalWinEvent(dat->hContact, hwndDlg, MSG_WINDOW_EVT_CLOSING); if (dat->nTypeMode == PROTOTYPE_SELFTYPING_ON) { NotifyTyping(dat, PROTOTYPE_SELFTYPING_OFF); } if (dat->hBkgBrush) DeleteObject(dat->hBkgBrush); if (dat->sendBuffer != NULL) free(dat->sendBuffer); if (dat->hwndStatus) DestroyWindow(dat->hwndStatus); tcmdlist_free(dat->cmdList); WindowList_Remove(g_dat->hMessageWindowList, hwndDlg); DBWriteContactSettingDword(DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_SAVEPERCONTACT, SRMSGDEFSET_SAVEPERCONTACT)?dat->hContact:NULL, SRMMMOD, "splitterPos", dat->splitterPos); SetWindowLong(GetDlgItem(hwndDlg, IDC_SPLITTER), GWL_WNDPROC, (LONG) OldSplitterProc); SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_UNSUBCLASSED, 0, 0); SetWindowLong(GetDlgItem(hwndDlg, IDC_MESSAGE), GWL_WNDPROC, (LONG) OldMessageEditProc); { HFONT hFont; hFont = (HFONT) SendDlgItemMessage(hwndDlg, IDC_MESSAGE, WM_GETFONT, 0, 0); if (hFont != NULL && hFont != (HFONT) SendDlgItemMessage(hwndDlg, IDOK, WM_GETFONT, 0, 0)) DeleteObject(hFont); } { WINDOWPLACEMENT wp = { 0 }; HANDLE hContact; if (DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_SAVEPERCONTACT, SRMSGDEFSET_SAVEPERCONTACT)) hContact = dat->hContact; else hContact = NULL; wp.length = sizeof(wp); GetWindowPlacement(hwndDlg, &wp); if (!dat->windowWasCascaded) { DBWriteContactSettingDword(hContact, SRMMMOD, "x", wp.rcNormalPosition.left); DBWriteContactSettingDword(hContact, SRMMMOD, "y", wp.rcNormalPosition.top); } DBWriteContactSettingDword(hContact, SRMMMOD, "width", wp.rcNormalPosition.right - wp.rcNormalPosition.left); DBWriteContactSettingDword(hContact, SRMMMOD, "height", wp.rcNormalPosition.bottom - wp.rcNormalPosition.top); } if (dat->avatarPic) DeleteObject(dat->avatarPic); NotifyLocalWinEvent(dat->hContact, hwndDlg, MSG_WINDOW_EVT_CLOSE); if (dat->hContact&&DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_DELTEMP, SRMSGDEFSET_DELTEMP)) { if (DBGetContactSettingByte(dat->hContact, "CList", "NotOnList", 0)) { CallService(MS_DB_CONTACT_DELETE, (WPARAM)dat->hContact, 0); } } free(dat); SetWindowLong(hwndDlg, GWL_USERDATA, 0); break; } return FALSE; }