From 1979fd80424d16b2e489f9b57d01d9c7811d25a2 Mon Sep 17 00:00:00 2001 From: dartraiden Date: Mon, 2 Jan 2023 21:10:29 +0300 Subject: Update copyrights --- protocols/JabberG/src/jabber_roster.cpp | 1102 +++++++++++++++---------------- 1 file changed, 551 insertions(+), 551 deletions(-) (limited to 'protocols/JabberG/src/jabber_roster.cpp') diff --git a/protocols/JabberG/src/jabber_roster.cpp b/protocols/JabberG/src/jabber_roster.cpp index 5072fe9ebb..03d9e9ffdd 100644 --- a/protocols/JabberG/src/jabber_roster.cpp +++ b/protocols/JabberG/src/jabber_roster.cpp @@ -1,551 +1,551 @@ -/* - -Jabber Protocol Plugin for Miranda NG - -Copyright (c) 2002-04 Santithorn Bunchua -Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-22 Miranda NG team - -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 "stdafx.h" - -#include - -////////////////////////////////////////////////////////////////////////// -// roster editor -// - -enum -{ - RRA_FILLLIST = 0, - RRA_SYNCROSTER, - RRA_SYNCDONE -}; - -struct ROSTEREDITDAT -{ - HWND hList; - int index; - int subindex; -}; - -static void _RosterItemEditEnd(HWND hEditor, ROSTEREDITDAT *edat, BOOL bCancel) -{ - if (!bCancel) { - int len = GetWindowTextLength(hEditor) + 1; - wchar_t *buff = (wchar_t*)mir_alloc(len*sizeof(wchar_t)); - if (buff) { - GetWindowText(hEditor, buff, len); - ListView_SetItemText(edat->hList, edat->index, edat->subindex, buff); - } - mir_free(buff); - } - DestroyWindow(hEditor); -} - -static LRESULT CALLBACK _RosterItemNewEditProc(HWND hEditor, UINT msg, WPARAM wParam, LPARAM lParam) -{ - ROSTEREDITDAT * edat = (ROSTEREDITDAT *)GetWindowLongPtr(hEditor, GWLP_USERDATA); - if (!edat) return 0; - switch (msg) { - case WM_KEYDOWN: - switch (wParam) { - case VK_RETURN: - _RosterItemEditEnd(hEditor, edat, FALSE); - return 0; - case VK_ESCAPE: - _RosterItemEditEnd(hEditor, edat, TRUE); - return 0; - } - break; - - case WM_GETDLGCODE: - if (lParam) { - MSG *msg2 = (MSG*)lParam; - if (msg2->message == WM_KEYDOWN && msg2->wParam == VK_TAB) return 0; - if (msg2->message == WM_CHAR && msg2->wParam == '\t') return 0; - } - return DLGC_WANTMESSAGE; - - case WM_KILLFOCUS: - _RosterItemEditEnd(hEditor, edat, FALSE); - return 0; - - case WM_DESTROY: - SetWindowLongPtr(hEditor, GWLP_USERDATA, (LONG_PTR)0); - free(edat); - return 0; - } - - return mir_callNextSubclass(hEditor, _RosterItemNewEditProc, msg, wParam, lParam); -} - -static LRESULT CALLBACK _RosterNewListProc(HWND hList, UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (msg == WM_MOUSEWHEEL || msg == WM_NCLBUTTONDOWN || msg == WM_NCRBUTTONDOWN) - SetFocus(hList); - - if (msg == WM_LBUTTONDOWN) { - POINT pt; - GetCursorPos(&pt); - ScreenToClient(hList, &pt); - - LVHITTESTINFO lvhti = { 0 }; - lvhti.pt = pt; - ListView_SubItemHitTest(hList, &lvhti); - if (lvhti.flags&LVHT_ONITEM && lvhti.iSubItem != 0) { - RECT rc; - wchar_t buff[260]; - ListView_GetSubItemRect(hList, lvhti.iItem, lvhti.iSubItem, LVIR_BOUNDS, &rc); - ListView_GetItemText(hList, lvhti.iItem, lvhti.iSubItem, buff, _countof(buff)); - HWND hEditor = CreateWindow(TEXT("EDIT"), buff, WS_CHILD | ES_AUTOHSCROLL, rc.left + 3, rc.top + 2, rc.right - rc.left - 3, rc.bottom - rc.top - 3, hList, nullptr, g_plugin.getInst(), nullptr); - SendMessage(hEditor, WM_SETFONT, (WPARAM)SendMessage(hList, WM_GETFONT, 0, 0), 0); - ShowWindow(hEditor, SW_SHOW); - SetWindowText(hEditor, buff); - ClientToScreen(hList, &pt); - ScreenToClient(hEditor, &pt); - mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); - mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); - - ROSTEREDITDAT * edat = (ROSTEREDITDAT *)malloc(sizeof(ROSTEREDITDAT)); - mir_subclassWindow(hEditor, _RosterItemNewEditProc); - edat->hList = hList; - edat->index = lvhti.iItem; - edat->subindex = lvhti.iSubItem; - SetWindowLongPtr(hEditor, GWLP_USERDATA, (LONG_PTR)edat); - } - } - return mir_callNextSubclass(hList, _RosterNewListProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// JabberRosterOptDlgProc - advanced options dialog procedure - -class CRosterEditorDlg : public CJabberDlgBase -{ - friend struct CJabberProto; - typedef CJabberDlgBase CSuper; - - uint8_t m_bRRAction; - BOOL m_bReadyToDownload = true; - BOOL m_bReadyToUpload = false; - - void _RosterSendRequest(uint8_t rrAction) - { - m_bRRAction = rrAction; - - m_proto->m_ThreadInfo->send( - XmlNodeIq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_GET)) - << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER)); - } - - int _RosterInsertListItem(const char *jid, const char *nick, const char *group, const char *subscr, bool bChecked) - { - Utf2T wszJid(jid); - LVITEM item = { 0 }; - item.mask = LVIF_TEXT | LVIF_STATE; - item.iItem = m_list.GetItemCount(); - item.pszText = wszJid; - - int index = m_list.InsertItem(&item); - if (index < 0) - return index; - - m_list.SetCheckState(index, bChecked); - - m_list.SetItemText(index, 1, Utf2T(nick)); - m_list.SetItemText(index, 2, Utf2T(group)); - m_list.SetItemText(index, 3, TranslateW(Utf2T(subscr))); - return index; - } - - void _RosterListClear() - { - m_list.DeleteAllItems(); - while (m_list.GetColumnWidth(0) > 0) - m_list.DeleteColumn(0); - - LV_COLUMN column = { 0 }; - column.mask = LVCF_TEXT; - - column.pszText = TranslateT("JID"); - m_list.InsertColumn(1, &column); - - column.pszText = TranslateT("Nickname"); - m_list.InsertColumn(2, &column); - - column.pszText = TranslateT("Group"); - m_list.InsertColumn(3, &column); - - column.pszText = TranslateT("Subscription"); - m_list.InsertColumn(4, &column); - - RECT rc; - GetClientRect(m_list.GetHwnd(), &rc); - ResizeColumns(rc.right - rc.left); - } - - void ResizeColumns(int width) - { - m_list.SetColumnWidth(0, width * 40 / 100); - m_list.SetColumnWidth(1, width * 25 / 100); - m_list.SetColumnWidth(2, width * 20 / 100); - m_list.SetColumnWidth(3, width * 12 / 100); - } - - void OnChangeStatus() - { - int count = m_list.GetItemCount(); - btnDownload.Enable(m_proto->m_bJabberOnline); - btnUpload.Enable(count && m_proto->m_bJabberOnline); - btnExport.Enable(count > 0); - } - - CCtrlButton btnDownload, btnUpload, btnExport, btnImport; - CCtrlListView m_list; - -public: - CRosterEditorDlg(CJabberProto *m_proto) : - CSuper(m_proto, IDD_ROSTER_EDITOR), - m_list(this, IDC_ROSTER), - btnExport(this, IDC_EXPORT), - btnImport(this, IDC_IMPORT), - btnUpload(this, IDC_UPLOAD), - btnDownload(this, IDC_DOWNLOAD) - { - SetMinSize(550, 390); - - btnExport.OnClick = Callback(this, &CRosterEditorDlg::onClick_Export); - btnImport.OnClick = Callback(this, &CRosterEditorDlg::onClick_Import); - btnUpload.OnClick = Callback(this, &CRosterEditorDlg::onClick_Upload); - btnDownload.OnClick = Callback(this, &CRosterEditorDlg::onClick_Download); - } - - bool OnInitDialog() override - { - SetWindowTextW(m_hwnd, CMStringW(FORMAT, L"%s: %s", TranslateT("Roster Editor"), m_proto->m_tszUserName)); - - Window_SetIcon_IcoLib(m_hwnd, g_plugin.getIconHandle(IDI_AGENTS)); - - Utils_RestoreWindowPosition(m_hwnd, 0, m_proto->m_szModuleName, "rosterCtrlWnd_"); - - m_list.SetExtendedListViewStyle(LVS_EX_CHECKBOXES | LVS_EX_BORDERSELECT | LVS_EX_GRIDLINES); - mir_subclassWindow(m_list.GetHwnd(), _RosterNewListProc); - _RosterListClear(); - OnChangeStatus(); - return true; - } - - void OnDestroy() override - { - m_proto->m_hwndRosterEditor = nullptr; - Utils_SaveWindowPosition(m_hwnd, 0, m_proto->m_szModuleName, "rosterCtrlWnd_"); - Window_FreeIcon_IcoLib(m_hwnd); - } - - int Resizer(UTILRESIZECONTROL *urc) override - { - switch (urc->wId) { - case IDC_HEADERBAR: - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH; - case IDC_ROSTER: - ResizeColumns(urc->rcItem.right - urc->rcItem.left + urc->dlgNewSize.cx - urc->dlgOriginalSize.cx); - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORY_HEIGHT | RD_ANCHORX_WIDTH; - case IDC_DOWNLOAD: - case IDC_UPLOAD: - return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; - case IDC_EXPORT: - case IDC_IMPORT: - return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; - } - - void HandleNode(const TiXmlElement *node) - { - if (m_bRRAction == RRA_FILLLIST) { - _RosterListClear(); - auto *query = XmlFirstChild(node, "query"); - if (query == nullptr) return; - - for (auto *item : TiXmlFilter(query, "item")) { - const char *jid = XmlGetAttr(item, "jid"); - if (jid == nullptr) - continue; - - const char *name = XmlGetAttr(item, "name"); - const char *subscription = XmlGetAttr(item, "subscription"); - const char *group = XmlGetChildText(item, "group"); - _RosterInsertListItem(jid, name, group, subscription, true); - } - - // now it is require to process whole contact list to add not in roster contacts - for (auto &hContact : m_proto->AccContacts()) { - ptrW tszJid(m_proto->getWStringA(hContact, "jid")); - if (tszJid == nullptr) - continue; - - LVFINDINFO lvfi = { 0 }; - lvfi.flags = LVFI_STRING; - lvfi.psz = tszJid; - if (m_list.FindItem(-1, &lvfi) == -1) { - ptrA tszName(db_get_utfa(hContact, "CList", "MyHandle")); - ptrA tszGroup(db_get_utfa(hContact, "CList", "Group")); - _RosterInsertListItem(T2Utf(tszJid), tszName, tszGroup, nullptr, false); - } - } - m_bReadyToDownload = false; - m_bReadyToUpload = true; - btnDownload.SetText(TranslateT("Download")); - btnUpload.SetText(TranslateT("Upload")); - OnChangeStatus(); - return; - } - - if (m_bRRAction == RRA_SYNCROSTER) { - btnUpload.SetText(TranslateT("Uploading...")); - auto *queryRoster = XmlFirstChild(node, "query"); - if (!queryRoster) - return; - - int ListItemCount = m_list.GetItemCount(); - for (int index = 0; index < ListItemCount; index++) { - wchar_t jid[JABBER_MAX_JID_LEN] = L""; - wchar_t name[260]; - wchar_t group[260]; - wchar_t subscr[260]; - m_list.GetItemText(index, 0, jid, _countof(jid)); - m_list.GetItemText(index, 1, name, _countof(name)); - m_list.GetItemText(index, 2, group, _countof(group)); - m_list.GetItemText(index, 3, subscr, _countof(subscr)); - - T2Utf szJid(jid), szName(name), szGroup(group), szSubscr(subscr); - auto *itemRoster = XmlGetChildByTag(queryRoster, "item", "jid", szJid); - bool bRemove = !m_list.GetCheckState(index); - if (itemRoster && bRemove) { // delete item - XmlNodeIq iq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_SET)); - iq << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER) << XCHILD("item") << XATTR("jid", szJid) << XATTR("subscription", "remove"); - m_proto->m_ThreadInfo->send(iq); - } - else if (!bRemove) { - bool bPushed = itemRoster != 0; - const char *rosterName = 0, *rosterGroup = 0; - if (!bPushed) { - rosterName = XmlGetAttr(itemRoster, "name"); - if ((rosterName != nullptr || name[0] != 0) && mir_strcmpi(rosterName, szName)) - bPushed = true; - if (!bPushed) { - auto *szSub = XmlGetAttr(itemRoster, "subscription"); - if ((szSub != nullptr || subscr[0] != 0) && mir_strcmpi(szSub, szSubscr)) - bPushed = true; - } - if (!bPushed) { - rosterGroup = XmlGetChildText(itemRoster, "group"); - if (rosterGroup != nullptr && mir_strcmpi(rosterGroup, szGroup)) - bPushed = true; - } - } - if (bPushed) { - XmlNodeIq iq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_SET)); - auto *item = iq << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER) << XCHILD("item"); - if (rosterGroup || mir_strlen(szGroup)) - item << XCHILD("group", szGroup); - if (rosterName || mir_strlen(szName)) - item << XATTR("name", szName); - item << XATTR("jid", szJid) << XATTR("subscription", subscr[0] ? szSubscr : "none"); - m_proto->m_ThreadInfo->send(iq); - } - } - } - m_bRRAction = RRA_SYNCDONE; - _RosterSendRequest(RRA_FILLLIST); - return; - } - - btnUpload.SetText(TranslateT("Upload")); - onClick_Download(nullptr); - }; - - void onClick_Download(CCtrlButton*) - { - m_bReadyToUpload = m_bReadyToDownload = false; - OnChangeStatus(); - btnDownload.SetText(TranslateT("Downloading...")); - _RosterSendRequest(RRA_FILLLIST); - } - - void onClick_Upload(CCtrlButton*) - { - m_bReadyToUpload = false; - OnChangeStatus(); - btnUpload.SetText(TranslateT("Connecting...")); - _RosterSendRequest(RRA_SYNCROSTER); - } - - void onClick_Export(CCtrlButton*) - { - wchar_t filename[MAX_PATH] = { 0 }; - - wchar_t filter[MAX_PATH]; - mir_snwprintf(filter, L"%s (*.xml)%c*.xml%c%c", TranslateT("XML (UTF-8 encoded)"), 0, 0, 0); - OPENFILENAME ofn = {}; - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = m_hwnd; - ofn.lpstrFilter = filter; - ofn.lpstrFile = filename; - ofn.Flags = OFN_HIDEREADONLY; - ofn.nMaxFile = _countof(filename); - ofn.nMaxFileTitle = MAX_PATH; - ofn.lpstrDefExt = L"xml"; - if (!GetSaveFileNameW(&ofn)) - return; - - FILE * fp = _wfopen(filename, L"wb"); - if (!fp) - return; - - int ListItemCount = m_list.GetItemCount(); - - XmlNode root("Roster"); - - for (int index = 0; index < ListItemCount; index++) { - wchar_t jid[JABBER_MAX_JID_LEN] = L""; - wchar_t name[260] = L""; - wchar_t group[260] = L""; - wchar_t subscr[260] = L""; - m_list.GetItemText(index, 0, jid, _countof(jid)); - m_list.GetItemText(index, 1, name, _countof(name)); - m_list.GetItemText(index, 2, group, _countof(group)); - m_list.GetItemText(index, 3, subscr, _countof(subscr)); - root << XCHILD("Item") << XATTR("jid", T2Utf(jid)) << XATTR("name", T2Utf(name)) << XATTR("group", T2Utf(group)) << XATTR("subscription", T2Utf(subscr)); - } - - tinyxml2::XMLPrinter printer(fp); - root.Print(&printer); - fclose(fp); - } - - void onClick_Import(CCtrlButton*) - { - wchar_t filename[MAX_PATH] = {}; - - OPENFILENAME ofn = { 0 }; - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = m_hwnd; - ofn.hInstance = nullptr; - ofn.lpstrFilter = L"XML (UTF-8 encoded)(*.xml)\0*.xml\0\0"; - ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; - ofn.lpstrFile = filename; - ofn.nMaxFile = _countof(filename); - ofn.nMaxFileTitle = MAX_PATH; - ofn.lpstrDefExt = L"xml"; - if (!GetOpenFileNameW(&ofn)) - return; - - FILE * fp = _wfopen(filename, L"rb"); - if (!fp) - return; - - TiXmlDocument doc; - int ret = doc.LoadFile(fp); - fclose(fp); - if (ret != 0) - return; - - _RosterListClear(); - - const TiXmlElement *Table = TiXmlConst(&doc)["Workbook"]["Worksheet"]["Table"].ToElement(); - if (Table) { - for (auto *Row : TiXmlFilter(Table, "Row")) { - bool bAdd = false; - const char *jid = nullptr; - const char *name = nullptr; - const char *group = nullptr; - const char *subscr = nullptr; - auto *Cell = XmlFirstChild(Row, "Cell"); - auto *Data = XmlFirstChild(Cell, "Data"); - if (Data) { - if (!mir_strcmpi(Data->GetText(), "+")) - bAdd = true; - else if (mir_strcmpi(Data->GetText(), "-")) - continue; - - Cell = Cell->NextSiblingElement("Cell"); - if (Cell) Data = XmlFirstChild(Cell, "Data"); - else Data = nullptr; - if (Data) { - jid = Data->GetText(); - if (!jid || mir_strlen(jid) == 0) - continue; - } - - Cell = Cell->NextSiblingElement("Cell"); - if (Cell) Data = XmlFirstChild(Cell, "Data"); - else Data = nullptr; - if (Data) name = Data->GetText(); - - Cell = Cell->NextSiblingElement("Cell"); - if (Cell) Data = XmlFirstChild(Cell, "Data"); - else Data = nullptr; - if (Data) group = Data->GetText(); - - Cell = Cell->NextSiblingElement("Cell"); - if (Cell) Data = XmlFirstChild(Cell, "Data"); - else Data = nullptr; - if (Data) subscr = Data->GetText(); - } - _RosterInsertListItem(jid, name, group, subscr, bAdd); - } - } - else if (Table = TiXmlConst(&doc)["Roster"].ToElement()) { - for (auto *Row : TiXmlFilter(Table, "Item")) { - auto *jid = Row->Attribute("jid"); - auto *name = Row->Attribute("name"); - auto *group = Row->Attribute("group"); - auto *subscr = Row->Attribute("subscription"); - _RosterInsertListItem(jid, name, group, subscr, true); - } - } - - OnChangeStatus(); - } -}; - -INT_PTR __cdecl CJabberProto::OnMenuHandleRosterControl(WPARAM, LPARAM) -{ - if (m_hwndRosterEditor) - SetForegroundWindow(m_hwndRosterEditor->GetHwnd()); - else { - m_hwndRosterEditor = new CRosterEditorDlg(this); - m_hwndRosterEditor->Show(); - } - - return 0; -} - -void CJabberProto::_RosterHandleGetRequest(const TiXmlElement *node, CJabberIqInfo*) -{ - if (m_hwndRosterEditor) - m_hwndRosterEditor->HandleNode(node); -} - -void CJabberProto::JabberUpdateDialogs(BOOL) -{ - if (m_hwndRosterEditor) - m_hwndRosterEditor->OnChangeStatus(); -} +/* + +Jabber Protocol Plugin for Miranda NG + +Copyright (c) 2002-04 Santithorn Bunchua +Copyright (c) 2005-12 George Hazan +Copyright (C) 2012-23 Miranda NG team + +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 "stdafx.h" + +#include + +////////////////////////////////////////////////////////////////////////// +// roster editor +// + +enum +{ + RRA_FILLLIST = 0, + RRA_SYNCROSTER, + RRA_SYNCDONE +}; + +struct ROSTEREDITDAT +{ + HWND hList; + int index; + int subindex; +}; + +static void _RosterItemEditEnd(HWND hEditor, ROSTEREDITDAT *edat, BOOL bCancel) +{ + if (!bCancel) { + int len = GetWindowTextLength(hEditor) + 1; + wchar_t *buff = (wchar_t*)mir_alloc(len*sizeof(wchar_t)); + if (buff) { + GetWindowText(hEditor, buff, len); + ListView_SetItemText(edat->hList, edat->index, edat->subindex, buff); + } + mir_free(buff); + } + DestroyWindow(hEditor); +} + +static LRESULT CALLBACK _RosterItemNewEditProc(HWND hEditor, UINT msg, WPARAM wParam, LPARAM lParam) +{ + ROSTEREDITDAT * edat = (ROSTEREDITDAT *)GetWindowLongPtr(hEditor, GWLP_USERDATA); + if (!edat) return 0; + switch (msg) { + case WM_KEYDOWN: + switch (wParam) { + case VK_RETURN: + _RosterItemEditEnd(hEditor, edat, FALSE); + return 0; + case VK_ESCAPE: + _RosterItemEditEnd(hEditor, edat, TRUE); + return 0; + } + break; + + case WM_GETDLGCODE: + if (lParam) { + MSG *msg2 = (MSG*)lParam; + if (msg2->message == WM_KEYDOWN && msg2->wParam == VK_TAB) return 0; + if (msg2->message == WM_CHAR && msg2->wParam == '\t') return 0; + } + return DLGC_WANTMESSAGE; + + case WM_KILLFOCUS: + _RosterItemEditEnd(hEditor, edat, FALSE); + return 0; + + case WM_DESTROY: + SetWindowLongPtr(hEditor, GWLP_USERDATA, (LONG_PTR)0); + free(edat); + return 0; + } + + return mir_callNextSubclass(hEditor, _RosterItemNewEditProc, msg, wParam, lParam); +} + +static LRESULT CALLBACK _RosterNewListProc(HWND hList, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_MOUSEWHEEL || msg == WM_NCLBUTTONDOWN || msg == WM_NCRBUTTONDOWN) + SetFocus(hList); + + if (msg == WM_LBUTTONDOWN) { + POINT pt; + GetCursorPos(&pt); + ScreenToClient(hList, &pt); + + LVHITTESTINFO lvhti = { 0 }; + lvhti.pt = pt; + ListView_SubItemHitTest(hList, &lvhti); + if (lvhti.flags&LVHT_ONITEM && lvhti.iSubItem != 0) { + RECT rc; + wchar_t buff[260]; + ListView_GetSubItemRect(hList, lvhti.iItem, lvhti.iSubItem, LVIR_BOUNDS, &rc); + ListView_GetItemText(hList, lvhti.iItem, lvhti.iSubItem, buff, _countof(buff)); + HWND hEditor = CreateWindow(TEXT("EDIT"), buff, WS_CHILD | ES_AUTOHSCROLL, rc.left + 3, rc.top + 2, rc.right - rc.left - 3, rc.bottom - rc.top - 3, hList, nullptr, g_plugin.getInst(), nullptr); + SendMessage(hEditor, WM_SETFONT, (WPARAM)SendMessage(hList, WM_GETFONT, 0, 0), 0); + ShowWindow(hEditor, SW_SHOW); + SetWindowText(hEditor, buff); + ClientToScreen(hList, &pt); + ScreenToClient(hEditor, &pt); + mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); + mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); + + ROSTEREDITDAT * edat = (ROSTEREDITDAT *)malloc(sizeof(ROSTEREDITDAT)); + mir_subclassWindow(hEditor, _RosterItemNewEditProc); + edat->hList = hList; + edat->index = lvhti.iItem; + edat->subindex = lvhti.iSubItem; + SetWindowLongPtr(hEditor, GWLP_USERDATA, (LONG_PTR)edat); + } + } + return mir_callNextSubclass(hList, _RosterNewListProc, msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// JabberRosterOptDlgProc - advanced options dialog procedure + +class CRosterEditorDlg : public CJabberDlgBase +{ + friend struct CJabberProto; + typedef CJabberDlgBase CSuper; + + uint8_t m_bRRAction; + BOOL m_bReadyToDownload = true; + BOOL m_bReadyToUpload = false; + + void _RosterSendRequest(uint8_t rrAction) + { + m_bRRAction = rrAction; + + m_proto->m_ThreadInfo->send( + XmlNodeIq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_GET)) + << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER)); + } + + int _RosterInsertListItem(const char *jid, const char *nick, const char *group, const char *subscr, bool bChecked) + { + Utf2T wszJid(jid); + LVITEM item = { 0 }; + item.mask = LVIF_TEXT | LVIF_STATE; + item.iItem = m_list.GetItemCount(); + item.pszText = wszJid; + + int index = m_list.InsertItem(&item); + if (index < 0) + return index; + + m_list.SetCheckState(index, bChecked); + + m_list.SetItemText(index, 1, Utf2T(nick)); + m_list.SetItemText(index, 2, Utf2T(group)); + m_list.SetItemText(index, 3, TranslateW(Utf2T(subscr))); + return index; + } + + void _RosterListClear() + { + m_list.DeleteAllItems(); + while (m_list.GetColumnWidth(0) > 0) + m_list.DeleteColumn(0); + + LV_COLUMN column = { 0 }; + column.mask = LVCF_TEXT; + + column.pszText = TranslateT("JID"); + m_list.InsertColumn(1, &column); + + column.pszText = TranslateT("Nickname"); + m_list.InsertColumn(2, &column); + + column.pszText = TranslateT("Group"); + m_list.InsertColumn(3, &column); + + column.pszText = TranslateT("Subscription"); + m_list.InsertColumn(4, &column); + + RECT rc; + GetClientRect(m_list.GetHwnd(), &rc); + ResizeColumns(rc.right - rc.left); + } + + void ResizeColumns(int width) + { + m_list.SetColumnWidth(0, width * 40 / 100); + m_list.SetColumnWidth(1, width * 25 / 100); + m_list.SetColumnWidth(2, width * 20 / 100); + m_list.SetColumnWidth(3, width * 12 / 100); + } + + void OnChangeStatus() + { + int count = m_list.GetItemCount(); + btnDownload.Enable(m_proto->m_bJabberOnline); + btnUpload.Enable(count && m_proto->m_bJabberOnline); + btnExport.Enable(count > 0); + } + + CCtrlButton btnDownload, btnUpload, btnExport, btnImport; + CCtrlListView m_list; + +public: + CRosterEditorDlg(CJabberProto *m_proto) : + CSuper(m_proto, IDD_ROSTER_EDITOR), + m_list(this, IDC_ROSTER), + btnExport(this, IDC_EXPORT), + btnImport(this, IDC_IMPORT), + btnUpload(this, IDC_UPLOAD), + btnDownload(this, IDC_DOWNLOAD) + { + SetMinSize(550, 390); + + btnExport.OnClick = Callback(this, &CRosterEditorDlg::onClick_Export); + btnImport.OnClick = Callback(this, &CRosterEditorDlg::onClick_Import); + btnUpload.OnClick = Callback(this, &CRosterEditorDlg::onClick_Upload); + btnDownload.OnClick = Callback(this, &CRosterEditorDlg::onClick_Download); + } + + bool OnInitDialog() override + { + SetWindowTextW(m_hwnd, CMStringW(FORMAT, L"%s: %s", TranslateT("Roster Editor"), m_proto->m_tszUserName)); + + Window_SetIcon_IcoLib(m_hwnd, g_plugin.getIconHandle(IDI_AGENTS)); + + Utils_RestoreWindowPosition(m_hwnd, 0, m_proto->m_szModuleName, "rosterCtrlWnd_"); + + m_list.SetExtendedListViewStyle(LVS_EX_CHECKBOXES | LVS_EX_BORDERSELECT | LVS_EX_GRIDLINES); + mir_subclassWindow(m_list.GetHwnd(), _RosterNewListProc); + _RosterListClear(); + OnChangeStatus(); + return true; + } + + void OnDestroy() override + { + m_proto->m_hwndRosterEditor = nullptr; + Utils_SaveWindowPosition(m_hwnd, 0, m_proto->m_szModuleName, "rosterCtrlWnd_"); + Window_FreeIcon_IcoLib(m_hwnd); + } + + int Resizer(UTILRESIZECONTROL *urc) override + { + switch (urc->wId) { + case IDC_HEADERBAR: + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH; + case IDC_ROSTER: + ResizeColumns(urc->rcItem.right - urc->rcItem.left + urc->dlgNewSize.cx - urc->dlgOriginalSize.cx); + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORY_HEIGHT | RD_ANCHORX_WIDTH; + case IDC_DOWNLOAD: + case IDC_UPLOAD: + return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; + case IDC_EXPORT: + case IDC_IMPORT: + return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; + } + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; + } + + void HandleNode(const TiXmlElement *node) + { + if (m_bRRAction == RRA_FILLLIST) { + _RosterListClear(); + auto *query = XmlFirstChild(node, "query"); + if (query == nullptr) return; + + for (auto *item : TiXmlFilter(query, "item")) { + const char *jid = XmlGetAttr(item, "jid"); + if (jid == nullptr) + continue; + + const char *name = XmlGetAttr(item, "name"); + const char *subscription = XmlGetAttr(item, "subscription"); + const char *group = XmlGetChildText(item, "group"); + _RosterInsertListItem(jid, name, group, subscription, true); + } + + // now it is require to process whole contact list to add not in roster contacts + for (auto &hContact : m_proto->AccContacts()) { + ptrW tszJid(m_proto->getWStringA(hContact, "jid")); + if (tszJid == nullptr) + continue; + + LVFINDINFO lvfi = { 0 }; + lvfi.flags = LVFI_STRING; + lvfi.psz = tszJid; + if (m_list.FindItem(-1, &lvfi) == -1) { + ptrA tszName(db_get_utfa(hContact, "CList", "MyHandle")); + ptrA tszGroup(db_get_utfa(hContact, "CList", "Group")); + _RosterInsertListItem(T2Utf(tszJid), tszName, tszGroup, nullptr, false); + } + } + m_bReadyToDownload = false; + m_bReadyToUpload = true; + btnDownload.SetText(TranslateT("Download")); + btnUpload.SetText(TranslateT("Upload")); + OnChangeStatus(); + return; + } + + if (m_bRRAction == RRA_SYNCROSTER) { + btnUpload.SetText(TranslateT("Uploading...")); + auto *queryRoster = XmlFirstChild(node, "query"); + if (!queryRoster) + return; + + int ListItemCount = m_list.GetItemCount(); + for (int index = 0; index < ListItemCount; index++) { + wchar_t jid[JABBER_MAX_JID_LEN] = L""; + wchar_t name[260]; + wchar_t group[260]; + wchar_t subscr[260]; + m_list.GetItemText(index, 0, jid, _countof(jid)); + m_list.GetItemText(index, 1, name, _countof(name)); + m_list.GetItemText(index, 2, group, _countof(group)); + m_list.GetItemText(index, 3, subscr, _countof(subscr)); + + T2Utf szJid(jid), szName(name), szGroup(group), szSubscr(subscr); + auto *itemRoster = XmlGetChildByTag(queryRoster, "item", "jid", szJid); + bool bRemove = !m_list.GetCheckState(index); + if (itemRoster && bRemove) { // delete item + XmlNodeIq iq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_SET)); + iq << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER) << XCHILD("item") << XATTR("jid", szJid) << XATTR("subscription", "remove"); + m_proto->m_ThreadInfo->send(iq); + } + else if (!bRemove) { + bool bPushed = itemRoster != 0; + const char *rosterName = 0, *rosterGroup = 0; + if (!bPushed) { + rosterName = XmlGetAttr(itemRoster, "name"); + if ((rosterName != nullptr || name[0] != 0) && mir_strcmpi(rosterName, szName)) + bPushed = true; + if (!bPushed) { + auto *szSub = XmlGetAttr(itemRoster, "subscription"); + if ((szSub != nullptr || subscr[0] != 0) && mir_strcmpi(szSub, szSubscr)) + bPushed = true; + } + if (!bPushed) { + rosterGroup = XmlGetChildText(itemRoster, "group"); + if (rosterGroup != nullptr && mir_strcmpi(rosterGroup, szGroup)) + bPushed = true; + } + } + if (bPushed) { + XmlNodeIq iq(m_proto->AddIQ(&CJabberProto::_RosterHandleGetRequest, JABBER_IQ_TYPE_SET)); + auto *item = iq << XCHILDNS("query", JABBER_FEAT_IQ_ROSTER) << XCHILD("item"); + if (rosterGroup || mir_strlen(szGroup)) + item << XCHILD("group", szGroup); + if (rosterName || mir_strlen(szName)) + item << XATTR("name", szName); + item << XATTR("jid", szJid) << XATTR("subscription", subscr[0] ? szSubscr : "none"); + m_proto->m_ThreadInfo->send(iq); + } + } + } + m_bRRAction = RRA_SYNCDONE; + _RosterSendRequest(RRA_FILLLIST); + return; + } + + btnUpload.SetText(TranslateT("Upload")); + onClick_Download(nullptr); + }; + + void onClick_Download(CCtrlButton*) + { + m_bReadyToUpload = m_bReadyToDownload = false; + OnChangeStatus(); + btnDownload.SetText(TranslateT("Downloading...")); + _RosterSendRequest(RRA_FILLLIST); + } + + void onClick_Upload(CCtrlButton*) + { + m_bReadyToUpload = false; + OnChangeStatus(); + btnUpload.SetText(TranslateT("Connecting...")); + _RosterSendRequest(RRA_SYNCROSTER); + } + + void onClick_Export(CCtrlButton*) + { + wchar_t filename[MAX_PATH] = { 0 }; + + wchar_t filter[MAX_PATH]; + mir_snwprintf(filter, L"%s (*.xml)%c*.xml%c%c", TranslateT("XML (UTF-8 encoded)"), 0, 0, 0); + OPENFILENAME ofn = {}; + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + ofn.hwndOwner = m_hwnd; + ofn.lpstrFilter = filter; + ofn.lpstrFile = filename; + ofn.Flags = OFN_HIDEREADONLY; + ofn.nMaxFile = _countof(filename); + ofn.nMaxFileTitle = MAX_PATH; + ofn.lpstrDefExt = L"xml"; + if (!GetSaveFileNameW(&ofn)) + return; + + FILE * fp = _wfopen(filename, L"wb"); + if (!fp) + return; + + int ListItemCount = m_list.GetItemCount(); + + XmlNode root("Roster"); + + for (int index = 0; index < ListItemCount; index++) { + wchar_t jid[JABBER_MAX_JID_LEN] = L""; + wchar_t name[260] = L""; + wchar_t group[260] = L""; + wchar_t subscr[260] = L""; + m_list.GetItemText(index, 0, jid, _countof(jid)); + m_list.GetItemText(index, 1, name, _countof(name)); + m_list.GetItemText(index, 2, group, _countof(group)); + m_list.GetItemText(index, 3, subscr, _countof(subscr)); + root << XCHILD("Item") << XATTR("jid", T2Utf(jid)) << XATTR("name", T2Utf(name)) << XATTR("group", T2Utf(group)) << XATTR("subscription", T2Utf(subscr)); + } + + tinyxml2::XMLPrinter printer(fp); + root.Print(&printer); + fclose(fp); + } + + void onClick_Import(CCtrlButton*) + { + wchar_t filename[MAX_PATH] = {}; + + OPENFILENAME ofn = { 0 }; + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + ofn.hwndOwner = m_hwnd; + ofn.hInstance = nullptr; + ofn.lpstrFilter = L"XML (UTF-8 encoded)(*.xml)\0*.xml\0\0"; + ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + ofn.lpstrFile = filename; + ofn.nMaxFile = _countof(filename); + ofn.nMaxFileTitle = MAX_PATH; + ofn.lpstrDefExt = L"xml"; + if (!GetOpenFileNameW(&ofn)) + return; + + FILE * fp = _wfopen(filename, L"rb"); + if (!fp) + return; + + TiXmlDocument doc; + int ret = doc.LoadFile(fp); + fclose(fp); + if (ret != 0) + return; + + _RosterListClear(); + + const TiXmlElement *Table = TiXmlConst(&doc)["Workbook"]["Worksheet"]["Table"].ToElement(); + if (Table) { + for (auto *Row : TiXmlFilter(Table, "Row")) { + bool bAdd = false; + const char *jid = nullptr; + const char *name = nullptr; + const char *group = nullptr; + const char *subscr = nullptr; + auto *Cell = XmlFirstChild(Row, "Cell"); + auto *Data = XmlFirstChild(Cell, "Data"); + if (Data) { + if (!mir_strcmpi(Data->GetText(), "+")) + bAdd = true; + else if (mir_strcmpi(Data->GetText(), "-")) + continue; + + Cell = Cell->NextSiblingElement("Cell"); + if (Cell) Data = XmlFirstChild(Cell, "Data"); + else Data = nullptr; + if (Data) { + jid = Data->GetText(); + if (!jid || mir_strlen(jid) == 0) + continue; + } + + Cell = Cell->NextSiblingElement("Cell"); + if (Cell) Data = XmlFirstChild(Cell, "Data"); + else Data = nullptr; + if (Data) name = Data->GetText(); + + Cell = Cell->NextSiblingElement("Cell"); + if (Cell) Data = XmlFirstChild(Cell, "Data"); + else Data = nullptr; + if (Data) group = Data->GetText(); + + Cell = Cell->NextSiblingElement("Cell"); + if (Cell) Data = XmlFirstChild(Cell, "Data"); + else Data = nullptr; + if (Data) subscr = Data->GetText(); + } + _RosterInsertListItem(jid, name, group, subscr, bAdd); + } + } + else if (Table = TiXmlConst(&doc)["Roster"].ToElement()) { + for (auto *Row : TiXmlFilter(Table, "Item")) { + auto *jid = Row->Attribute("jid"); + auto *name = Row->Attribute("name"); + auto *group = Row->Attribute("group"); + auto *subscr = Row->Attribute("subscription"); + _RosterInsertListItem(jid, name, group, subscr, true); + } + } + + OnChangeStatus(); + } +}; + +INT_PTR __cdecl CJabberProto::OnMenuHandleRosterControl(WPARAM, LPARAM) +{ + if (m_hwndRosterEditor) + SetForegroundWindow(m_hwndRosterEditor->GetHwnd()); + else { + m_hwndRosterEditor = new CRosterEditorDlg(this); + m_hwndRosterEditor->Show(); + } + + return 0; +} + +void CJabberProto::_RosterHandleGetRequest(const TiXmlElement *node, CJabberIqInfo*) +{ + if (m_hwndRosterEditor) + m_hwndRosterEditor->HandleNode(node); +} + +void CJabberProto::JabberUpdateDialogs(BOOL) +{ + if (m_hwndRosterEditor) + m_hwndRosterEditor->OnChangeStatus(); +} -- cgit v1.2.3