/* former MetaContacts Plugin for Miranda IM. Copyright © 2014 Miranda NG Team Copyright © 2004-07 Scott Ellis Copyright © 2004 Universite Louis PASTEUR, STRASBOURG. 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 "metacontacts.h" // Holds the differents changes that have to made struct { MCONTACT hMeta; // HANDLE of the MetaContact that is edited. DBCachedContact *cc; MCONTACT hDefaultContact; // HANDLE of the new default contact MCONTACT hOfflineContact; int num_deleted, // DWORD number of deleted contacts num_contacts; // DWORD number of contacts MCONTACT hDeletedContacts[MAX_CONTACTS]; // HANDLEs of the subcontacts to be removed from this metacontact MCONTACT hContact[MAX_CONTACTS]; // HANDLEs of the subcontacts, in the order they should be in } static g_data; // global CHANGES structure ///////////////////////////////////////////////////////////////////////////////////////// static void FillContactList(HWND hList) { TCHAR buff[256]; SendMessage(hList, LVM_DELETEALLITEMS, 0, 0); LVITEM LvItem = { 0 }; LvItem.mask = LVIF_TEXT; // Text Style LvItem.cchTextMax = 256; // Max size of test for (int i = 0; i < g_data.num_contacts; i++) { LvItem.iItem = i; TCHAR *ptszCDN = cli.pfnGetContactDisplayName(g_data.hContact[i], 0); if (ptszCDN == NULL) ptszCDN = TranslateT("(Unknown contact)"); LvItem.iSubItem = 0; // clist display name LvItem.pszText = ptszCDN; ListView_InsertItem(hList, &LvItem); LvItem.iSubItem = 1; // id char *szProto = GetContactProto(g_data.hContact[i]); if (szProto) { PROTOACCOUNT *pa = ProtoGetAccount(szProto); char *szField = (char *)CallProtoService(szProto, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0); DBVARIANT dbv; if (!db_get(g_data.hContact[i], szProto, szField, &dbv)) { switch (dbv.type) { case DBVT_ASCIIZ: _tcsncpy(buff, _A2T(dbv.pszVal), SIZEOF(buff)); break; case DBVT_WCHAR: _tcsncpy(buff, dbv.ptszVal, SIZEOF(buff)); break; case DBVT_BYTE: _itot(dbv.bVal, buff, 10); break; case DBVT_WORD: _itot(dbv.wVal, buff, 10); break; case DBVT_DWORD: _itot(dbv.dVal, buff, 10); break; default: buff[0] = 0; } db_free(&dbv); } else buff[0] = 0; LvItem.pszText = buff; SendMessage(hList, LVM_SETITEM, 0, (LPARAM)&LvItem); // Enter text to SubItems LvItem.iSubItem = 2; // protocol _tcsncpy_s(buff, (pa == NULL) ? _A2T(szProto) : pa->tszAccountName, _TRUNCATE); ListView_SetItem(hList, &LvItem); } else { LvItem.pszText = TranslateT("Unknown"); ListView_SetItem(hList, &LvItem); LvItem.iSubItem = 2; // protocol ListView_SetItem(hList, &LvItem); } LvItem.iSubItem = 3; // Default (Yes/No) LvItem.pszText = (g_data.hContact[i] == g_data.hDefaultContact ? TranslateT("Yes") : TranslateT("No")); ListView_SetItem(hList, &LvItem); LvItem.iSubItem = 4; // Offline (Yes/No) LvItem.pszText = (g_data.hContact[i] == g_data.hOfflineContact ? TranslateT("Yes") : TranslateT("No")); ListView_SetItem(hList, &LvItem); } } static void SetListSelection(HWND hList, int sel) { ListView_SetItemState(hList, sel, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); } /** Scans the \c CHANGES and call the appropriate function for each change. * * @param chg : Structure holding all the change info (See CHANGES). */ static void ApplyChanges() { // remove removed contacts for (int i = 0; i < g_data.num_deleted; i++) { Meta_Delete(g_data.hDeletedContacts[i], 0); if (g_data.hDeletedContacts[i] == g_data.hDefaultContact) g_data.hDefaultContact = 0; if (g_data.hDeletedContacts[i] == g_data.hOfflineContact) g_data.hOfflineContact = 0; } // set contact positions for (int i = 0; i < g_data.num_contacts; i++) if (Meta_GetContactNumber(g_data.cc, g_data.hContact[i]) != i) Meta_SwapContacts(g_data.cc, Meta_GetContactNumber(g_data.cc, g_data.hContact[i]), i); NotifyEventHooks(hSubcontactsChanged, g_data.hMeta, g_data.hDefaultContact); // set default db_mc_setDefaultNum(g_data.hMeta, (g_data.hDefaultContact) ? Meta_GetContactNumber(g_data.cc, g_data.hDefaultContact) : 0, true); // set offline if (g_data.hOfflineContact) db_set_dw(g_data.hMeta, META_PROTO, "OfflineSend", Meta_GetContactNumber(g_data.cc, g_data.hOfflineContact)); else db_set_dw(g_data.hMeta, META_PROTO, "OfflineSend", INVALID_CONTACT_ID); // fix nick MCONTACT most_online = Meta_GetMostOnline(g_data.cc); Meta_CopyContactNick(g_data.cc, most_online); // fix status Meta_FixStatus(g_data.cc); // fix avatar most_online = Meta_GetMostOnlineSupporting(g_data.cc, PFLAGNUM_4, PF4_AVATARS); if (most_online) { PROTO_AVATAR_INFORMATIONT AI = { sizeof(AI) }; AI.hContact = g_data.hMeta; AI.format = PA_FORMAT_UNKNOWN; _tcscpy(AI.filename, _T("X")); if (CallProtoService(META_PROTO, PS_GETAVATARINFOT, 0, (LPARAM)&AI) == GAIR_SUCCESS) db_set_ts(g_data.hMeta, "ContactPhoto", "File", AI.filename); } } LRESULT ProcessCustomDraw(LPARAM lParam) { LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam; switch (lplvcd->nmcd.dwDrawStage) { case CDDS_PREPAINT: //Before the paint cycle begins //request notifications for individual listview items return CDRF_NOTIFYITEMDRAW; case CDDS_ITEMPREPAINT: //Before an item is drawn if (g_data.hContact[(int)lplvcd->nmcd.dwItemSpec] == g_data.hDefaultContact) lplvcd->clrText = RGB(255, 0, 0); return CDRF_NEWFONT; } return 0; } /** Callback function for the 'Edit' Dialog. * * All the UI is controlled here, from display to functionnalities. * * @param hwndDlg : HANDLE to the 'Edit' Dialog. * @param uMsg : Specifies the message received by this dialog. * @param wParam : Specifies additional message-specific information. * @param lParam : Specifies additional message-specific information (handle of MetaContact to edit) * * @return TRUE if the dialog processed the message, FALSE if it did not. */ #define WMU_SETTITLE (WM_USER + 1) static INT_PTR CALLBACK Meta_EditDialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { HWND hwndList = GetDlgItem(hwndDlg, IDC_LST_CONTACTS); int sel; switch (msg) { case WM_INITDIALOG: TranslateDialogDefault(hwndDlg); SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadIconEx(I_EDIT)); { DBCachedContact *cc = currDb->m_cache->GetCachedContact(lParam); if (cc == NULL) { DestroyWindow(hwndDlg); return FALSE; } // Disable the 'Apply' button. EnableWindow(GetDlgItem(hwndDlg, IDC_VALIDATE), FALSE); ListView_SetExtendedListViewStyle(hwndList, LVS_EX_FULLROWSELECT); // Create list columns LVCOLUMN LvCol = { 0 }; LvCol.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; LvCol.pszText = TranslateT("Contact"); LvCol.cx = 150; ListView_InsertColumn(hwndList, 0, &LvCol); LvCol.pszText = TranslateT("ID"); LvCol.cx = 130; ListView_InsertColumn(hwndList, 1, &LvCol); LvCol.pszText = TranslateT("Protocol"); LvCol.cx = 100; ListView_InsertColumn(hwndList, 2, &LvCol); LvCol.pszText = TranslateT("Default"); LvCol.cx = 60; ListView_InsertColumn(hwndList, 3, &LvCol); LvCol.pszText = TranslateT("Send offline"); LvCol.cx = 85; ListView_InsertColumn(hwndList, 4, &LvCol); int offline_contact_number = db_get_dw(lParam, META_PROTO, "OfflineSend", INVALID_CONTACT_ID); ZeroMemory(&g_data, sizeof(g_data)); g_data.cc = cc; g_data.hMeta = lParam; g_data.num_contacts = cc->nSubs; g_data.num_deleted = 0; g_data.hDefaultContact = Meta_GetContactHandle(g_data.cc, cc->nDefault); g_data.hOfflineContact = Meta_GetContactHandle(g_data.cc, offline_contact_number); for (int i = 0; i < cc->nSubs; i++) g_data.hContact[i] = Meta_GetContactHandle(g_data.cc, i); SendMessage(hwndDlg, WMU_SETTITLE, 0, lParam); } FillContactList(hwndList); ListView_SetItemState(hwndList, 0, LVIS_FOCUSED | LVIS_SELECTED, 0x000F); return TRUE; case WMU_SETTITLE: { TCHAR *ptszCDN = cli.pfnGetContactDisplayName(lParam, 0); if (ptszCDN == NULL) ptszCDN = TranslateT("(Unknown contact)"); SetDlgItemText(hwndDlg, IDC_ED_NAME, ptszCDN); } return TRUE; case WM_NOTIFY: if (LOWORD(wParam) == IDC_LST_CONTACTS) { LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam; if (pnmv->hdr.code == LVN_ITEMCHANGED) { int sel = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED | LVNI_SELECTED); // return item selected // enable buttons EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_REM), sel != -1); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_SETDEFAULT), sel != -1 && g_data.hContact[sel] != g_data.hDefaultContact); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_UP), sel > 0); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_DOWN), (sel != -1 && sel < g_data.num_contacts - 1)); HWND hwndOffline = GetDlgItem(hwndDlg, IDC_BTN_SETOFFLINE); EnableWindow(hwndOffline, sel != -1); SetWindowText(hwndOffline, TranslateTS((sel != -1 && g_data.hContact[sel] != g_data.hOfflineContact) ? LPGENT("Send &offline") : LPGENT("Send &online"))); } } break; case WM_COMMAND: // the message that is being sent always switch (HIWORD(wParam)) { case BN_CLICKED: // A button ('Remove', 'OK', 'Cancel' or 'Apply', normally) has been clicked switch (LOWORD(wParam)) { case IDC_VALIDATE: // Apply changes, if there is still one contact attached to the metacontact. if (g_data.num_contacts == 0) { // Otherwise, delete the metacontact. if (IDYES == MessageBox(hwndDlg, TranslateT(szDelMsg), TranslateT("Delete metacontact?"), MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON1)) { Meta_Delete(g_data.hMeta, 0); DestroyWindow(hwndDlg); } return TRUE; } ApplyChanges(); // Disable the 'Apply' button. EnableWindow(GetDlgItem(hwndDlg, IDC_VALIDATE), FALSE); break; case IDOK: if (IsWindowEnabled(GetDlgItem(hwndDlg, IDC_VALIDATE))) { // If there are changes that could be made, if (g_data.num_contacts == 0) { // do the work that would have be done if the 'Apply' button was clicked. if (IDYES == MessageBox(hwndDlg, TranslateT(szDelMsg), TranslateT("Delete metacontact?"), MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON1)) { Meta_Delete(g_data.hMeta, 0); DestroyWindow(hwndDlg); } return TRUE; } ApplyChanges(); } EndDialog(hwndDlg, IDOK); return TRUE; case IDCANCEL: // Simply close the dialog EndDialog(hwndDlg, IDCANCEL); return TRUE; case IDC_BTN_SETDEFAULT: sel = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED | LVNI_SELECTED); InvalidateRect(hwndList, 0, TRUE); g_data.hDefaultContact = g_data.hContact[sel]; SendMessage(hwndDlg, WMU_SETTITLE, 0, (LPARAM)g_data.hContact[sel]); FillContactList(hwndList); SetListSelection(hwndList, sel); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_SETDEFAULT), FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_VALIDATE), TRUE); return TRUE; case IDC_BTN_SETOFFLINE: sel = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED | LVNI_SELECTED); InvalidateRect(hwndList, 0, TRUE); if (g_data.hContact[sel] != g_data.hOfflineContact) g_data.hOfflineContact = g_data.hContact[sel]; else g_data.hOfflineContact = 0; FillContactList(hwndList); SetListSelection(hwndList, sel); EnableWindow(GetDlgItem(hwndDlg, IDC_VALIDATE), TRUE); return TRUE; case IDC_BTN_REM: sel = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED | LVNI_SELECTED); g_data.num_contacts--; g_data.hDeletedContacts[g_data.num_deleted++] = g_data.hContact[sel]; if (g_data.hDefaultContact == g_data.hContact[sel]) { if (g_data.num_contacts > 0) { g_data.hDefaultContact = g_data.hContact[0]; SetWindowText(GetDlgItem(hwndDlg, IDC_ED_DEFAULT), cli.pfnGetContactDisplayName(g_data.hDefaultContact, 0)); } else { g_data.hDefaultContact = 0; SetWindowText(GetDlgItem(hwndDlg, IDC_ED_DEFAULT), _T("None")); } } for (int i = sel; i < g_data.num_contacts; i++) g_data.hContact[i] = g_data.hContact[i + 1]; FillContactList(hwndList); // disable buttons EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_REM), FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_SETDEFAULT), FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_UP), FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_DOWN), FALSE); // Enable the 'Apply' button. EnableWindow(GetDlgItem(hwndDlg, IDC_VALIDATE), TRUE); return TRUE; case IDC_BTN_UP: sel = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED | LVNI_SELECTED); { MCONTACT temp = g_data.hContact[sel]; g_data.hContact[sel] = g_data.hContact[sel - 1]; g_data.hContact[sel - 1] = temp; } FillContactList(hwndList); sel--; SetListSelection(hwndList, sel); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_UP), (sel > 0)); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_DOWN), (sel < g_data.num_contacts - 1)); EnableWindow(GetDlgItem(hwndDlg, IDC_VALIDATE), TRUE); return TRUE; case IDC_BTN_DOWN: sel = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED | LVNI_SELECTED); { MCONTACT temp = g_data.hContact[sel]; g_data.hContact[sel] = g_data.hContact[sel + 1]; g_data.hContact[sel + 1] = temp; } FillContactList(hwndList); sel++; SetListSelection(hwndList, sel); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_UP), (sel > 0)); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_DOWN), (sel < g_data.num_contacts - 1)); EnableWindow(GetDlgItem(hwndDlg, IDC_VALIDATE), TRUE); return TRUE; } } break; case WM_CLOSE: DestroyWindow(hwndDlg); return TRUE; case WM_DESTROY: Skin_ReleaseIcon((HICON)SendMessage(hwndDlg, WM_SETICON, ICON_BIG, 0)); EndDialog(hwndDlg, IDCANCEL); break; } return FALSE; } /** Display the 'Edit' Dialog * * Present a dialog in which the user can edit some properties of the MetaContact. * * @param wParam : HANDLE to the MetaContact to be edited. * @param lParam : Allways set to 0. */ INT_PTR Meta_Edit(WPARAM wParam, LPARAM lParam) { HWND clui = (HWND)CallService(MS_CLUI_GETHWND, 0, 0); DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_METAEDIT), clui, Meta_EditDialogProc, (LPARAM)wParam); return 0; }