/* Scriver Copyright 2000-2009 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. */ #include "commonheaders.h" #define SB_CHAR_WIDTH 40 #define SB_SENDING_WIDTH 25 #define SB_UNICODE_WIDTH 18 #define TIMERID_FLASHWND 1 #define TIMEOUT_FLASHWND 900 static void DrawTab(ParentWindowData *dat, HWND hwnd, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK TabCtrlProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); extern TCHAR *GetNickname(HANDLE hContact, const char* szProto); void SubclassTabCtrl(HWND hwnd) { mir_subclassWindow(hwnd, TabCtrlProc); SendMessage(hwnd, EM_SUBCLASSED, 0, 0); } void UnsubclassTabCtrl(HWND hwnd) { SendMessage(hwnd, EM_UNSUBCLASSED, 0, 0); } static const TCHAR *titleTokenNames[] = {_T("%name%"), _T("%status%"), _T("%statusmsg%"), _T("%account%")}; TCHAR* GetWindowTitle(HANDLE *hContact, const char *szProto) { int isTemplate; int i, j, len; TCHAR* tokens[4] = {0}; int tokenLen[4] = {0}; TCHAR *p, *tmplt, *title; char *accModule; TCHAR *pszNewTitleEnd = mir_tstrdup(TranslateT("Message Session")); isTemplate = 0; if (hContact && szProto) { tokens[0] = GetNickname(hContact, szProto); tokenLen[0] = lstrlen(tokens[0]); tokens[1] = mir_tstrdup((TCHAR *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, szProto ? db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE) : ID_STATUS_OFFLINE, GSMDF_TCHAR)); tokenLen[1] = lstrlen(tokens[1]); tokens[2] = DBGetStringT(hContact, "CList", "StatusMsg"); if (tokens[2] != NULL) { tokenLen[2] = (int)lstrlen(tokens[2]); for (i = j = 0; i < tokenLen[2]; i++) { if (tokens[2][i] == '\r') { continue; } else if (tokens[2][i] == '\n') { tokens[2][j++] = ' '; } else { tokens[2][j++] = tokens[2][i]; } } tokens[2][j] = '\0'; tokenLen[2] = j; } accModule = (char *) CallService(MS_PROTO_GETCONTACTBASEACCOUNT, (WPARAM) hContact, 0); if (accModule != NULL) { PROTOACCOUNT* proto = (PROTOACCOUNT*)CallService(MS_PROTO_GETACCOUNT, 0, (LPARAM)accModule); if (proto != NULL) { tokens[3] = mir_tstrdup(proto->tszAccountName); tokenLen[3] = lstrlen(tokens[3]); } } tmplt = DBGetStringT(NULL, SRMMMOD, SRMSGSET_WINDOWTITLE); if (tmplt != NULL) { isTemplate = 1; } else { if (g_dat.flags & SMF_STATUSICON) { tmplt = _T("%name% - "); } else { tmplt = _T("%name% (%status%) : "); } } } else { tmplt = _T(""); } for (len = 0, p = tmplt; *p; p++, len++) { if (*p == '%') { for (i = 0; i < SIZEOF(titleTokenNames); i ++) { int tnlen = (int)_tcslen(titleTokenNames[i]); if (!_tcsncmp(p, titleTokenNames[i], tnlen)) { len += tokenLen[i] - 1; p += tnlen - 1; break; } } } } if (!isTemplate) { len += lstrlen(pszNewTitleEnd); } title = (TCHAR *)mir_alloc(sizeof(TCHAR) * (len + 1)); for (len = 0, p = tmplt; *p; p++) { if (*p == '%') { for (i = 0; i < SIZEOF(titleTokenNames); i ++) { int tnlen = lstrlen(titleTokenNames[i]); if (!_tcsncmp(p, titleTokenNames[i], tnlen)) { if (tokens[i] != NULL) { memcpy(title+len, tokens[i], sizeof(TCHAR) * tokenLen[i]); len += tokenLen[i]; } p += tnlen - 1; break; } } if (i < SIZEOF(titleTokenNames)) continue; } title[len++] = *p; } if (!isTemplate) { memcpy(title+len, pszNewTitleEnd, sizeof(TCHAR) * lstrlen(pszNewTitleEnd)); len += lstrlen(pszNewTitleEnd); } title[len] = '\0'; if (isTemplate) { mir_free(tmplt); } for (i = 0; i < SIZEOF(titleTokenNames); i ++) { mir_free(tokens[i]); } mir_free(pszNewTitleEnd); return title; } TCHAR* GetTabName(HANDLE *hContact) { if (hContact) { return GetNickname(hContact, NULL); } return NULL; } static int GetChildCount(ParentWindowData *dat) { return TabCtrl_GetItemCount(dat->hwndTabs); } static void GetChildWindowRect(ParentWindowData *dat, RECT *rcChild) { RECT rc, rcStatus, rcTabs; GetClientRect(dat->hwnd, &rc); GetClientRect(dat->hwndTabs, &rcTabs); TabCtrl_AdjustRect(dat->hwndTabs, FALSE, &rcTabs); rcStatus.top = rcStatus.bottom = 0; if (dat->flags2 & SMF2_SHOWSTATUSBAR) { GetWindowRect(dat->hwndStatus, &rcStatus); } rcChild->left = 0; rcChild->right = rc.right; if (dat->flags2 & SMF2_TABSATBOTTOM) { rcChild->top = 2; if ((dat->flags2 & SMF2_USETABS && !(dat->flags2 & SMF2_HIDEONETAB)) || (dat->childrenCount > 1)) { rcChild->bottom = rcTabs.bottom + 4; } else { rcChild->bottom = rc.bottom - rc.top - (rcStatus.bottom - rcStatus.top); } } else { if ((dat->flags2 & SMF2_USETABS && !(dat->flags2 & SMF2_HIDEONETAB)) || (dat->childrenCount > 1)) { rcChild->top = rcTabs.top; } else { rcChild->top = 2;//rcTabs.top - 2; } rcChild->bottom = rc.bottom - rc.top - (rcStatus.bottom - rcStatus.top); } } static int GetTabFromHWND(ParentWindowData *dat, HWND child) { MessageWindowTabData * mwtd; TCITEM tci; int l, i; l = TabCtrl_GetItemCount(dat->hwndTabs); for (i = 0; i < l; i++) { ZeroMemory(&tci, sizeof(TCITEM)); tci.mask = TCIF_PARAM; TabCtrl_GetItem(dat->hwndTabs, i, &tci); mwtd = (MessageWindowTabData *) tci.lParam; if (mwtd->hwnd == child) { return i; } } return -1; } static MessageWindowTabData * GetChildFromTab(HWND hwndTabs, int tabId) { TCITEM tci; ZeroMemory(&tci, sizeof(TCITEM)); tci.mask = TCIF_PARAM; if (TabCtrl_GetItem(hwndTabs, tabId, &tci)) { return (MessageWindowTabData *) tci.lParam; } return NULL; } static MessageWindowTabData * GetChildFromHWND(ParentWindowData *dat, HWND hwnd) { MessageWindowTabData * mwtd; TCITEM tci; int l, i; l = TabCtrl_GetItemCount(dat->hwndTabs); for (i = 0; i < l; i++) { ZeroMemory(&tci, sizeof(TCITEM)); tci.mask = TCIF_PARAM; TabCtrl_GetItem(dat->hwndTabs, i, &tci); mwtd = (MessageWindowTabData *) tci.lParam; if (mwtd->hwnd == hwnd) { return mwtd; } } return NULL; } static void GetMinimunWindowSize(ParentWindowData *dat, SIZE *size) { MINMAXINFO mmi; RECT rc, rcWindow; int i, minW = 216, minH = 80; GetWindowRect(dat->hwnd, &rcWindow); GetChildWindowRect(dat, &rc); for (i=0;ichildrenCount;i++) { MessageWindowTabData * mwtd = GetChildFromTab(dat->hwndTabs, i); SendMessage(mwtd->hwnd, WM_GETMINMAXINFO, 0, (LPARAM)&mmi); if (i==0 || mmi.ptMinTrackSize.x > minW) minW = mmi.ptMinTrackSize.x; if (i==0 || mmi.ptMinTrackSize.y > minH) minH = mmi.ptMinTrackSize.y; } if (dat->bMinimized) { size->cx = minW; size->cy = minH; } else { size->cx = minW + (rcWindow.right - rcWindow.left) - (rc.right - rc.left); size->cy = minH + (rcWindow.bottom - rcWindow.top) - (rc.bottom - rc.top); } } static void SetupStatusBar(ParentWindowData *dat) { int statusIconNum = GetStatusIconsCount(dat->hContact); int statwidths[4]; RECT rc; GetClientRect(dat->hwnd, &rc); statwidths[0] = rc.right - rc.left - SB_CHAR_WIDTH - SB_UNICODE_WIDTH - 2 * (statusIconNum > 0) - statusIconNum * (GetSystemMetrics(SM_CXSMICON) + 2); statwidths[1] = rc.right - rc.left - SB_UNICODE_WIDTH - 2 * (statusIconNum > 0) - statusIconNum * (GetSystemMetrics(SM_CXSMICON) + 2); statwidths[2] = rc.right - rc.left - SB_UNICODE_WIDTH; statwidths[3] = -1; SendMessage(dat->hwndStatus, SB_SETPARTS, 4, (LPARAM)statwidths); SendMessage(dat->hwndStatus, SB_SETTEXT, (WPARAM)(SBT_OWNERDRAW) | 2, 0); SendMessage(dat->hwndStatus, SB_SETTEXT, (WPARAM)(SBT_NOBORDERS) | 3, 0); } static int AddOrReplaceIcon(HIMAGELIST hList, int prevIndex, HICON hIcon) { int usageIdx = -1; int i; for (i = 0; i < g_dat.tabIconListUsageSize; i++) { if (!g_dat.tabIconListUsage[i].used && usageIdx == -1) { usageIdx = i; } if (g_dat.tabIconListUsage[i].index == prevIndex) { usageIdx = i; break; } } if (usageIdx == -1) { usageIdx = g_dat.tabIconListUsageSize; g_dat.tabIconListUsage = (ImageListUsageEntry*) mir_realloc(g_dat.tabIconListUsage, sizeof(ImageListUsageEntry) * (g_dat.tabIconListUsageSize + 1)); g_dat.tabIconListUsageSize++; } else { prevIndex = g_dat.tabIconListUsage[usageIdx].index; } g_dat.tabIconListUsage[usageIdx].used = 1; g_dat.tabIconListUsage[usageIdx].index = (int) ImageList_ReplaceIcon(hList, prevIndex, hIcon); return g_dat.tabIconListUsage[usageIdx].index; } static void ReleaseIcon(int index) { int i; for (i = 0; i < g_dat.tabIconListUsageSize; i++) { if (g_dat.tabIconListUsage[i].index == index) { g_dat.tabIconListUsage[i].used = 0; } } } static void ActivateChild(ParentWindowData *dat, HWND child) { int i; RECT rcChild; GetChildWindowRect(dat, &rcChild); SetWindowPos(child, HWND_TOP, rcChild.left, rcChild.top, rcChild.right-rcChild.left, rcChild.bottom - rcChild.top, SWP_NOSIZE); i = GetTabFromHWND(dat, child); if ( i == -1 ) return; else { MessageWindowTabData *mwtd; if (( mwtd = GetChildFromTab(dat->hwndTabs, i)) == NULL ) return; dat->hContact = mwtd->hContact; if (child != dat->hwndActive) { HWND prev = dat->hwndActive; dat->hwndActive = child; SetupStatusBar(dat); SendMessage(dat->hwndActive, DM_UPDATESTATUSBAR, 0, 0); SendMessage(dat->hwndActive, DM_UPDATETITLEBAR, 0, 0); SendMessage(dat->hwnd, WM_SIZE, 0, 0); ShowWindow(dat->hwndActive, SW_SHOWNOACTIVATE); SendMessage(dat->hwndActive, DM_SCROLLLOGTOBOTTOM, 0, 0); if (prev!=NULL) ShowWindow(prev, SW_HIDE); } else { SendMessage(dat->hwnd, WM_SIZE, 0, 0); } TabCtrl_SetCurSel(dat->hwndTabs, i); SendMessage(dat->hwndActive, DM_ACTIVATE, WA_ACTIVE, 0); } } static void AddChild(ParentWindowData *dat, HWND hwnd, HANDLE hContact) { TCITEM tci; int tabId; MessageWindowTabData *mwtd = (MessageWindowTabData *) mir_alloc(sizeof(MessageWindowTabData)); mwtd->hwnd = hwnd; mwtd->hContact = hContact; mwtd->szProto = GetContactProto(hContact); mwtd->parent = dat; dat->childrenCount++; tci.mask = TCIF_PARAM | TCIF_IMAGE | TCIF_TEXT; tci.lParam = (LPARAM)mwtd; tci.iImage = -1; tci.pszText = _T(""); tabId = TabCtrl_InsertItem(dat->hwndTabs, dat->childrenCount-1, &tci); // ActivateChild(dat, mdat->hwnd); SetWindowPos(mwtd->hwnd, HWND_TOP, dat->childRect.left, dat->childRect.top, dat->childRect.right-dat->childRect.left, dat->childRect.bottom - dat->childRect.top, SWP_HIDEWINDOW); SendMessage(dat->hwnd, WM_SIZE, 0, 0); if (MyEnableThemeDialogTexture) MyEnableThemeDialogTexture(hwnd, ETDT_ENABLETAB); } static void RemoveChild(ParentWindowData *dat, HWND child) { int tab = GetTabFromHWND(dat, child); if (tab >= 0) { TCITEM tci; tci.mask = TCIF_PARAM | TCIF_IMAGE; TabCtrl_GetItem(dat->hwndTabs, tab, &tci); TabCtrl_DeleteItem(dat->hwndTabs, tab); mir_free((MessageWindowTabData *) tci.lParam); dat->childrenCount--; if (child == dat->hwndActive) { if (tab == TabCtrl_GetItemCount(dat->hwndTabs)) tab--; if (tab >=0 ) { ActivateChild(dat, GetChildFromTab(dat->hwndTabs, tab)->hwnd); } else dat->hwndActive = NULL; } ReleaseIcon(tci.iImage); } } static void CloseOtherChilden(ParentWindowData *dat, HWND child) { int i; ActivateChild(dat, child); for (i=dat->childrenCount-1;i>=0;i--) { MessageWindowTabData *mwtd = GetChildFromTab(dat->hwndTabs, i); if (mwtd != NULL && mwtd->hwnd != child) { SendMessage(mwtd->hwnd, WM_CLOSE, 0, 0); } } ActivateChild(dat, child); } static void ActivateNextChild(ParentWindowData *dat, HWND child) { int i = GetTabFromHWND(dat, child); int l = TabCtrl_GetItemCount(dat->hwndTabs); i = (i+1) % l; ActivateChild(dat, GetChildFromTab(dat->hwndTabs, i)->hwnd); } static void ActivatePrevChild(ParentWindowData *dat, HWND child) { int i = GetTabFromHWND(dat, child); int l = TabCtrl_GetItemCount(dat->hwndTabs); i = (i+l-1) % l; ActivateChild(dat, GetChildFromTab(dat->hwndTabs, i)->hwnd); } static void ActivateChildByIndex(ParentWindowData *dat, int index) { int l = TabCtrl_GetItemCount(dat->hwndTabs); if (index < l) { MessageWindowTabData *mwtd = GetChildFromTab(dat->hwndTabs, index); if (mwtd != NULL) { ActivateChild(dat, mwtd->hwnd); } } } static void SetContainerWindowStyle(ParentWindowData *dat) { ShowWindow(dat->hwndStatus, (dat->flags2 & SMF2_SHOWSTATUSBAR) ? SW_SHOW : SW_HIDE); DWORD ws = GetWindowLongPtr(dat->hwnd, GWL_STYLE) & ~(WS_CAPTION); if (dat->flags2 & SMF2_SHOWTITLEBAR) ws |= WS_CAPTION; SetWindowLongPtr(dat->hwnd, GWL_STYLE, ws); ws = GetWindowLongPtr(dat->hwnd, GWL_EXSTYLE)& ~WS_EX_LAYERED; ws |= dat->flags2 & SMF2_USETRANSPARENCY ? WS_EX_LAYERED : 0; SetWindowLongPtr(dat->hwnd , GWL_EXSTYLE , ws); if (dat->flags2 & SMF2_USETRANSPARENCY) pSetLayeredWindowAttributes(dat->hwnd, RGB(255,255,255), (BYTE)(255-g_dat.inactiveAlpha), LWA_ALPHA); ws = GetWindowLongPtr(dat->hwndTabs, GWL_STYLE) & ~(TCS_BOTTOM | 0x2000); if (dat->flags2 & SMF2_TABSATBOTTOM) ws |= TCS_BOTTOM; ws |= 0x2000; if (dat->flags2 & SMF2_TABCLOSEBUTTON) TabCtrl_SetPadding(dat->hwndTabs, GetSystemMetrics(SM_CXEDGE) + 12, GetSystemMetrics(SM_CYEDGE) + 1); else TabCtrl_SetPadding(dat->hwndTabs, GetSystemMetrics(SM_CXEDGE) + 4, GetSystemMetrics(SM_CYEDGE) + 1); SetWindowLongPtr(dat->hwndTabs, GWL_STYLE, ws); RECT rc; GetWindowRect(dat->hwnd, &rc); SetWindowPos(dat->hwnd, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOSENDCHANGING); } INT_PTR CALLBACK DlgProcParentWindow(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { DWORD ws; ParentWindowData *dat; dat = (ParentWindowData *) GetWindowLongPtr(hwndDlg, GWLP_USERDATA); if (!dat && msg!=WM_INITDIALOG) return FALSE; switch (msg) { case WM_INITDIALOG: { HMENU hMenu; HANDLE hSContact; int savePerContact = db_get_b(NULL, SRMMMOD, SRMSGSET_SAVEPERCONTACT, SRMSGDEFSET_SAVEPERCONTACT); NewMessageWindowLParam *newData = (NewMessageWindowLParam *) lParam; dat = (ParentWindowData *) mir_alloc(sizeof(ParentWindowData)); dat->hContact = newData->hContact; dat->nFlash = 0; dat->nFlashMax = db_get_b(NULL, SRMMMOD, SRMSGSET_FLASHCOUNT, SRMSGDEFSET_FLASHCOUNT); dat->childrenCount = 0; dat->hwnd = hwndDlg; dat->mouseLBDown = 0; dat->windowWasCascaded = 0; dat->bMinimized = 0; dat->bVMaximized = 0; dat->flags2 = g_dat.flags2; dat->hwndStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0, hwndDlg, NULL, g_hInst, NULL); dat->isChat = newData->isChat; SendMessage(dat->hwndStatus, SB_SETMINHEIGHT, GetSystemMetrics(SM_CYSMICON), 0); //SetupStatusBar(dat); dat->hwndTabs = GetDlgItem(hwndDlg, IDC_TABS); dat->hwndActive = NULL; SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) dat); if (g_dat.hTabIconList != NULL) { TabCtrl_SetImageList(dat->hwndTabs, g_dat.hTabIconList); } dat->next = NULL; if (!newData->isChat) { dat->prev = g_dat.lastParent; g_dat.lastParent = dat; } else { dat->prev = g_dat.lastChatParent; g_dat.lastChatParent = dat; } if (dat->prev != NULL) { dat->prev->next = dat; } WindowList_Add(g_dat.hParentWindowList, hwndDlg, hwndDlg); SubclassTabCtrl(dat->hwndTabs); SetContainerWindowStyle(dat); hSContact = savePerContact ? dat->hContact : NULL; dat->bTopmost = db_get_b(hSContact, SRMMMOD, SRMSGSET_TOPMOST, SRMSGDEFSET_TOPMOST); if (ScriverRestoreWindowPosition(hwndDlg, hSContact, SRMMMOD, (newData->isChat && !savePerContact) ? "chat" : "", 0, SW_HIDE)) SetWindowPos(hwndDlg, 0, 0, 0, 450, 300, SWP_NOZORDER | SWP_NOMOVE | SWP_HIDEWINDOW); if (!savePerContact && db_get_b(NULL, SRMMMOD, SRMSGSET_CASCADE, SRMSGDEFSET_CASCADE)) WindowList_Broadcast(g_dat.hParentWindowList, DM_CASCADENEWWINDOW, (WPARAM) hwndDlg, (LPARAM)&dat->windowWasCascaded); hMenu = GetSystemMenu( hwndDlg, FALSE ); InsertMenu( hMenu, 0, MF_BYPOSITION | MF_SEPARATOR, 0, NULL ); if (dat->bTopmost) { InsertMenu( hMenu, 0, MF_BYPOSITION | MF_ENABLED | MF_CHECKED | MF_STRING, IDM_TOPMOST, TranslateT("Always On Top")); SetWindowPos(hwndDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } else InsertMenu( hMenu, 0, MF_BYPOSITION | MF_ENABLED | MF_UNCHECKED | MF_STRING, IDM_TOPMOST, TranslateT("Always On Top")); } return TRUE; case WM_GETMINMAXINFO: { MINMAXINFO *mmi = (MINMAXINFO *) lParam; SIZE size; if (dat->bVMaximized) { MONITORINFO mi; HMONITOR hMonitor; WINDOWPLACEMENT wp; RECT rcDesktop; wp.length = sizeof(wp); GetWindowPlacement(hwndDlg, &wp); hMonitor = MonitorFromRect(&wp.rcNormalPosition, MONITOR_DEFAULTTONEAREST); mi.cbSize = sizeof(mi); GetMonitorInfo(hMonitor, &mi); rcDesktop = mi.rcWork; mmi->ptMaxSize.x = wp.rcNormalPosition.right - wp.rcNormalPosition.left; mmi->ptMaxSize.y = rcDesktop.bottom - rcDesktop.top; mmi->ptMaxPosition.x = wp.rcNormalPosition.left; if (IsIconic(hwndDlg)) { mmi->ptMaxPosition.y = rcDesktop.top; } else { mmi->ptMaxPosition.y = 0; } } GetMinimunWindowSize(dat, &size); mmi->ptMinTrackSize.x = size.cx; mmi->ptMinTrackSize.y = size.cy; return FALSE; } case WM_SIZE: if (wParam == SIZE_MINIMIZED) dat->bMinimized = 1; if ( IsIconic(hwndDlg)) MoveWindow(dat->hwndActive, dat->childRect.left, dat->childRect.top, dat->childRect.right-dat->childRect.left, dat->childRect.bottom - dat->childRect.top, TRUE); else { RECT rc, rcStatus, rcChild, rcWindow; SIZE size; dat->bMinimized = 0; GetClientRect(hwndDlg, &rc); GetWindowRect(hwndDlg, &rcWindow); rcStatus.top = rcStatus.bottom = 0; if (dat->flags2 & SMF2_SHOWSTATUSBAR) { GetWindowRect(dat->hwndStatus, &rcStatus); SetupStatusBar(dat); } MoveWindow(dat->hwndTabs, 0, 2, (rc.right - rc.left), (rc.bottom - rc.top) - (rcStatus.bottom - rcStatus.top) - 2, FALSE); RedrawWindow(dat->hwndTabs, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE); GetMinimunWindowSize(dat, &size); if ((rcWindow.bottom-rcWindow.top) < size.cy || (rcWindow.right-rcWindow.left) < size.cx) { if ((rcWindow.bottom-rcWindow.top) < size.cy) rcWindow.bottom = rcWindow.top + size.cy; if ((rcWindow.right-rcWindow.left) < size.cx) rcWindow.right = rcWindow.left + size.cx; MoveWindow(hwndDlg, rcWindow.left, rcWindow.top, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top, TRUE); } GetChildWindowRect(dat, &rcChild); memcpy(&dat->childRect, &rcChild, sizeof(RECT)); MoveWindow(dat->hwndActive, rcChild.left, rcChild.top, rcChild.right-rcChild.left, rcChild.bottom - rcChild.top, TRUE); RedrawWindow(GetDlgItem(dat->hwndActive, IDC_LOG), NULL, NULL, RDW_INVALIDATE); if (dat->flags2 & SMF2_SHOWSTATUSBAR) { SendMessage(dat->hwndStatus, WM_SIZE, 0, 0); RedrawWindow(dat->hwndStatus, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE); } } return FALSE; case WM_SETFOCUS: if (dat->hwndActive != NULL) SetFocus(dat->hwndActive); return TRUE; case WM_CLOSE: if (g_dat.flags2 & SMF2_HIDECONTAINERS && dat->childrenCount > 0) ShowWindow(hwndDlg, SW_HIDE); else DestroyWindow(hwndDlg); return TRUE; case WM_MEASUREITEM: return CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam); case WM_DRAWITEM: { LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT) lParam; if (dat && dat->hwndActive && dis->hwndItem == dat->hwndStatus) { MessageWindowTabData *mwtd = GetChildFromHWND(dat, dat->hwndActive); if (mwtd != NULL) { DrawStatusIcons(mwtd->hContact, dis->hDC, dis->rcItem, 2); } return TRUE; } if (dis->hwndItem == dat->hwndTabs) { DrawTab(dat, dat->hwndTabs, wParam, lParam); return TRUE; } return CallService(MS_CLIST_MENUDRAWITEM, wParam, lParam); } case WM_COMMAND: if (CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(wParam), MPCF_CONTACTMENU), (LPARAM)dat->hContact)) break; if ( LOWORD(wParam) == IDCANCEL) return TRUE; break; case WM_NOTIFY: { NMHDR* pNMHDR = (NMHDR*) lParam; if (pNMHDR->hwndFrom == dat->hwndTabs) { switch (pNMHDR->code) { case TCN_SELCHANGE: { TCITEM tci = {0}; int iSel = TabCtrl_GetCurSel(dat->hwndTabs); tci.mask = TCIF_PARAM; if (TabCtrl_GetItem(dat->hwndTabs, iSel, &tci)) { MessageWindowTabData * mwtd = (MessageWindowTabData *) tci.lParam; ActivateChild(dat, mwtd->hwnd); SetFocus(dat->hwndActive); } } break; case NM_RCLICK: { TCHITTESTINFO thinfo; int tabId, x, y; GetCursorPos(&thinfo.pt); x = thinfo.pt.x; y = thinfo.pt.y; ScreenToClient(dat->hwndTabs, &thinfo.pt); tabId = TabCtrl_HitTest(dat->hwndTabs, &thinfo); if (tabId != -1) { HMENU hMenu, hSubMenu, hUserMenu; BOOL menuResult; MessageWindowTabData * mwtd = GetChildFromTab(dat->hwndTabs, tabId); hMenu = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_CONTEXT)); hSubMenu = GetSubMenu(hMenu, 3); TranslateMenu(hSubMenu); hUserMenu = (HMENU) SendMessage(mwtd->hwnd, DM_GETCONTEXTMENU, 0, 0); if (hUserMenu != NULL) { InsertMenu(hSubMenu, 0, MF_POPUP | MF_BYPOSITION, (UINT_PTR)hUserMenu, TranslateT("User Menu")); InsertMenu(hSubMenu, 1, MF_SEPARATOR | MF_BYPOSITION, 0, 0); } menuResult = TrackPopupMenu(hSubMenu, TPM_RETURNCMD, x, y, 0, hwndDlg, NULL); switch (menuResult) { case IDM_CLOSETAB: SendMessage(mwtd->hwnd, WM_CLOSE, 0, 0); break; case IDM_CLOSEOTHERTABS: CloseOtherChilden(dat, mwtd->hwnd); break; default: CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(menuResult), MPCF_CONTACTMENU), (LPARAM)mwtd->hContact); } if (hUserMenu != NULL) { DestroyMenu(hUserMenu); } DestroyMenu(hMenu); } } break; } } else if (pNMHDR->hwndFrom == dat->hwndStatus) { switch (pNMHDR->code) { case NM_CLICK: // case NM_RCLICK: { NMMOUSE *nm=(NMMOUSE*)lParam; RECT rc; SendMessage(dat->hwndStatus, SB_GETRECT, SendMessage(dat->hwndStatus, SB_GETPARTS, 0, 0) - 2, (LPARAM)&rc); if (nm->pt.x >= rc.left) { MessageWindowTabData *mwtd = GetChildFromHWND(dat, dat->hwndActive); if (mwtd != NULL) { CheckStatusIconClick(mwtd->hContact, dat->hwndStatus, nm->pt, rc, 2, (pNMHDR->code == NM_RCLICK ? MBCF_RIGHTBUTTON : 0)); } } return TRUE; } } } } break; case WM_DROPFILES: SendMessage(dat->hwndActive, WM_DROPFILES, wParam, lParam); break; case WM_TIMER: if (wParam == TIMERID_FLASHWND) { if (dat->nFlash < 2 * dat->nFlashMax) { FlashWindow(hwndDlg, TRUE); dat->nFlash++; } else { KillTimer(hwndDlg, TIMERID_FLASHWND); FlashWindow(hwndDlg, FALSE); } } break; case WM_CONTEXTMENU: if (dat->hwndStatus && dat->hwndStatus == (HWND) wParam) { RECT rc; POINT pt, pt2; GetCursorPos(&pt); pt2 = pt; ScreenToClient(dat->hwndStatus, &pt); SendMessage(dat->hwndStatus, SB_GETRECT, SendMessage(dat->hwndStatus, SB_GETPARTS, 0, 0) - 2, (LPARAM)&rc); if (pt.x >= rc.left) { MessageWindowTabData *mwtd = GetChildFromHWND(dat, dat->hwndActive); if (mwtd != NULL) CheckStatusIconClick(mwtd->hContact, dat->hwndStatus, pt, rc, 2, MBCF_RIGHTBUTTON); break; } else SendMessage(dat->hwndActive, WM_CONTEXTMENU, (WPARAM)hwndDlg, 0); } break; case WM_ACTIVATE: if (LOWORD(wParam) == WA_INACTIVE) { ws = GetWindowLongPtr(hwndDlg, GWL_EXSTYLE) & ~WS_EX_LAYERED; ws |= dat->flags2 & SMF2_USETRANSPARENCY ? WS_EX_LAYERED : 0; SetWindowLongPtr(hwndDlg , GWL_EXSTYLE , ws); if (dat->flags2 & SMF2_USETRANSPARENCY) { pSetLayeredWindowAttributes(hwndDlg, RGB(255,255,255), (BYTE)(255-g_dat.inactiveAlpha), LWA_ALPHA); // RedrawWindow(hwndDlg, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN); } break; } if (dat->hwndActive != NULL) { ActivateChild(dat, dat->hwndActive); g_dat.hFocusWnd = dat->hwndActive; PostMessage(dat->hwndActive, DM_SETFOCUS, 0, msg); } if (KillTimer(hwndDlg, TIMERID_FLASHWND)) { FlashWindow(hwndDlg, FALSE); dat->nFlash = 0; } ws = GetWindowLongPtr(hwndDlg, GWL_EXSTYLE) & ~WS_EX_LAYERED; ws |= dat->flags2 & SMF2_USETRANSPARENCY ? WS_EX_LAYERED : 0; SetWindowLongPtr(hwndDlg , GWL_EXSTYLE , ws); if (dat->flags2 & SMF2_USETRANSPARENCY) pSetLayeredWindowAttributes(hwndDlg, RGB(255,255,255), (BYTE)(255-g_dat.activeAlpha), LWA_ALPHA); break; case WM_LBUTTONDOWN: if (!IsZoomed(hwndDlg)) { POINT pt; GetCursorPos(&pt); return SendMessage(hwndDlg, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y)); } break; case WM_MOVING: if ((GetAsyncKeyState(VK_CONTROL) & 0x8000)) { int snapPixels = 10; RECT rcDesktop; RECT *pRect = (RECT *)lParam; POINT pt; MONITORINFO mi; HMONITOR hMonitor = MonitorFromRect(pRect, MONITOR_DEFAULTTONEAREST); SIZE szSize = {pRect->right-pRect->left,pRect->bottom-pRect->top}; mi.cbSize = sizeof(mi); GetMonitorInfo(hMonitor, &mi); GetCursorPos(&pt); // SystemParametersInfo(SPI_GETWORKAREA, 0, &rcDesktop, 0); rcDesktop = mi.rcWork; pRect->left = pt.x-dat->mouseLBDownPos.x; pRect->top = pt.y-dat->mouseLBDownPos.y; pRect->right = pRect->left+szSize.cx; pRect->bottom = pRect->top+szSize.cy; if (pRect->top < rcDesktop.top+snapPixels && pRect->top > rcDesktop.top-snapPixels) { pRect->top = rcDesktop.top; pRect->bottom = rcDesktop.top + szSize.cy; } if (pRect->left < rcDesktop.left+snapPixels && pRect->left > rcDesktop.left-snapPixels) { pRect->left = rcDesktop.left; pRect->right = rcDesktop.left + szSize.cx; } if (pRect->right < rcDesktop.right+snapPixels && pRect->right > rcDesktop.right-snapPixels) { pRect->right = rcDesktop.right; pRect->left = rcDesktop.right - szSize.cx; } if (pRect->bottom < rcDesktop.bottom+snapPixels && pRect->bottom > rcDesktop.bottom-snapPixels) { pRect->bottom = rcDesktop.bottom; pRect->top = rcDesktop.bottom - szSize.cy; } } break; case WM_SYSCOMMAND: if ((wParam & 0xFFF0) == SC_MAXIMIZE) { if (GetKeyState(VK_CONTROL) & 0x8000) { dat->bVMaximized = 1; } else { dat->bVMaximized = 0; } } else if ((wParam & 0xFFF0) == SC_MOVE) { RECT rc; GetWindowRect(hwndDlg, &rc); dat->mouseLBDownPos.x = LOWORD(lParam) - rc.left; dat->mouseLBDownPos.y = HIWORD(lParam) - rc.top; } else if (wParam == IDM_TOPMOST) { HMENU hMenu = GetSystemMenu(hwndDlg, FALSE); if (dat->bTopmost) { CheckMenuItem(hMenu, IDM_TOPMOST, MF_BYCOMMAND | MF_UNCHECKED); SetWindowPos(hwndDlg, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); dat->bTopmost = FALSE; } else { CheckMenuItem(hMenu, IDM_TOPMOST, MF_BYCOMMAND | MF_CHECKED); SetWindowPos(hwndDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); dat->bTopmost = TRUE; } } break; case WM_DESTROY: { char szSettingName[64]; for (int i=dat->childrenCount; --i >= 0;) { TCITEM tci; tci.mask = TCIF_PARAM | TCIF_IMAGE; TabCtrl_GetItem(dat->hwndTabs, i, &tci); TabCtrl_DeleteItem(dat->hwndTabs, i); mir_free((MessageWindowTabData *) tci.lParam); ReleaseIcon(tci.iImage); } SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); WindowList_Remove(g_dat.hParentWindowList, hwndDlg); int savePerContact = db_get_b(NULL, SRMMMOD, SRMSGSET_SAVEPERCONTACT, SRMSGDEFSET_SAVEPERCONTACT); HANDLE hContact = (savePerContact) ? dat->hContact : NULL; WINDOWPLACEMENT wp = { sizeof(wp) }; GetWindowPlacement(hwndDlg, &wp); char *szNamePrefix = (!savePerContact && dat->isChat) ? "chat" : ""; if (!dat->windowWasCascaded) { wsprintfA(szSettingName,"%sx",szNamePrefix); db_set_dw(hContact, SRMMMOD, szSettingName, wp.rcNormalPosition.left); wsprintfA(szSettingName,"%sy",szNamePrefix); db_set_dw(hContact, SRMMMOD, szSettingName, wp.rcNormalPosition.top); } wsprintfA(szSettingName,"%swidth",szNamePrefix); db_set_dw(hContact, SRMMMOD, szSettingName, wp.rcNormalPosition.right - wp.rcNormalPosition.left); wsprintfA(szSettingName,"%sheight",szNamePrefix); db_set_dw(hContact, SRMMMOD, szSettingName, wp.rcNormalPosition.bottom - wp.rcNormalPosition.top); db_set_b(hContact, SRMMMOD, SRMSGSET_TOPMOST, (BYTE)dat->bTopmost); if (g_dat.lastParent == dat) g_dat.lastParent = dat->prev; if (g_dat.lastChatParent == dat) g_dat.lastChatParent = dat->prev; if (dat->prev != NULL) dat->prev->next = dat->next; if (dat->next != NULL) dat->next->prev = dat->prev; UnsubclassTabCtrl(dat->hwndTabs); mir_free(dat); } break; case DM_ERRORDECIDED: break; case CM_STARTFLASHING: if ((GetActiveWindow() != hwndDlg || GetForegroundWindow() != hwndDlg)) {// && !(g_dat.flags2 & SMF2_STAYMINIMIZED)) { dat->nFlash = 0; SetTimer(hwndDlg, TIMERID_FLASHWND, TIMEOUT_FLASHWND, NULL); } break; case CM_POPUPWINDOW: EnableWindow(hwndDlg, TRUE); if (wParam) { /* incoming message */ if (g_dat.flags & SMF_STAYMINIMIZED) { if (!IsWindowVisible(hwndDlg)) ShowWindow(hwndDlg, SW_SHOWMINNOACTIVE); if (dat->childrenCount == 1 || ((g_dat.flags2 & SMF2_SWITCHTOACTIVE) && (IsIconic(hwndDlg) || GetForegroundWindow() != hwndDlg))) SendMessage(hwndDlg, CM_ACTIVATECHILD, 0, (LPARAM)lParam); } else { ShowWindow(hwndDlg, IsIconic(hwndDlg) ? SW_SHOWNORMAL : SW_SHOWNA); if (dat->childrenCount == 1 || ((g_dat.flags2 & SMF2_SWITCHTOACTIVE) && (IsIconic(hwndDlg) || GetForegroundWindow() != hwndDlg))) SendMessage(hwndDlg, CM_ACTIVATECHILD, 0, (LPARAM)lParam); SetWindowPos(hwndDlg, HWND_TOP, 0,0,0,0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } } else { /* outgoing message */ ShowWindow(hwndDlg, IsIconic(hwndDlg) ? SW_SHOWNORMAL : SW_SHOW); SetForegroundWindow(hwndDlg); SetFocus((HWND)lParam); } break; case CM_REMOVECHILD: RemoveChild(dat, (HWND) lParam); if (dat->childrenCount != 0) SetFocus(dat->hwndActive); else PostMessage(hwndDlg, WM_CLOSE, 0, 0); return TRUE; case CM_ADDCHILD: AddChild(dat, (HWND)wParam, (HANDLE)lParam); return TRUE; case CM_ACTIVATECHILD: ActivateChild(dat, (HWND) lParam); return TRUE; case CM_ACTIVATEPREV: ActivatePrevChild(dat, (HWND) lParam); SetFocus(dat->hwndActive); return TRUE; case CM_ACTIVATENEXT: ActivateNextChild(dat, (HWND) lParam); SetFocus(dat->hwndActive); return TRUE; case CM_ACTIVATEBYINDEX: ActivateChildByIndex(dat, (int) lParam); SetFocus(dat->hwndActive); return TRUE; case CM_GETCHILDCOUNT: SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)GetChildCount(dat)); return TRUE; case CM_GETACTIVECHILD: SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)dat->hwndActive); return TRUE; case CM_GETTOOLBARSTATUS: SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)(dat->flags2 & SMF2_SHOWTOOLBAR) != 0); return TRUE; case DM_SENDMESSAGE: { for (int i=0; i < dat->childrenCount; i++) { MessageWindowTabData * mwtd = GetChildFromTab(dat->hwndTabs, i); SendMessage(mwtd->hwnd, DM_SENDMESSAGE, wParam, lParam); } } break; case DM_OPTIONSAPPLIED: dat->flags2 = g_dat.flags2; SetContainerWindowStyle(dat); SendMessage(hwndDlg, WM_SIZE, 0, 0); break; case CM_UPDATETITLEBAR: { HWND hwnd = (HWND) lParam; TitleBarData *tbd = (TitleBarData *) wParam; if (tbd != NULL && dat->hwndActive == hwnd) { if (tbd->iFlags & TBDF_TEXT) { TCHAR oldtitle[256]; GetWindowText(hwndDlg, oldtitle, SIZEOF(oldtitle)); if (lstrcmp(tbd->pszText, oldtitle)) { //swt() flickers even if the title hasn't actually changed SetWindowText(hwndDlg, tbd->pszText); //SendMessage(hwndDlg, WM_SIZE, 0, 0); } } if (tbd->iFlags & TBDF_ICON) { SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)tbd->hIcon); if (tbd->hIconBig != NULL) { SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)tbd->hIconBig); } if (pTaskbarInterface) pTaskbarInterface->SetOverlayIcon(hwndDlg, tbd->hIconNot, L""); } } break; } case CM_UPDATESTATUSBAR: { HWND hwnd = (HWND) lParam; StatusBarData *sbd = (StatusBarData *) wParam; if (sbd != NULL) { if ((sbd->iFlags & SBDF_TEXT) && dat->hwndActive == hwnd) { SendMessage(dat->hwndStatus, SB_SETTEXT, sbd->iItem, (LPARAM)sbd->pszText); } if ((sbd->iFlags & SBDF_ICON) && dat->hwndActive == hwnd) { SendMessage(dat->hwndStatus, SB_SETICON, sbd->iItem, (LPARAM)sbd->hIcon); } RedrawWindow(dat->hwndStatus, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW); } } break; case DM_STATUSICONCHANGE: SendMessage(dat->hwndStatus, SB_SETTEXT, (WPARAM)(SBT_OWNERDRAW) | 2, 0); SetupStatusBar(dat); RedrawWindow(dat->hwndStatus, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW); break; case CM_UPDATETABCONTROL: { TCHAR *ptszTemp = NULL; TabControlData *tcd = (TabControlData *) wParam; int tabId = GetTabFromHWND(dat, (HWND) lParam); if (tabId >= 0 && tcd != NULL) { TCITEM tci; tci.mask = 0; if (tcd->iFlags & TCDF_TEXT) { tci.mask |= TCIF_TEXT; tci.pszText = tcd->pszText; if (g_dat.flags2 & SMF2_LIMITNAMES) { TCHAR * ltext = limitText(tcd->pszText, g_dat.limitNamesLength); if (ltext != tcd->pszText) tci.pszText = ptszTemp = ltext; } } if (tcd->iFlags & TCDF_ICON) { int iconIdx = -1; if (tcd->hIcon != NULL) { TCITEM tci2; tci2.mask = TCIF_IMAGE; TabCtrl_GetItem(dat->hwndTabs, tabId, &tci2); iconIdx = AddOrReplaceIcon(g_dat.hTabIconList, tci2.iImage, tcd->hIcon); } tci.mask |= TCIF_IMAGE; tci.iImage = iconIdx; } TabCtrl_SetItem(dat->hwndTabs, tabId, &tci); } mir_free(ptszTemp); } break; case DM_SWITCHINFOBAR: dat->flags2 ^= SMF2_SHOWINFOBAR; { for (int i=0; i < dat->childrenCount; i++) { MessageWindowTabData * mwtd = GetChildFromTab(dat->hwndTabs, i); SendMessage(mwtd->hwnd, DM_SWITCHINFOBAR, 0, 0); } SendMessage(hwndDlg, WM_SIZE, 0, 0); } break; case DM_SWITCHSTATUSBAR: dat->flags2 ^= SMF2_SHOWSTATUSBAR; ShowWindow(dat->hwndStatus, (dat->flags2 & SMF2_SHOWSTATUSBAR) ? SW_SHOW : SW_HIDE); SendMessage(hwndDlg, WM_SIZE, 0, 0); break; case DM_SWITCHTOOLBAR: dat->flags2 ^= SMF2_SHOWTOOLBAR; { for (int i=0; i < dat->childrenCount; i++) { MessageWindowTabData * mwtd = GetChildFromTab(dat->hwndTabs, i); SendMessage(mwtd->hwnd, DM_SWITCHTOOLBAR, 0, 0); } SendMessage(hwndDlg, WM_SIZE, 0, 0); } break; case DM_SWITCHTITLEBAR: { RECT rc; dat->flags2 ^= SMF2_SHOWTITLEBAR; ws = GetWindowLongPtr(hwndDlg, GWL_STYLE) & ~(WS_CAPTION); if (dat->flags2 & SMF2_SHOWTITLEBAR) ws |= WS_CAPTION; SetWindowLongPtr(hwndDlg, GWL_STYLE, ws); GetWindowRect(hwndDlg, &rc); SetWindowPos(hwndDlg, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOSENDCHANGING); // SendMessage(hwndDlg, WM_SIZE, 0, 0); RedrawWindow(hwndDlg, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); } break; case DM_CASCADENEWWINDOW: if ((HWND)wParam != hwndDlg) { 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; } return FALSE; } static void DrawTab(ParentWindowData *dat, HWND hwnd, WPARAM wParam, LPARAM lParam) { TabCtrlData *tcdat; TCITEM tci; LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT) lParam; int iTabIndex = lpDIS->itemID; tcdat = (TabCtrlData *) GetWindowLongPtr(hwnd, GWLP_USERDATA); if (iTabIndex >= 0) { HANDLE hTheme = NULL; int tstate; TCHAR szLabel[1024]; tci.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_STATE; tci.pszText = szLabel; tci.cchTextMax = SIZEOF(szLabel); tci.dwStateMask = TCIS_HIGHLIGHTED; if (TabCtrl_GetItem(hwnd, iTabIndex, &tci)) { IMAGEINFO info; RECT rIcon = lpDIS->rcItem; RECT rect = lpDIS->rcItem; RECT rectTab = lpDIS->rcItem; int bSelected = lpDIS->itemState & ODS_SELECTED; int atTop = (GetWindowLongPtr(hwnd, GWL_STYLE) & TCS_BOTTOM) == 0; UINT dwFormat; if (!MyIsAppThemed || !MyIsAppThemed()) { FillRect(lpDIS->hDC, &rect, GetSysColorBrush(COLOR_BTNFACE)); } else { if (lpDIS->itemState & ODS_SELECTED) tstate = TTIS_SELECTED; else if (lpDIS->itemState & ODS_FOCUS) tstate = TTIS_FOCUSED; else if (lpDIS->itemState & ODS_HOTLIGHT) tstate = TTIS_HOT; else tstate = TTIS_NORMAL; if (!bSelected) InflateRect(&rectTab, 1, 1); hTheme = MyOpenThemeData(hwnd, L"TAB"); if (MyIsThemeBackgroundPartiallyTransparent(hTheme, TABP_TABITEM, tstate)) MyDrawThemeParentBackground(hwnd, lpDIS->hDC, &rectTab); MyDrawThemeBackground(hTheme, lpDIS->hDC, TABP_TABITEM, tstate, &rectTab, NULL); } if (atTop) { dwFormat = DT_SINGLELINE|DT_TOP|DT_CENTER|DT_NOPREFIX|DT_NOCLIP; rIcon.top = rect.top + GetSystemMetrics(SM_CYEDGE); if (tci.iImage >= 0) { rIcon.left = rect.left + GetSystemMetrics(SM_CXEDGE) + (bSelected ? 6 : 2); ImageList_GetImageInfo(g_dat.hTabIconList, tci.iImage, &info); ImageList_DrawEx(g_dat.hTabIconList, tci.iImage, lpDIS->hDC, rIcon.left, rIcon.top, 0, 0, CLR_NONE, CLR_NONE, ILD_NORMAL); rect.left = rIcon.left + (info.rcImage.right - info.rcImage.left); } if (dat->flags2 & SMF2_TABCLOSEBUTTON) { ImageList_GetImageInfo(g_dat.hButtonIconList, 0, &info); rIcon.left = rect.right - GetSystemMetrics(SM_CXEDGE) - (bSelected ? 6 : 2) - (info.rcImage.right - info.rcImage.left); ImageList_DrawEx(g_dat.hButtonIconList, 0, lpDIS->hDC, rIcon.left, rIcon.top, 0, 0, CLR_NONE, CLR_NONE, ILD_NORMAL); rect.right = rIcon.left - 1; } rect.top += GetSystemMetrics(SM_CYEDGE) + 2; } else { dwFormat = DT_SINGLELINE|DT_BOTTOM|DT_CENTER|DT_NOPREFIX|DT_NOCLIP; rIcon.left = rect.left + GetSystemMetrics(SM_CXEDGE) + (bSelected ? 6 : 2); if (tci.iImage >= 0) { ImageList_GetImageInfo(g_dat.hTabIconList, tci.iImage, &info); rIcon.top = rect.bottom - (info.rcImage.bottom - info.rcImage.top) - 1; ImageList_DrawEx(g_dat.hTabIconList, tci.iImage, lpDIS->hDC, rIcon.left, rIcon.top, 0, 0, CLR_NONE, CLR_NONE, ILD_NORMAL); rect.left = rIcon.left + (info.rcImage.right - info.rcImage.left); } if (dat->flags2 & SMF2_TABCLOSEBUTTON) { ImageList_GetImageInfo(g_dat.hButtonIconList, 0, &info); rIcon.top = rect.bottom - (info.rcImage.bottom - info.rcImage.top) - 2; rIcon.left = rect.right - GetSystemMetrics(SM_CXEDGE) - (bSelected ? 6 : 2) - (info.rcImage.right - info.rcImage.left); ImageList_DrawEx(g_dat.hButtonIconList, 0, lpDIS->hDC, rIcon.left, rIcon.top, 0, 0, CLR_NONE, CLR_NONE, ILD_NORMAL); rect.right = rIcon.left - 1; } rect.bottom -= GetSystemMetrics(SM_CYEDGE) + 2; } if (hTheme) { MyDrawThemeText(hTheme, lpDIS->hDC, TABP_TABITEM, tstate, szLabel, -1, dwFormat, 0, &rect); } else DrawText(lpDIS->hDC, szLabel, -1, &rect, dwFormat); if (tcdat->bDragged && iTabIndex == tcdat->destTab && iTabIndex != tcdat->srcTab) { RECT hlRect = lpDIS->rcItem; if (bSelected) { hlRect.bottom-=GetSystemMetrics(SM_CYEDGE); hlRect.top+=GetSystemMetrics(SM_CYEDGE); hlRect.left+=GetSystemMetrics(SM_CXEDGE); hlRect.right-=GetSystemMetrics(SM_CXEDGE); } else { if (atTop) { hlRect.top += GetSystemMetrics(SM_CYEDGE);; hlRect.bottom += GetSystemMetrics(SM_CYEDGE);; } else { hlRect.top -= GetSystemMetrics(SM_CYEDGE);; hlRect.bottom -= GetSystemMetrics(SM_CYEDGE);; } } FrameRect(lpDIS->hDC, &hlRect, GetSysColorBrush(COLOR_HIGHLIGHT)); hlRect.left++; hlRect.top++; hlRect.right--; hlRect.bottom--; FrameRect(lpDIS->hDC, &hlRect, GetSysColorBrush(COLOR_HIGHLIGHT)); } if (hTheme) MyCloseThemeData(hTheme); } } } LRESULT CALLBACK TabCtrlProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { TabCtrlData *dat; dat = (TabCtrlData *) GetWindowLongPtr(hwnd, GWLP_USERDATA); switch(msg) { case EM_SUBCLASSED: dat = (TabCtrlData *) mir_alloc(sizeof(TabCtrlData)); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) dat); dat->bDragging = FALSE; dat->bDragged = FALSE; dat->srcTab = -1; dat->destTab = -1; return 0; case WM_MBUTTONDOWN: { TCITEM tci; int tabId; MessageWindowTabData *mwtd; TCHITTESTINFO thinfo; thinfo.pt.x = LOWORD(lParam); thinfo.pt.y = HIWORD(lParam); tabId = TabCtrl_HitTest(hwnd, &thinfo); if (tabId >= 0) { tci.mask = TCIF_PARAM; TabCtrl_GetItem(hwnd, tabId, &tci); mwtd = (MessageWindowTabData *) tci.lParam; if (mwtd != NULL) { SendMessage(mwtd->hwnd, WM_CLOSE, 0, 0); dat->srcTab = -1; } } return 0; } case WM_LBUTTONDBLCLK: { TCHITTESTINFO thinfo; int tabId; thinfo.pt.x = LOWORD(lParam); thinfo.pt.y = HIWORD(lParam); tabId = TabCtrl_HitTest(hwnd, &thinfo); if (tabId >=0 && tabId == dat->srcTab) { SendMessage(GetChildFromTab(hwnd, tabId)->hwnd, WM_CLOSE, 0, 0); dat->srcTab = -1; } dat->destTab = -1; } break; case WM_LBUTTONDOWN: if (!dat->bDragging) { TCHITTESTINFO thinfo; FILETIME ft; thinfo.pt.x = LOWORD(lParam); thinfo.pt.y = HIWORD(lParam); dat->srcTab = TabCtrl_HitTest(hwnd, &thinfo); GetSystemTimeAsFileTime(&ft); if (dat->srcTab >=0 ) { dat->bDragging = TRUE; dat->bDragged = FALSE; dat->clickLParam = lParam; dat->clickWParam = wParam; dat->lastClickTime = ft.dwLowDateTime; dat->mouseLBDownPos.x = thinfo.pt.x; dat->mouseLBDownPos.y = thinfo.pt.y; SetCapture(hwnd); } return 0; } break; case WM_CAPTURECHANGED: case WM_LBUTTONUP: if (dat->bDragging) { TCHITTESTINFO thinfo; thinfo.pt.x = LOWORD(lParam); thinfo.pt.y = HIWORD(lParam); if (dat->bDragged) { ImageList_DragLeave(GetDesktopWindow()); ImageList_EndDrag(); ImageList_Destroy(dat->hDragImageList); SetCursor(LoadCursor(NULL, IDC_ARROW)); dat->destTab = TabCtrl_HitTest(hwnd, &thinfo); if (thinfo.flags != TCHT_NOWHERE && dat->destTab != dat->srcTab) { NMHDR nmh; TCHAR sBuffer[501]; TCITEM item; int curSel; curSel = TabCtrl_GetCurSel(hwnd); item.mask = TCIF_IMAGE | TCIF_PARAM | TCIF_TEXT; item.pszText = sBuffer; item.cchTextMax = SIZEOF(sBuffer); TabCtrl_GetItem(hwnd, dat->srcTab, &item); sBuffer[SIZEOF(sBuffer)-1] = '\0'; if (curSel == dat->srcTab) { curSel = dat->destTab; } else { if (curSel > dat->srcTab && curSel <= dat->destTab) { curSel--; } else if (curSel < dat->srcTab && curSel >= dat->destTab) { curSel++; } } TabCtrl_DeleteItem(hwnd, dat->srcTab); TabCtrl_InsertItem(hwnd, dat->destTab, &item ); TabCtrl_SetCurSel(hwnd, curSel); dat->destTab = -1; nmh.hwndFrom = hwnd; nmh.idFrom = GetDlgCtrlID(hwnd); nmh.code = TCN_SELCHANGE; SendMessage(GetParent(hwnd), WM_NOTIFY, nmh.idFrom, (LPARAM)&nmh); UpdateWindow(hwnd); } else if (thinfo.flags == TCHT_NOWHERE) { MessageWindowTabData *mwtd; TCITEM tci; POINT pt; NewMessageWindowLParam newData = { 0 }; dat->destTab = -1; tci.mask = TCIF_PARAM; TabCtrl_GetItem(hwnd, dat->srcTab, &tci); mwtd = (MessageWindowTabData *) tci.lParam; if (mwtd != NULL) { HWND hChild = mwtd->hwnd; HANDLE hContact = mwtd->hContact; HWND hParent; GetCursorPos(&pt); hParent = WindowFromPoint(pt); while (GetParent(hParent) != NULL) { hParent = GetParent(hParent); } hParent = WindowList_Find(g_dat.hParentWindowList, hParent); if ((hParent != NULL && hParent != GetParent(hwnd)) || (hParent == NULL && mwtd->parent->childrenCount > 1 && (GetKeyState(VK_CONTROL) & 0x8000))) { if (hParent == NULL) { MONITORINFO mi; HMONITOR hMonitor; RECT rc, rcDesktop; newData.hContact = hContact; hParent = (HWND)CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_MSGWIN), NULL, DlgProcParentWindow, (LPARAM)& newData); GetWindowRect(hParent, &rc); rc.right = (rc.right - rc.left); rc.bottom = (rc.bottom - rc.top); rc.left = pt.x - rc.right / 2; rc.top = pt.y - rc.bottom / 2; hMonitor = MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST); mi.cbSize = sizeof(mi); GetMonitorInfo(hMonitor, &mi); rcDesktop = mi.rcWork; if (rc.left < rcDesktop.left) { rc.left = rcDesktop.left; } if (rc.top < rcDesktop.top) { rc.top = rcDesktop.top; } MoveWindow(hParent, rc.left, rc.top, rc.right, rc.bottom, FALSE); } NotifyLocalWinEvent(hContact, hChild, MSG_WINDOW_EVT_CLOSING); NotifyLocalWinEvent(hContact, hChild, MSG_WINDOW_EVT_CLOSE); SetParent(hChild, hParent); SendMessage(GetParent(hwnd), CM_REMOVECHILD, 0, (LPARAM)hChild); SendMessage(hChild, DM_SETPARENT, 0, (LPARAM)hParent); SendMessage(hParent, CM_ADDCHILD, (WPARAM)hChild, (LPARAM)hContact); SendMessage(hChild, DM_UPDATETABCONTROL, 0, 0); SendMessage(hParent, CM_ACTIVATECHILD, 0, (LPARAM)hChild); NotifyLocalWinEvent(hContact, hChild, MSG_WINDOW_EVT_OPENING); NotifyLocalWinEvent(hContact, hChild, MSG_WINDOW_EVT_OPEN); ShowWindow(hParent, SW_SHOWNA); EnableWindow(hParent, TRUE); } } } else { dat->destTab = -1; RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); } } else if (dat->srcTab >= 0 && g_dat.flags2 & SMF2_TABCLOSEBUTTON) { IMAGEINFO info; POINT pt; RECT rect; int atTop = (GetWindowLongPtr(hwnd, GWL_STYLE) & TCS_BOTTOM) == 0; TabCtrl_GetItemRect(hwnd, dat->srcTab, &rect); pt.x = LOWORD(lParam); pt.y = HIWORD(lParam); ImageList_GetImageInfo(g_dat.hButtonIconList, 0, &info); rect.left = rect.right - (info.rcImage.right - info.rcImage.left) - 6; if (!atTop) { rect.top = rect.bottom - (info.rcImage.bottom - info.rcImage.top); } if (pt.x >= rect.left && pt.x < rect.left + (info.rcImage.right - info.rcImage.left) && pt.y >= rect.top && pt.y < rect.top + (info.rcImage.bottom - info.rcImage.top)) { HBITMAP hOldBitmap, hBmp; COLORREF color1, color2; HDC hdc = GetDC(NULL); HDC hdcMem = CreateCompatibleDC(hdc); pt.x -= rect.left; pt.y -= rect.top; hBmp = CreateCompatibleBitmap(hdc, info.rcImage.right - info.rcImage.left + 1, info.rcImage.bottom - info.rcImage.top + 1); hOldBitmap = (HBITMAP)SelectObject(hdcMem, hBmp); SetPixel(hdcMem, pt.x, pt.y, 0x000000); ImageList_DrawEx(g_dat.hButtonIconList, 0, hdcMem, 0, 0, 0, 0, CLR_NONE, CLR_NONE, ILD_NORMAL); color1 = GetPixel(hdcMem, pt.x, pt.y); SetPixel(hdcMem, pt.x, pt.y, 0xFFFFFF); ImageList_DrawEx(g_dat.hButtonIconList, 0, hdcMem, 0, 0, 0, 0, CLR_NONE, CLR_NONE, ILD_NORMAL); color2 = GetPixel(hdcMem, pt.x, pt.y); SelectObject(hdcMem, hOldBitmap); DeleteDC(hdcMem); DeleteObject(hBmp); ReleaseDC(NULL, hdc); if (color1 != 0x000000 || color2 != 0xFFFFFF) { SendMessage(GetChildFromTab(hwnd, dat->srcTab)->hwnd, WM_CLOSE, 0, 0); dat->srcTab = -1; } } else { SendMessage(hwnd, WM_LBUTTONDOWN, dat->clickWParam, dat->clickLParam); } } else { SendMessage(hwnd, WM_LBUTTONDOWN, dat->clickWParam, dat->clickLParam); } dat->bDragged = FALSE; dat->bDragging = FALSE; dat->destTab = -1; ReleaseCapture(); } break; case WM_MOUSEMOVE: if (wParam & MK_LBUTTON) { if (dat->bDragging) { FILETIME ft; TCHITTESTINFO thinfo; GetSystemTimeAsFileTime(&ft); thinfo.pt.x = LOWORD(lParam); thinfo.pt.y = HIWORD(lParam); if (!dat->bDragged) { if ((abs(thinfo.pt.x-dat->mouseLBDownPos.x)<3 && abs(thinfo.pt.y-dat->mouseLBDownPos.y)<3) || (ft.dwLowDateTime - dat->lastClickTime) < 10*1000*150) break; } if (!dat->bDragged) { POINT pt; RECT rect; RECT rect2; HDC hDC, hMemDC; HBITMAP hBitmap, hOldBitmap; HBRUSH hBrush = CreateSolidBrush(RGB(255,0,254)); GetCursorPos(&pt); TabCtrl_GetItemRect(hwnd, dat->srcTab, &rect); rect.right -= rect.left-1; rect.bottom -= rect.top-1; rect2.left = 0; rect2.right = rect.right; rect2.top = 0; rect2.bottom = rect.bottom; dat->hDragImageList = ImageList_Create(rect.right, rect.bottom, ILC_COLOR | ILC_MASK, 0, 1); hDC = GetDC(hwnd); hMemDC = CreateCompatibleDC(hDC); hBitmap = CreateCompatibleBitmap(hDC, rect.right, rect.bottom); hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap); FillRect(hMemDC, &rect2, hBrush); SetWindowOrgEx (hMemDC, rect.left, rect.top, NULL); SendMessage(hwnd, WM_PRINTCLIENT, (WPARAM)hMemDC, PRF_CLIENT); SelectObject(hMemDC, hOldBitmap); ImageList_AddMasked(dat->hDragImageList, hBitmap, RGB(255,0,254)); DeleteObject(hBitmap); DeleteObject(hBrush); ReleaseDC(hwnd, hDC); DeleteDC(hMemDC); ImageList_BeginDrag(dat->hDragImageList, 0, dat->mouseLBDownPos.x - rect.left, dat->mouseLBDownPos.y - rect.top); ImageList_DragEnter(GetDesktopWindow(), pt.x, pt.y); SetCursor(hDragCursor); dat->mouseLBDownPos.x = thinfo.pt.x; dat->mouseLBDownPos.y = thinfo.pt.y; } else { TCHITTESTINFO thinfo; POINT pt; int newDest; GetCursorPos(&pt); thinfo.pt = pt; ScreenToClient(hwnd, &thinfo.pt); newDest = TabCtrl_HitTest(hwnd, &thinfo); if (thinfo.flags == TCHT_NOWHERE) { newDest = -1; } if (newDest != dat->destTab) { dat->destTab = newDest; ImageList_DragLeave(GetDesktopWindow()); RedrawWindow(hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW); ImageList_DragEnter(GetDesktopWindow(), pt.x, pt.y); } else { ImageList_DragMove(pt.x, pt.y); } } dat->bDragged = TRUE; return 0; } } break; case EM_UNSUBCLASSED: mir_free(dat); return 0; } return mir_callNextSubclass(hwnd, TabCtrlProc, msg, wParam, lParam); } int ScriverRestoreWindowPosition(HWND hwnd,HANDLE hContact,const char *szModule,const char *szNamePrefix, int flags, int showCmd) { RECT rcDesktop; WINDOWPLACEMENT wp; char szSettingName[64]; int x,y; MONITORINFO mi; HMONITOR hMonitor; // SystemParametersInfo(SPI_GETWORKAREA, 0, &rcDesktop, 0); wp.length=sizeof(wp); GetWindowPlacement(hwnd,&wp); wsprintfA(szSettingName,"%sx",szNamePrefix); x=db_get_dw(hContact,szModule,szSettingName,-1); wsprintfA(szSettingName,"%sy",szNamePrefix); y=(int)db_get_dw(hContact,szModule,szSettingName,-1); if (x==-1) return 1; if (flags&RWPF_NOSIZE) { OffsetRect(&wp.rcNormalPosition,x-wp.rcNormalPosition.left,y-wp.rcNormalPosition.top); } else { wp.rcNormalPosition.left=x; wp.rcNormalPosition.top=y; wsprintfA(szSettingName,"%swidth",szNamePrefix); wp.rcNormalPosition.right=wp.rcNormalPosition.left+db_get_dw(hContact,szModule,szSettingName,-1); wsprintfA(szSettingName,"%sheight",szNamePrefix); wp.rcNormalPosition.bottom=wp.rcNormalPosition.top+db_get_dw(hContact,szModule,szSettingName,-1); } wp.flags=0; wp.showCmd = showCmd; hMonitor = MonitorFromRect(&wp.rcNormalPosition, MONITOR_DEFAULTTONEAREST); mi.cbSize = sizeof(mi); GetMonitorInfo(hMonitor, &mi); rcDesktop = mi.rcWork; if (wp.rcNormalPosition.left > rcDesktop.right || wp.rcNormalPosition.top > rcDesktop.bottom || wp.rcNormalPosition.right < rcDesktop.left || wp.rcNormalPosition.bottom < rcDesktop.top) return 1; SetWindowPlacement(hwnd,&wp); return 0; } HWND GetParentWindow(HANDLE hContact, BOOL bChat) { NewMessageWindowLParam newData = { 0 }; newData.hContact = hContact; newData.isChat = bChat; if (g_dat.flags2 & SMF2_USETABS) { if (!bChat || !(g_dat.flags2 & SMF2_SEPARATECHATSCONTAINERS)) { if (g_dat.lastParent != NULL) { int tabsNum = (int) SendMessage(g_dat.lastParent->hwnd, CM_GETCHILDCOUNT, 0, 0); if (!(g_dat.flags2 & SMF2_LIMITTABS) || tabsNum < g_dat.limitTabsNum) { return g_dat.lastParent->hwnd; } } } else { if (g_dat.lastChatParent != NULL) { int tabsNum = (int) SendMessage(g_dat.lastChatParent->hwnd, CM_GETCHILDCOUNT, 0, 0); if (!(g_dat.flags2 & SMF2_LIMITCHATSTABS) || tabsNum < g_dat.limitChatsTabsNum) { return g_dat.lastChatParent->hwnd; } } } } if (!(g_dat.flags2 & SMF2_SEPARATECHATSCONTAINERS)) { newData.isChat =FALSE; } return CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_MSGWIN), NULL, DlgProcParentWindow, (LPARAM)& newData); }