/* Miranda NG: the free IM client for Microsoft* Windows* Copyright (c) 2012-14 Miranda NG project (http://miranda-ng.org), Copyright (c) 2000-12 Miranda 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 "..\..\core\commonheaders.h" #include "findadd.h" enum { COLUMNID_PROTO, COLUMNID_HANDLE, COLUMNID_NICK, COLUMNID_FIRST, COLUMNID_LAST, COLUMNID_EMAIL, NUM_COLUMNID }; void SaveColumnSizes(HWND hwndResults) { int columnOrder[NUM_COLUMNID]; int columnCount; char szSetting[32]; int i; struct FindAddDlgData *dat; dat = (struct FindAddDlgData*)GetWindowLongPtr(GetParent(hwndResults), GWLP_USERDATA); columnCount = Header_GetItemCount(ListView_GetHeader(hwndResults)); if (columnCount != NUM_COLUMNID) return; ListView_GetColumnOrderArray(hwndResults, columnCount, columnOrder); for (i=0; i < NUM_COLUMNID; i++) { mir_snprintf(szSetting, SIZEOF(szSetting), "ColOrder%d", i); db_set_b(NULL, "FindAdd", szSetting, (BYTE)columnOrder[i]); if (i>=columnCount) continue; mir_snprintf(szSetting, SIZEOF(szSetting), "ColWidth%d", i); db_set_w(NULL, "FindAdd", szSetting, (WORD)ListView_GetColumnWidth(hwndResults, i)); } db_set_b(NULL, "FindAdd", "SortColumn", (BYTE)dat->iLastColumnSortIndex); db_set_b(NULL, "FindAdd", "SortAscending", (BYTE)dat->bSortAscending); } static const TCHAR *szColumnNames[] = { NULL, NULL, _T("Nick"), _T("First Name"), _T("Last Name"), _T("E-mail") }; static int defaultColumnSizes[] = {0, 90, 100, 100, 100, 2000}; void LoadColumnSizes(HWND hwndResults, const char *szProto) { HDITEM hdi; int columnOrder[NUM_COLUMNID]; int columnCount; char szSetting[32]; int i; FindAddDlgData *dat; bool colOrdersValid; defaultColumnSizes[COLUMNID_PROTO] = GetSystemMetrics(SM_CXSMICON) + 4; dat = (FindAddDlgData*)GetWindowLongPtr(GetParent(hwndResults), GWLP_USERDATA); columnCount = NUM_COLUMNID; colOrdersValid = true; for (i=0; i < NUM_COLUMNID; i++) { LVCOLUMN lvc; if (i < columnCount) { int bNeedsFree = FALSE; lvc.mask = LVCF_TEXT | LVCF_WIDTH; if (szColumnNames[i] != NULL) lvc.pszText = TranslateTS(szColumnNames[i]); else if (i == COLUMNID_HANDLE) { if (szProto) { bNeedsFree = TRUE; lvc.pszText = mir_a2t((char*)CallProtoServiceInt(NULL,szProto, PS_GETCAPS, PFLAG_UNIQUEIDTEXT, 0)); } else lvc.pszText = _T("ID"); } else lvc.mask &= ~LVCF_TEXT; mir_snprintf(szSetting, SIZEOF(szSetting), "ColWidth%d", i); lvc.cx = db_get_w(NULL, "FindAdd", szSetting, defaultColumnSizes[i]); ListView_InsertColumn(hwndResults, i, (LPARAM)&lvc); if (bNeedsFree) mir_free(lvc.pszText); } mir_snprintf(szSetting, SIZEOF(szSetting), "ColOrder%d", i); columnOrder[i] = db_get_b(NULL, "FindAdd", szSetting, -1); if (columnOrder[i] == -1 || columnOrder[i] >= NUM_COLUMNID) colOrdersValid = false; } if (colOrdersValid) ListView_SetColumnOrderArray(hwndResults, columnCount, columnOrder); dat->iLastColumnSortIndex = db_get_b(NULL, "FindAdd", "SortColumn", COLUMNID_NICK); if (dat->iLastColumnSortIndex >= columnCount) dat->iLastColumnSortIndex = COLUMNID_NICK; dat->bSortAscending = db_get_b(NULL, "FindAdd", "SortAscending", TRUE); hdi.mask = HDI_FORMAT; hdi.fmt = HDF_LEFT | HDF_STRING | (dat->bSortAscending ? HDF_SORTDOWN : HDF_SORTUP); Header_SetItem(ListView_GetHeader(hwndResults), dat->iLastColumnSortIndex, &hdi); } static LPARAM ListView_GetItemLParam(HWND hwndList, int idx) { LVITEM lv; lv.iItem = idx; lv.mask = LVIF_PARAM; ListView_GetItem(hwndList, &lv); return lv.lParam; } int CALLBACK SearchResultsCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { struct FindAddDlgData *dat = (struct FindAddDlgData*)GetWindowLongPtr((HWND) lParamSort, GWLP_USERDATA); int sortMultiplier; int sortCol; struct ListSearchResult *lsr1, *lsr2; HWND hList = GetDlgItem((HWND) lParamSort, IDC_RESULTS); sortMultiplier = dat->bSortAscending?1:-1; sortCol = dat->iLastColumnSortIndex; if (!dat->bFlexSearchResult) { lsr1 = (struct ListSearchResult*)ListView_GetItemLParam(hList, (int)lParam1); lsr2 = (struct ListSearchResult*)ListView_GetItemLParam(hList, (int)lParam2); if (lsr1 == NULL || lsr2 == NULL) return 0; switch(sortCol) { case COLUMNID_PROTO: return lstrcmpA(lsr1->szProto, lsr2->szProto)*sortMultiplier; case COLUMNID_HANDLE: return lstrcmpi(lsr1->psr.id, lsr2->psr.id)*sortMultiplier; case COLUMNID_NICK: return lstrcmpi(lsr1->psr.nick, lsr2->psr.nick)*sortMultiplier; case COLUMNID_FIRST: return lstrcmpi(lsr1->psr.firstName, lsr2->psr.firstName)*sortMultiplier; case COLUMNID_LAST: return lstrcmpi(lsr1->psr.lastName, lsr2->psr.lastName)*sortMultiplier; case COLUMNID_EMAIL: return lstrcmpi(lsr1->psr.email, lsr2->psr.email)*sortMultiplier; } } else { TCHAR szText1[100]; TCHAR szText2[100]; ListView_GetItemText(hList, (int)lParam1, sortCol, szText1, SIZEOF(szText1)); ListView_GetItemText(hList, (int)lParam2, sortCol, szText2, SIZEOF(szText2)); return _tcsicmp(szText1, szText2)*sortMultiplier; } return 0; } void FreeSearchResults(HWND hwndResults) { LV_ITEM lvi; struct ListSearchResult *lsr; for (lvi.iItem = ListView_GetItemCount(hwndResults)-1;lvi.iItem>=0;lvi.iItem--) { lvi.mask = LVIF_PARAM; ListView_GetItem(hwndResults, &lvi); lsr = (struct ListSearchResult*)lvi.lParam; if (lsr == NULL) continue; mir_free(lsr->psr.id); mir_free(lsr->psr.email); mir_free(lsr->psr.nick); mir_free(lsr->psr.firstName); mir_free(lsr->psr.lastName); mir_free(lsr); } ListView_DeleteAllItems(hwndResults); EnableResultButtons(GetParent(hwndResults), 0); } // on its own thread static void BeginSearchFailed(void * arg) { TCHAR buf[128]; if (arg != NULL) { const TCHAR* protoName = (TCHAR*)arg; mir_sntprintf(buf, SIZEOF(buf), TranslateT("Could not start a search on '%s', there was a problem - is %s connected?"), protoName, protoName); mir_free((char*)arg); } else lstrcpyn(buf, TranslateT("Could not search on any of the protocols, are you online?"), SIZEOF(buf)); MessageBox(0, buf, TranslateT("Problem with search"), MB_OK | MB_ICONERROR); } int BeginSearch(HWND, struct FindAddDlgData *dat, const char *szProto, const char *szSearchService, DWORD requiredCapability, void *pvSearchParams) { if (szProto == NULL) { int failures = 0; dat->searchCount = 0; dat->search = (struct ProtoSearchInfo*)mir_calloc(sizeof(struct ProtoSearchInfo) * accounts.getCount()); for (int i=0; i < accounts.getCount();i++) { PROTOACCOUNT* pa = accounts[i]; if (!Proto_IsAccountEnabled(pa)) continue; DWORD caps = (DWORD)CallProtoServiceInt(NULL,pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); if (!(caps&requiredCapability)) continue; dat->search[dat->searchCount].hProcess = (HANDLE)CallProtoServiceInt(NULL,pa->szModuleName, szSearchService, 0, (LPARAM)pvSearchParams); dat->search[dat->searchCount].szProto = pa->szModuleName; if (dat->search[dat->searchCount].hProcess == NULL) failures++; else dat->searchCount++; } if (failures) { //infuriatingly vague error message. fixme. if (dat->searchCount == 0) { forkthread(BeginSearchFailed, 0, NULL); mir_free(dat->search); dat->search = NULL; return 1; } } } else { dat->search = (struct ProtoSearchInfo*)mir_alloc(sizeof(struct ProtoSearchInfo)); dat->searchCount = 1; dat->search[0].hProcess = (HANDLE)CallProtoServiceInt(NULL,szProto, szSearchService, 0, (LPARAM)pvSearchParams); dat->search[0].szProto = szProto; if (dat->search[0].hProcess == NULL) { //infuriatingly vague error message. fixme. PROTOACCOUNT* pa = Proto_GetAccount(szProto); forkthread(BeginSearchFailed, 0, mir_tstrdup(pa->tszAccountName)); mir_free(dat->search); dat->search = NULL; dat->searchCount = 0; return 1; } } return 0; } // !!!!!!!! this code is dangerous like a hell void SetStatusBarSearchInfo(HWND hwndStatus, struct FindAddDlgData *dat) { TCHAR str[256]; if (dat->searchCount != 0) { int i; lstrcpy(str, TranslateT("Searching")); for (i=0; i < dat->searchCount; i++) { PROTOACCOUNT* pa = Proto_GetAccount(dat->search[i].szProto); if (!pa) continue; lstrcat(str, i ? _T(", ") : _T(" ")); lstrcat(str, pa->tszAccountName); } } else lstrcpy(str, TranslateT("Idle")); SendMessage(hwndStatus, SB_SETTEXT, 0, (LPARAM)str); } struct ProtoResultsSummary { const char *szProto; int count; }; void SetStatusBarResultInfo(HWND hwndDlg) { HWND hwndStatus = GetDlgItem(hwndDlg, IDC_STATUSBAR); HWND hwndResults = GetDlgItem(hwndDlg, IDC_RESULTS); LV_ITEM lvi; struct ListSearchResult *lsr; struct ProtoResultsSummary *subtotal = NULL; int subtotalCount = 0; int i, total; TCHAR str[256]; total = ListView_GetItemCount(hwndResults); for (lvi.iItem = total-1;lvi.iItem>=0;lvi.iItem--) { lvi.mask = LVIF_PARAM; ListView_GetItem(hwndResults, &lvi); lsr = (struct ListSearchResult*)lvi.lParam; if (lsr == NULL) continue; for (i=0;iszProto) { subtotal[i].count++; break; } } if (i == subtotalCount) { subtotal = (struct ProtoResultsSummary*)mir_realloc(subtotal, sizeof(struct ProtoResultsSummary)*(subtotalCount+1)); subtotal[subtotalCount].szProto = lsr->szProto; subtotal[subtotalCount++].count = 1; } } if (total != 0) { TCHAR substr[64]; PROTOACCOUNT* pa = Proto_GetAccount(subtotal[0].szProto); if (pa == NULL) return; if (subtotalCount == 1) { if (total == 1) mir_sntprintf(str, SIZEOF(str), TranslateT("1 %s user found"), pa->tszAccountName); else mir_sntprintf(str, SIZEOF(str), TranslateT("%d %s users found"), total, pa->tszAccountName); } else { mir_sntprintf(str, SIZEOF(str), TranslateT("%d users found ("), total); for (i=0; i < subtotalCount; i++) { if (i) { if ((pa = Proto_GetAccount(subtotal[i].szProto)) == NULL) return; lstrcat(str, _T(", ")); } mir_sntprintf(substr, SIZEOF(substr), _T("%d %s"), subtotal[i].count, pa->tszAccountName); lstrcat(str, substr); } lstrcat(str, _T(")")); } mir_free(subtotal); } else lstrcpy(str, TranslateT("No users found")); SendMessage(hwndStatus, SB_SETTEXT, 2, (LPARAM)str); } void CreateResultsColumns(HWND hwndResults, struct FindAddDlgData *dat, char *szProto) { SaveColumnSizes(hwndResults); while (ListView_DeleteColumn(hwndResults, 0)); ListView_SetImageList(hwndResults, dat->himlComboIcons, LVSIL_SMALL); LoadColumnSizes(hwndResults, szProto); } void ShowMoreOptionsMenu(HWND hwndDlg, int x, int y) { struct FindAddDlgData *dat; HMENU hPopupMenu, hMenu; int commandId; struct ListSearchResult *lsr; dat = (struct FindAddDlgData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); { LVITEM lvi; if (ListView_GetSelectedCount( GetDlgItem(hwndDlg, IDC_RESULTS)) != 1) return; lvi.mask = LVIF_PARAM; lvi.iItem = ListView_GetNextItem( GetDlgItem(hwndDlg, IDC_RESULTS), -1, LVNI_ALL|LVNI_SELECTED); ListView_GetItem( GetDlgItem(hwndDlg, IDC_RESULTS), &lvi); lsr = (struct ListSearchResult*)lvi.lParam; } hMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_CONTEXT)); hPopupMenu = GetSubMenu(hMenu, 4); TranslateMenu(hPopupMenu); commandId = TrackPopupMenu(hPopupMenu, TPM_RIGHTBUTTON|TPM_RETURNCMD, x, y, 0, hwndDlg, NULL); switch(commandId) { case IDC_ADD: { ADDCONTACTSTRUCT acs; acs.handle = NULL; acs.handleType = HANDLE_SEARCHRESULT; acs.szProto = lsr->szProto; acs.psr = &lsr->psr; CallService(MS_ADDCONTACT_SHOW, (WPARAM)hwndDlg, (LPARAM)&acs); break; } case IDC_DETAILS: { HANDLE hContact; hContact = (HANDLE)CallProtoServiceInt(NULL,lsr->szProto, PS_ADDTOLIST, PALF_TEMPORARY, (LPARAM)&lsr->psr); CallService(MS_USERINFO_SHOWDIALOG, (WPARAM)hContact, 0); break; } case IDC_SENDMESSAGE: { HANDLE hContact; hContact = (HANDLE)CallProtoServiceInt(NULL,lsr->szProto, PS_ADDTOLIST, PALF_TEMPORARY, (LPARAM)&lsr->psr); CallService(MS_MSG_SENDMESSAGE, (WPARAM)hContact, (LPARAM)(const char*)NULL); break; } } DestroyMenu(hPopupMenu); DestroyMenu(hMenu); }