//////////////////////////////////////////////////////////////////////////////// // Gadu-Gadu Plugin for Miranda IM // // Copyright (c) 2009-2012 Bartosz Białek // // 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 "gg.h" #define GGS_CONCUR_SESS "%s/ConcurSess" static void gg_clearsessionslist(HWND hwndDlg) { HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS); LV_COLUMN column = {0}; RECT rc; int iWidth; if (!hList) return; ListView_DeleteAllItems(hList); while (ListView_DeleteColumn(hList, 0)); column.mask = LVCF_TEXT; column.cx = 500; column.pszText = TranslateT("Client Name"); ListView_InsertColumn(hList, 1, &column); column.pszText=TranslateT("IP Address"); ListView_InsertColumn(hList, 2, &column); column.pszText = TranslateT("Login Time"); ListView_InsertColumn(hList, 3, &column); column.pszText = TranslateT("Action"); ListView_InsertColumn(hList, 4, &column); GetClientRect(hList, &rc); iWidth = rc.right - rc.left; ListView_SetColumnWidth(hList, 0, iWidth * 45 / 100); ListView_SetColumnWidth(hList, 1, iWidth * 20 / 100); ListView_SetColumnWidth(hList, 2, iWidth * 20 / 100); ListView_SetColumnWidth(hList, 3, LVSCW_AUTOSIZE_USEHEADER); } static void ListView_SetItemTextA(HWND hwndLV, int i, int iSubItem, char* pszText) { LV_ITEMA _ms_lvi; _ms_lvi.iSubItem = iSubItem; _ms_lvi.pszText = pszText; SendMessageA(hwndLV, LVM_SETITEMTEXTA, i, (LPARAM)&_ms_lvi); } static int gg_insertlistitem(HWND hList, gg_multilogon_id_t* id, const char* clientName, const char* ip, const char* loginTime) { LVITEM item = {0}; int index; item.iItem = ListView_GetItemCount(hList); item.mask = LVIF_PARAM; item.lParam = (LPARAM)id; index = ListView_InsertItem(hList, &item); if (index < 0) return index; ListView_SetItemTextA(hList, index, 0, (char*)clientName); ListView_SetItemTextA(hList, index, 1, (char*)ip); ListView_SetItemTextA(hList, index, 2, (char*)loginTime); ListView_SetItemText(hList, index, 3, TranslateT("sign out")); return index; } static void gg_listsessions(GGPROTO* gg, HWND hwndDlg) { HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS); list_t l; if (!hList) return; EnterCriticalSection(&gg->sessions_mutex); for (l = gg->sessions; l; l = l->next) { struct gg_multilogon_session* sess = (struct gg_multilogon_session*)l->data; struct in_addr ia; char* ip; char loginTime[20]; ia.S_un.S_addr = sess->remote_addr; ip = inet_ntoa(ia); strftime(loginTime, sizeof(loginTime), "%d-%m-%Y %H:%M:%S", localtime(&sess->logon_time)); gg_insertlistitem(hList, &sess->id, sess->name, ip, loginTime); } LeaveCriticalSection(&gg->sessions_mutex); EnableWindow(GetDlgItem(hwndDlg, IDC_SIGNOUTALL), ListView_GetItemCount(hList) > 0); } static int sttSessionsDlgResizer(HWND hwndDlg, LPARAM lParam, UTILRESIZECONTROL* urc) { switch (urc->wId) { case IDC_HEADERBAR: return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH; case IDC_SESSIONS: return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORY_HEIGHT | RD_ANCHORX_WIDTH; case IDC_SIGNOUTALL: return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; } return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; } static BOOL IsOverAction(HWND hwndDlg) { HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS); LVHITTESTINFO hti; RECT rc; HDC hdc; TCHAR szText[256]; SIZE textSize; int textPosX; GetCursorPos(&hti.pt); ScreenToClient(hList, &hti.pt); GetClientRect(hList, &rc); if (!PtInRect(&rc, hti.pt) || ListView_SubItemHitTest(hList, &hti) == -1 || hti.iSubItem != 3 || !(hti.flags & LVHT_ONITEMLABEL)) return FALSE; ListView_GetSubItemRect(hList, hti.iItem, hti.iSubItem, LVIR_LABEL, &rc); szText[0] = 0; ListView_GetItemText(hList, hti.iItem, hti.iSubItem, szText, SIZEOF(szText)); hdc = GetDC(hList); GetTextExtentPoint32(hdc, szText, lstrlen(szText), &textSize); ReleaseDC(hList, hdc); textPosX = rc.left + (((rc.right - rc.left) - textSize.cx) / 2); return (hti.pt.x > textPosX && hti.pt.x < textPosX + textSize.cx); } static HCURSOR hHandCursor = NULL; #define WM_MULTILOGONINFO (WM_USER + 10) #define HM_PROTOACK (WM_USER + 11) static INT_PTR CALLBACK gg_sessions_viewdlg(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) { GGPROTO* gg = (GGPROTO*)GetWindowLongPtr(hwndDlg, DWLP_USER); switch (message) { case WM_INITDIALOG: { GGPROTO* gg = (GGPROTO*)lParam; TCHAR oldTitle[256], newTitle[256]; HANDLE hProtoAckEvent; SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)lParam); TranslateDialogDefault(hwndDlg); GetDlgItemText(hwndDlg, IDC_HEADERBAR, oldTitle, SIZEOF(oldTitle)); mir_snprintf(newTitle, SIZEOF(newTitle), oldTitle, GG_PROTONAME); SetDlgItemText(hwndDlg, IDC_HEADERBAR, newTitle); WindowSetIcon(hwndDlg, "sessions"); gg->hwndSessionsDlg = hwndDlg; if (hHandCursor == NULL) hHandCursor = LoadCursor(NULL, IDC_HAND); hProtoAckEvent = HookEventMessage(ME_PROTO_ACK, hwndDlg, HM_PROTOACK); SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)hProtoAckEvent); ListView_SetExtendedListViewStyle(GetDlgItem(hwndDlg, IDC_SESSIONS), LVS_EX_FULLROWSELECT); SendMessage(hwndDlg, WM_MULTILOGONINFO, 0, 0); return TRUE; } case HM_PROTOACK: { ACKDATA* ack = (ACKDATA*)lParam; if (!strcmp(ack->szModule, GG_PROTO) && !ack->hContact && ack->type == ACKTYPE_STATUS && ack->result == ACKRESULT_SUCCESS && (ack->lParam == ID_STATUS_OFFLINE || (ack->hProcess == (HANDLE)ID_STATUS_CONNECTING && ack->lParam != ID_STATUS_OFFLINE && !ListView_GetItemCount(GetDlgItem(hwndDlg, IDC_SESSIONS))))) { gg_clearsessionslist(hwndDlg); EnableWindow(GetDlgItem(hwndDlg, IDC_SIGNOUTALL), FALSE); } break; } case WM_MULTILOGONINFO: gg_clearsessionslist(hwndDlg); gg_listsessions(gg, hwndDlg); break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_SIGNOUTALL: { HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS); LVITEM lvi = {0}; int iCount = ListView_GetItemCount(hList), i; lvi.mask = LVIF_PARAM; for (i = 0; i < iCount; i++) { lvi.iItem = i; ListView_GetItem(hList, &lvi); EnterCriticalSection(&gg->sess_mutex); gg_multilogon_disconnect(gg->sess, *((gg_multilogon_id_t*)lvi.lParam)); LeaveCriticalSection(&gg->sess_mutex); } break; } } break; case WM_NOTIFY: if (((LPNMHDR)lParam)->idFrom == IDC_SESSIONS) { switch (((LPNMHDR)lParam)->code) { case NM_CUSTOMDRAW: { LPNMLVCUSTOMDRAW nm = (LPNMLVCUSTOMDRAW)lParam; switch (nm->nmcd.dwDrawStage) { case CDDS_PREPAINT: if (ListView_GetItemCount(nm->nmcd.hdr.hwndFrom) == 0) { const LPCTSTR szText = gg_isonline(gg) ? TranslateT("There are no active concurrent sessions for this account.") : TranslateT("You have to be logged in to view concurrent sessions."); RECT rc; HWND hwndHeader = ListView_GetHeader(nm->nmcd.hdr.hwndFrom); SIZE textSize; int textPosX; GetClientRect(nm->nmcd.hdr.hwndFrom, &rc); if (hwndHeader != NULL) { RECT rcHeader; GetClientRect(hwndHeader, &rcHeader); rc.top += rcHeader.bottom; } GetTextExtentPoint32(nm->nmcd.hdc, szText, lstrlen(szText), &textSize); textPosX = rc.left + (((rc.right - rc.left) - textSize.cx) / 2); ExtTextOut(nm->nmcd.hdc, textPosX, rc.top + textSize.cy, ETO_OPAQUE, &rc, szText, lstrlen(szText), NULL); } // FALL THROUGH case CDDS_ITEMPREPAINT: SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT , CDRF_NOTIFYSUBITEMDRAW); return TRUE; case CDDS_SUBITEM | CDDS_ITEMPREPAINT: { RECT rc; ListView_GetSubItemRect(nm->nmcd.hdr.hwndFrom, nm->nmcd.dwItemSpec, nm->iSubItem, LVIR_LABEL, &rc); if (nm->nmcd.hdr.idFrom == IDC_SESSIONS && nm->iSubItem == 3) { TCHAR szText[256]; szText[0] = 0; ListView_GetItemText(nm->nmcd.hdr.hwndFrom, nm->nmcd.dwItemSpec, nm->iSubItem, szText, SIZEOF(szText)); FillRect(nm->nmcd.hdc, &rc, GetSysColorBrush(COLOR_WINDOW)); SetTextColor(nm->nmcd.hdc, RGB(0, 0, 255)); DrawText(nm->nmcd.hdc, szText, -1, &rc, DT_END_ELLIPSIS | DT_CENTER | DT_NOPREFIX | DT_SINGLELINE | DT_TOP); SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT); return TRUE; } break; } } break; } case NM_CLICK: if (IsOverAction(hwndDlg)) { LPNMITEMACTIVATE nm = (LPNMITEMACTIVATE)lParam; LVITEM lvi = {0}; lvi.mask = LVIF_PARAM; lvi.iItem = nm->iItem; ListView_GetItem(nm->hdr.hwndFrom, &lvi); EnterCriticalSection(&gg->sess_mutex); gg_multilogon_disconnect(gg->sess, *((gg_multilogon_id_t*)lvi.lParam)); LeaveCriticalSection(&gg->sess_mutex); } break; } } break; case WM_CONTEXTMENU: { HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS); POINT pt = {(short)LOWORD(lParam), (short)HIWORD(lParam)}, ptDlg = pt; LVHITTESTINFO lvhti = {0}; ScreenToClient(hwndDlg, &ptDlg); if (ChildWindowFromPoint(hwndDlg, ptDlg) == hList) { HMENU hMenu; int iSelection; lvhti.pt = pt; ScreenToClient(hList, &lvhti.pt); if (ListView_HitTest(hList, &lvhti) == -1) break; hMenu = CreatePopupMenu(); AppendMenu(hMenu, MFT_STRING, 10001, TranslateT("Copy Text")); AppendMenu(hMenu, MFT_STRING, 10002, TranslateT("Whois")); iSelection = TrackPopupMenu(hMenu, TPM_RIGHTBUTTON | TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL); switch (iSelection) { case 10001: { char szText[512], szClientName[256], szIP[64], szLoginTime[64]; HGLOBAL hData; if (!OpenClipboard(hwndDlg)) break; EmptyClipboard(); szClientName[0] = szIP[0] = szLoginTime[0] = 0; ListView_GetItemText(hList, lvhti.iItem, 0, szClientName, SIZEOF(szClientName)); ListView_GetItemText(hList, lvhti.iItem, 1, szIP, SIZEOF(szIP)); ListView_GetItemText(hList, lvhti.iItem, 2, szLoginTime, SIZEOF(szLoginTime)); mir_snprintf(szText, SIZEOF(szText), "%s\t%s\t%s", szClientName, szIP, szLoginTime); if ((hData = GlobalAlloc(GMEM_MOVEABLE, lstrlenA(szText) + 1)) != NULL) { lstrcpyA((char*)GlobalLock(hData), szText); GlobalUnlock(hData); SetClipboardData(CF_TEXT, hData); } CloseClipboard(); break; } case 10002: { char szUrl[256], szIP[64]; szIP[0] = 0; ListView_GetItemText(hList, lvhti.iItem, 1, szIP, SIZEOF(szIP)); mir_snprintf(szUrl, SIZEOF(szUrl), "http://whois.domaintools.com/%s", szIP); CallService(MS_UTILS_OPENURL, 1, (LPARAM)szUrl); break; } } DestroyMenu(hMenu); } break; } case WM_GETMINMAXINFO: ((LPMINMAXINFO)lParam)->ptMinTrackSize.x = 620; ((LPMINMAXINFO)lParam)->ptMinTrackSize.y = 220; return 0; case WM_SIZE: { UTILRESIZEDIALOG urd = {0}; urd.cbSize = sizeof(urd); urd.hInstance = hInstance; urd.hwndDlg = hwndDlg; urd.lpTemplate = MAKEINTRESOURCEA(IDD_SESSIONS); urd.pfnResizer = sttSessionsDlgResizer; CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd); return 0; } case WM_SETCURSOR: if (LOWORD(lParam) == HTCLIENT && IsOverAction(hwndDlg)) { SetCursor(hHandCursor); SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); return TRUE; } break; case WM_CLOSE: DestroyWindow(hwndDlg); break; case WM_DESTROY: { HANDLE hProtoAckEvent = (HANDLE)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); if (hProtoAckEvent) UnhookEvent(hProtoAckEvent); gg->hwndSessionsDlg = NULL; WindowFreeIcon(hwndDlg); break; } } return FALSE; } INT_PTR gg_sessions_view(GGPROTO* gg, WPARAM wParam, LPARAM lParam) { if (gg->hwndSessionsDlg && IsWindow(gg->hwndSessionsDlg)) { ShowWindow(gg->hwndSessionsDlg, SW_SHOWNORMAL); SetForegroundWindow(gg->hwndSessionsDlg); SetFocus(gg->hwndSessionsDlg); } else CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_SESSIONS), NULL, gg_sessions_viewdlg, (LPARAM)gg); return 0; } void gg_sessions_updatedlg(GGPROTO* gg) { if (gg->hwndSessionsDlg && IsWindow(gg->hwndSessionsDlg)) SendMessage(gg->hwndSessionsDlg, WM_MULTILOGONINFO, 0, 0); } BOOL gg_sessions_closedlg(GGPROTO* gg) { if (gg->hwndSessionsDlg && IsWindow(gg->hwndSessionsDlg)) return PostMessage(gg->hwndSessionsDlg, WM_CLOSE, 0, 0); return FALSE; } void gg_sessions_menus_init(GGPROTO* gg, HGENMENU hRoot) { CLISTMENUITEM mi = {0}; char service[64]; mi.cbSize = sizeof(mi); mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTHANDLE | CMIF_TCHAR; mi.hParentMenu = hRoot; mir_snprintf(service, sizeof(service), GGS_CONCUR_SESS, GG_PROTO); CreateProtoServiceFunction(service, gg_sessions_view, gg); if (gg->hMenuRoot) mi.position = 2050000001; else mi.position = 200003; mi.icolibItem = GetIconHandle(IDI_SESSIONS); mi.ptszName = LPGENT("Concurrent &sessions"); mi.pszService = service; CallService(MS_CLIST_ADDPROTOMENUITEM, 0, (LPARAM)&mi); }