/* Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) 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 version 2 of the License. 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, see . */ #include "stdafx.h" BOOL ScreenToClient(HWND hWnd, LPRECT lpRect); ///////////////////////////////////////////////////////////////////////////////////////// void LoadTransparentData(HWND hwndDlg, MCONTACT hContact) { CheckDlgButton(hwndDlg, IDC_MAKETRANSPBKG, db_get_b(hContact, "ContactPhoto", "MakeTransparentBkg", g_plugin.getByte("MakeTransparentBkg", 0)) ? BST_CHECKED : BST_UNCHECKED); SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETPOS, 0, (LPARAM)db_get_w(hContact, "ContactPhoto", "TranspBkgNumPoints", g_plugin.getWord("TranspBkgNumPoints", 5))); SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETPOS, 0, (LPARAM)db_get_w(hContact, "ContactPhoto", "TranspBkgColorDiff", g_plugin.getWord("TranspBkgColorDiff", 10))); BOOL transp_enabled = IsDlgButtonChecked(hwndDlg, IDC_MAKETRANSPBKG); EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), transp_enabled); EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), transp_enabled); EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), transp_enabled); EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), transp_enabled); EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), transp_enabled); EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), transp_enabled); } static void SaveTransparentData(HWND hwndDlg, MCONTACT hContact) { BOOL transp = IsDlgButtonChecked(hwndDlg, IDC_MAKETRANSPBKG); if (g_plugin.getByte("MakeTransparentBkg", 0) == transp) db_unset(hContact, "ContactPhoto", "MakeTransparentBkg"); else db_set_b(hContact, "ContactPhoto", "MakeTransparentBkg", transp); uint16_t tmp = (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_GETPOS, 0, 0); if (g_plugin.getWord("TranspBkgNumPoints", 5) == tmp) db_unset(hContact, "ContactPhoto", "TranspBkgNumPoints"); else db_set_w(hContact, "ContactPhoto", "TranspBkgNumPoints", tmp); tmp = (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_GETPOS, 0, 0); if (g_plugin.getWord("TranspBkgColorDiff", 10) == tmp) db_unset(hContact, "ContactPhoto", "TranspBkgColorDiff"); else db_set_w(hContact, "ContactPhoto", "TranspBkgColorDiff", tmp); } void SaveTransparentData(HWND hwndDlg, MCONTACT hContact, BOOL locked) { SaveTransparentData(hwndDlg, hContact); MCONTACT tmp = GetContactThatHaveTheAvatar(hContact, locked); if (tmp != hContact) SaveTransparentData(hwndDlg, tmp); } ///////////////////////////////////////////////////////////////////////////////////////// // User info dialog class AvatarUserInfoDlg : public CUserInfoPageDlg { HANDLE hHook = nullptr; CCtrlSpin spinPoints, spinDifference; CCtrlCheck chkTransparent; CCtrlButton btnHide, btnChange, btnDefaults, btnProtect, btnReset, btnDelete; void ReloadAvatar() { SaveTransparentData(m_hwnd, m_hContact, IsDlgButtonChecked(m_hwnd, IDC_PROTECTAVATAR)); ChangeAvatar(m_hContact, true); } INT_PTR OnChangeAvatar(UINT, WPARAM, LPARAM) { InvalidateRect(GetDlgItem(m_hwnd, IDC_PROTOPIC), nullptr, TRUE); return 0; } UI_MESSAGE_MAP(AvatarUserInfoDlg, CUserInfoPageDlg); UI_MESSAGE(DM_AVATARCHANGED, OnChangeAvatar); UI_MESSAGE_MAP_END(); public: AvatarUserInfoDlg() : CUserInfoPageDlg(g_plugin, IDD_USER_AVATAR), btnHide(this, IDC_HIDEAVATAR), btnReset(this, IDC_RESET), btnChange(this, IDC_CHANGE), btnDelete(this, IDC_DELETE), btnProtect(this, IDC_PROTECTAVATAR), btnDefaults(this, ID_USE_DEFAULTS), chkTransparent(this, IDC_MAKETRANSPBKG), spinPoints(this, IDC_BKG_NUM_POINTS_SPIN, 8, 2), spinDifference(this, IDC_BKG_NUM_POINTS_SPIN, 100) { btnHide.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Hide); btnReset.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Reset); btnChange.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Change); btnDelete.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Delete); btnProtect.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Protect); btnDefaults.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_UseDefaults); chkTransparent.OnChange = Callback(this, &AvatarUserInfoDlg::onChange_Transparent); } bool OnInitDialog() override { hHook = HookEventMessage(ME_AV_AVATARCHANGED, m_hwnd, DM_AVATARCHANGED); if (m_hContact != 0) btnDelete.Hide(); LoadTransparentData(m_hwnd, GetContactThatHaveTheAvatar(m_hContact)); return true; } bool OnRefresh() override { HWND protopic = GetDlgItem(m_hwnd, IDC_PROTOPIC); SendMessage(protopic, AVATAR_SETCONTACT, 0, m_hContact); SendMessage(protopic, AVATAR_SETAVATARBORDERCOLOR, 0, GetSysColor(COLOR_BTNSHADOW)); SendMessage(protopic, AVATAR_SETNOAVATARTEXT, 0, (LPARAM)LPGENW("Contact has no avatar")); SendMessage(protopic, AVATAR_RESPECTHIDDEN, 0, FALSE); SendMessage(protopic, AVATAR_SETRESIZEIFSMALLER, 0, FALSE); CheckDlgButton(m_hwnd, IDC_PROTECTAVATAR, db_get_b(m_hContact, "ContactPhoto", "Locked", 0) ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(m_hwnd, IDC_HIDEAVATAR, Contact::IsHidden(m_hContact) ? BST_CHECKED : BST_UNCHECKED); return false; } bool IsEmpty() const override { auto wszFileName(db_get_wsm(m_hContact, "ContactPhoto", "File")); return (wszFileName.IsEmpty() || _waccess(wszFileName, 0) != 0); } void OnDestroy() override { UnhookEvent(hHook); } void onClick_UseDefaults(CCtrlButton *) { m_hContact = GetContactThatHaveTheAvatar(m_hContact); db_unset(m_hContact, "ContactPhoto", "MakeTransparentBkg"); db_unset(m_hContact, "ContactPhoto", "TranspBkgNumPoints"); db_unset(m_hContact, "ContactPhoto", "TranspBkgColorDiff"); LoadTransparentData(m_hwnd, m_hContact); ReloadAvatar(); } void onClick_Change(CCtrlButton *) { SetAvatar(m_hContact, 0); CheckDlgButton(m_hwnd, IDC_PROTECTAVATAR, db_get_b(m_hContact, "ContactPhoto", "Locked", 0) ? BST_CHECKED : BST_UNCHECKED); } void onClick_Hide(CCtrlButton *) { bool hidden = IsDlgButtonChecked(m_hwnd, IDC_HIDEAVATAR) != 0; SetAvatarAttribute(m_hContact, AVS_HIDEONCLIST, hidden); if (hidden != Contact::IsHidden(m_hContact)) Contact::Hide(m_hContact, hidden); } void onClick_Protect(CCtrlButton *) { BOOL locked = IsDlgButtonChecked(m_hwnd, IDC_PROTECTAVATAR); SaveTransparentData(m_hwnd, m_hContact, locked); ProtectAvatar(m_hContact, locked ? 1 : 0); } void onChange_Transparent(CCtrlCheck *) { BOOL enable = IsDlgButtonChecked(m_hwnd, IDC_MAKETRANSPBKG); EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_L), enable); EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_SPIN), enable); EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS), enable); EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_L), enable); EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_SPIN), enable); EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE), enable); ReloadAvatar(); } void onClick_Reset(CCtrlButton *) { ProtectAvatar(m_hContact, 0); if (MessageBox(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) { DBVARIANT dbv = { 0 }; if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) { DeleteFile(dbv.pwszVal); db_free(&dbv); } } db_unset(m_hContact, "ContactPhoto", "Locked"); db_unset(m_hContact, "ContactPhoto", "Backup"); db_unset(m_hContact, "ContactPhoto", "RFile"); db_unset(m_hContact, "ContactPhoto", "File"); db_unset(m_hContact, "ContactPhoto", "Format"); char *szProto = Proto_GetBaseAccountName(m_hContact); db_unset(m_hContact, szProto, "AvatarHash"); DeleteAvatarFromCache(m_hContact, FALSE); QueueAdd(m_hContact); } void onClick_Delete(CCtrlButton *) { ProtectAvatar(m_hContact, 0); if (MessageBoxW(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) { DBVARIANT dbv = { 0 }; if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) { DeleteFile(dbv.pwszVal); db_free(&dbv); } } db_unset(m_hContact, "ContactPhoto", "Locked"); db_unset(m_hContact, "ContactPhoto", "Backup"); db_unset(m_hContact, "ContactPhoto", "RFile"); db_unset(m_hContact, "ContactPhoto", "File"); db_unset(m_hContact, "ContactPhoto", "Format"); DeleteAvatarFromCache(m_hContact, FALSE); } }; ///////////////////////////////////////////////////////////////////////////////////////// // Protocol avatar dialog class AvatarProtoInfoDlg : public CUserInfoPageDlg { char* GetSelectedProtocol() { // Get selection int iItem = protocols.GetSelectionMark(); if (iItem < 0) return nullptr; // Get protocol name LVITEM item = {0}; item.mask = LVIF_PARAM; item.iItem = iItem; SendMessage(protocols.GetHwnd(), LVM_GETITEMA, 0, (LPARAM)&item); return (char *)item.lParam; } void OffsetWindow(int iCtrlId, int dx) { HWND hwnd = GetDlgItem(m_hwnd, iCtrlId); RECT rc; GetWindowRect(hwnd, &rc); ScreenToClient(m_hwnd, &rc); OffsetRect(&rc, dx, 0); MoveWindow(hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE); } void EnableDisableControls(char *proto) { if (chkPerProto.IsChecked()) { if (proto == nullptr) { EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), FALSE); EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), FALSE); } else { if (!ProtoServiceExists(proto, PS_SETMYAVATAR)) { EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), FALSE); EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), FALSE); } else { EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), TRUE); int width, height; SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_GETUSEDSPACE, (WPARAM)&width, (LPARAM)&height); EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), (LPARAM)width != 0 || height != 0); } } } else { EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), TRUE); if (g_plugin.getByte("GlobalUserAvatarNotConsistent", 1)) EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), TRUE); else { int width, height; SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_GETUSEDSPACE, (WPARAM)&width, (LPARAM)&height); EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), (LPARAM)width != 0 || height != 0); } } } CCtrlCheck chkPerProto; CCtrlButton btnChange, btnDelete; CCtrlListView protocols; public: AvatarProtoInfoDlg() : CUserInfoPageDlg(g_plugin, IDD_PROTO_AVATARS), protocols(this, IDC_PROTOCOLS), btnChange(this, IDC_CHANGE), btnDelete(this, IDC_DELETE), chkPerProto(this, IDC_PER_PROTO) { protocols.OnItemChanged = Callback(this, &AvatarProtoInfoDlg::onItemChanged_List); btnChange.OnClick = Callback(this, &AvatarProtoInfoDlg::onClick_Change); btnDelete.OnClick = Callback(this, &AvatarProtoInfoDlg::onClick_Delete); CreateLink(chkPerProto, g_plugin.bPerProto); chkPerProto.OnChange = Callback(this, &AvatarProtoInfoDlg::onChange_PerProto); } bool OnInitDialog() override { HWND protopic = GetDlgItem(m_hwnd, IDC_PROTOPIC); SendMessage(protopic, AVATAR_SETAVATARBORDERCOLOR, 0, (LPARAM)GetSysColor(COLOR_BTNSHADOW)); SendMessage(protopic, AVATAR_SETNOAVATARTEXT, 0, (LPARAM)LPGENW("No avatar")); SendMessage(protopic, AVATAR_SETRESIZEIFSMALLER, 0, (LPARAM)FALSE); protocols.SetExtendedListViewStyleEx(0, LVS_EX_SUBITEMIMAGES); HIMAGELIST hIml = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 4, 0); protocols.SetImageList(hIml, LVSIL_SMALL); LVCOLUMN lvc = { 0 }; lvc.mask = LVCF_FMT; lvc.fmt = LVCFMT_IMAGE | LVCFMT_LEFT; protocols.InsertColumn(0, &lvc); LVITEM item = { 0 }; item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE; item.iItem = 1000; // List protocols int num = 0; for (auto &it : Accounts()) { if (!ProtoServiceExists(it->szModuleName, PS_GETMYAVATAR)) continue; if (!Proto_IsAvatarsEnabled(it->szModuleName)) continue; ImageList_AddIcon(hIml, Skin_LoadProtoIcon(it->szModuleName, ID_STATUS_ONLINE)); item.pszText = it->tszAccountName; item.iImage = num; item.lParam = (LPARAM)it->szModuleName; protocols.InsertItem(&item); num++; } protocols.SetCurSel(0); protocols.SetColumnWidth(0, LVSCW_AUTOSIZE); protocols.Arrange(LVA_ALIGNLEFT | LVA_ALIGNTOP); return true; } void onItemChanged_List(CCtrlListView::TEventInfo *ev) { LPNMLISTVIEW li = ev->nmlv; if (li->uNewState & LVIS_SELECTED) { SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, li->lParam); EnableDisableControls((char*)li->lParam); } } INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override { if (msg == WM_NOTIFY) { LPNMHDR hdr = (LPNMHDR)lParam; if (hdr->idFrom == IDC_PROTOPIC && hdr->code == NM_AVATAR_CHANGED) EnableDisableControls(GetSelectedProtocol()); } return CDlgBase::DlgProc(msg, wParam, lParam); } void onClick_Change(CCtrlButton *) { if (!chkPerProto.IsChecked()) SetMyAvatar(NULL, NULL); else { char *proto = GetSelectedProtocol(); if (proto != nullptr) SetMyAvatar((WPARAM)proto, NULL); } } void onClick_Delete(CCtrlButton *) { if (!chkPerProto.IsChecked()) { if (MessageBox(m_hwnd, TranslateT("Are you sure you want to remove your avatar?"), TranslateT("Global avatar"), MB_YESNO) == IDYES) SetMyAvatar(NULL, (LPARAM)L""); } else { if (char *proto = GetSelectedProtocol()) { char description[256]; CallProtoService(proto, PS_GETNAME, _countof(description), (LPARAM)description); wchar_t *descr = mir_a2u(description); if (MessageBox(m_hwnd, TranslateT("Are you sure you want to remove your avatar?"), descr, MB_YESNO) == IDYES) SetMyAvatar((WPARAM)proto, (LPARAM)L""); mir_free(descr); } } } void onChange_PerProto(CCtrlCheck *pCheck) { int diff = 147; // Pre-calc if (chkPerProto.IsChecked()) { if (!IsWindowVisible(protocols.GetHwnd())) { // Show list of protocols protocols.Show(); // Move controls OffsetWindow(IDC_PROTOPIC, diff); OffsetWindow(IDC_CHANGE, diff); OffsetWindow(IDC_DELETE, diff); } char *proto = GetSelectedProtocol(); if (proto == nullptr) { protocols.SetItemState(0, LVIS_FOCUSED | LVIS_SELECTED, 0x0F); } else { SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, (LPARAM)proto); EnableDisableControls(proto); } } else { if (!m_bInitialized) { PostMessage(m_hwnd, WM_COMMAND, IDC_PER_PROTO, 0); } else if (!pCheck || IsWindowVisible(protocols.GetHwnd())) { // Hide list of protocols protocols.Hide(); // Move controls OffsetWindow(IDC_PROTOPIC, -diff); OffsetWindow(IDC_CHANGE, -diff); OffsetWindow(IDC_DELETE, -diff); } SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, NULL); } } }; int OnDetailsInit(WPARAM wParam, LPARAM hContact) { USERINFOPAGE uip = {}; uip.szTitle.a = LPGEN("Avatar"); uip.flags = ODPF_ICON; uip.dwInitParam = (LPARAM)g_plugin.getIconHandle(IDI_AVATAR); if (hContact == NULL) { // User dialog uip.pDialog = new AvatarProtoInfoDlg(); g_plugin.addUserInfo(wParam, &uip); } else { char *szProto = Proto_GetBaseAccountName(hContact); if (szProto == nullptr || g_plugin.getByte(szProto, 1)) { // Contact dialog uip.position = -2000000000; uip.pDialog = new AvatarUserInfoDlg(); g_plugin.addUserInfo(wParam, &uip); } } return 0; }