/* Jabber Protocol Plugin for Miranda NG Copyright (c) 2002-04 Santithorn Bunchua Copyright (c) 2005-12 George Hazan Copyright (c) 2007-09 Maxim Mluhov Copyright (c) 2007-09 Victor Pavlychko Copyright (C) 2012-21 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 "jabber_list.h" #include "jabber_iq.h" #include "jabber_caps.h" #include "jabber_privacy.h" #include "jabber_notes.h" CNoteItem::CNoteItem() { } CNoteItem::CNoteItem(const TiXmlElement *hXml, const char *szFrom) { SetData( XmlGetChildText(hXml, "title"), szFrom ? szFrom : XmlGetAttr(hXml, "from"), Utf2T(XmlGetChildText(hXml, "text")), XmlGetAttr(hXml, "tags")); } CNoteItem::~CNoteItem() { mir_free(m_szTitle); mir_free(m_szFrom); mir_free(m_szText); mir_free(m_szTags); mir_free(m_szTagsStr); } void CNoteItem::SetData(const char *title, const char *from, const wchar_t*text, const char *tags) { mir_free(m_szTitle); mir_free(m_szFrom); mir_free(m_szText); mir_free(m_szTags); mir_free(m_szTagsStr); m_szTitle = rtrim(mir_strdup(title)); m_szText = JabberStrFixLines(text); m_szFrom = rtrim(mir_strdup(from)); auto *szTags = tags; auto *p = m_szTags = (char *)mir_alloc(mir_strlen(szTags) + 2); auto *q = m_szTagsStr = (char *)mir_alloc(mir_strlen(szTags) + 1); for (; szTags && *szTags; ++szTags) { if (isspace(*szTags)) continue; if (*szTags == ',') { *q++ = ','; *p++ = 0; continue; } *q++ = *p++ = *szTags; } q[0] = p[0] = p[1] = 0; } bool CNoteItem::HasTag(const char *szTag) { if (!szTag || !*szTag) return true; for (auto *p = m_szTags; p && *p; p = p + mir_strlen(p) + 1) if (!mir_strcmp(p, szTag)) return true; return false; } int CNoteItem::cmp(const CNoteItem *p1, const CNoteItem *p2) { int ret = 0; if (ret = mir_strcmp(p1->m_szTitle, p2->m_szTitle)) return ret; if (ret = mir_wstrcmp(p1->m_szText, p2->m_szText)) return ret; if (ret = mir_strcmp(p1->m_szTagsStr, p2->m_szTagsStr)) return ret; if (p1 < p2) return -1; if (p1 > p2) return 1; return 0; } void CNoteList::LoadXml(const TiXmlElement *hXml) { destroy(); m_bIsModified = false; for (auto *it : TiXmlEnum(hXml)) { CNoteItem *pNote = new CNoteItem(it); if (pNote->IsNotEmpty()) insert(pNote); else delete pNote; } } void CNoteList::SaveXml(TiXmlElement *hXmlParent) { m_bIsModified = false; for (auto &it : *this) { TiXmlElement *hXmlItem = hXmlParent << XCHILD("note"); hXmlItem << XATTR("from", it->GetFrom()) << XATTR("tags", it->GetTagsStr()); hXmlItem << XCHILD("title", it->GetTitle()); hXmlItem << XCHILD("text", T2Utf(it->GetText())); } } ///////////////////////////////////////////////////////////////////////////////////////// // Single note editor class CJabberDlgNoteItem : public CJabberDlgBase { typedef CJabberDlgBase CSuper; typedef void (CJabberProto::*TFnProcessNote)(CNoteItem *, bool ok); CNoteItem *m_pNote; TFnProcessNote m_fnProcess; CCtrlEdit m_txtTitle; CCtrlEdit m_txtText; CCtrlEdit m_txtTags; public: CJabberDlgNoteItem(CJabberDlgBase *parent, CNoteItem *pNote) : CSuper(parent->GetProto(), IDD_NOTE_EDIT), m_pNote(pNote), m_fnProcess(nullptr), m_txtTitle(this, IDC_TXT_TITLE), m_txtText(this, IDC_TXT_TEXT), m_txtTags(this, IDC_TXT_TAGS) { SetParent(parent->GetHwnd()); } CJabberDlgNoteItem(CJabberProto *proto, CNoteItem *pNote, TFnProcessNote fnProcess) : CSuper(proto, IDD_NOTE_EDIT), m_pNote(pNote), m_fnProcess(fnProcess), m_txtTitle(this, IDC_TXT_TITLE), m_txtText(this, IDC_TXT_TEXT), m_txtTags(this, IDC_TXT_TAGS) { } bool OnInitDialog() override { CSuper::OnInitDialog(); Window_SetIcon_IcoLib(m_hwnd, g_plugin.getIconHandle(IDI_NOTES)); if (m_fnProcess) { CMStringW buf; if (m_fnProcess == &CJabberProto::ProcessIncomingNote) buf.Format(TranslateT("Incoming note from %s"), m_pNote->GetFrom()); else buf.Format(TranslateT("Send note to %s"), m_pNote->GetFrom()); SetWindowText(m_hwnd, buf); } m_txtTitle.SetText(Utf2T(m_pNote->GetTitle())); m_txtText.SetText(m_pNote->GetText()); m_txtTags.SetText(Utf2T(m_pNote->GetTagsStr())); return true; } bool OnApply() override { T2Utf szTitle(ptrW(m_txtTitle.GetText())); T2Utf szTags(ptrW(m_txtTags.GetText())); m_pNote->SetData(szTitle, m_pNote->GetFrom(), ptrW(m_txtText.GetText()), szTags); m_autoClose = false; if (m_fnProcess) (m_proto->*m_fnProcess)(m_pNote, true); return true; } int Resizer(UTILRESIZECONTROL *urc) override { switch (urc->wId) { case IDC_TXT_TITLE: return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP; case IDC_TXT_TEXT: return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; case IDC_ST_TAGS: case IDC_TXT_TAGS: return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; case IDOK: case IDCANCEL: return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; } return CSuper::Resizer(urc); } void OnDestroy() override { if (m_fnProcess) (m_proto->*m_fnProcess)(m_pNote, false); } }; ///////////////////////////////////////////////////////////////////////////////////////// // Notebook window class CCtrlNotebookList : public CCtrlListBox { typedef CCtrlListBox CSuper; bool m_adding; HFONT m_hfntNormal, m_hfntSmall, m_hfntBold; public: CCtrlNotebookList(CDlgBase* dlg, int ctrlId) : CCtrlListBox(dlg, ctrlId), m_adding(false) { m_hfntNormal = m_hfntSmall = m_hfntBold = nullptr; } void SetFonts(HFONT hfntNormal, HFONT hfntSmall, HFONT hfntBold) { m_hfntNormal = hfntNormal; m_hfntSmall = hfntSmall; m_hfntBold = hfntBold; } int AddString(wchar_t *text, LPARAM data = 0) { m_adding = true; int idx = CCtrlListBox::AddString(text, data); m_adding = false; if (idx == LB_ERR) return idx; MEASUREITEMSTRUCT mis = { 0 }; mis.CtlType = ODT_LISTBOX; mis.CtlID = m_idCtrl; mis.itemID = idx; mis.itemData = data; OnMeasureItem(&mis); if (mis.itemHeight) SendMessage(m_hwnd, LB_SETITEMHEIGHT, idx, mis.itemHeight); return idx; } void OnInit() { CSuper::OnInit(); Subclass(); } LRESULT CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) { if (msg == WM_SIZE) { SendMessage(m_hwnd, WM_SETREDRAW, FALSE, 0); int cnt = GetCount(); for (int idx = 0; idx < cnt; ++idx) { MEASUREITEMSTRUCT mis = { 0 }; mis.CtlType = ODT_LISTBOX; mis.CtlID = m_idCtrl; mis.itemID = idx; mis.itemData = GetItemData(idx); OnMeasureItem(&mis); if (mis.itemHeight) SendMessage(m_hwnd, LB_SETITEMHEIGHT, idx, mis.itemHeight); } SendMessage(m_hwnd, WM_SETREDRAW, TRUE, 0); RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE); } return CSuper::CustomWndProc(msg, wParam, lParam); } BOOL OnDrawItem(DRAWITEMSTRUCT *lps) { if (m_adding) return FALSE; if (lps->itemID == -1) return TRUE; if (!lps->itemData) return TRUE; HDC hdc = lps->hDC; CNoteItem *pNote = (CNoteItem *)lps->itemData; SetBkMode(hdc, TRANSPARENT); if (lps->itemState & ODS_SELECTED) { FillRect(hdc, &lps->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT)); SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); } else { FillRect(hdc, &lps->rcItem, GetSysColorBrush(COLOR_WINDOW)); SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT)); } if (lps->itemID) { RECT rcTmp = lps->rcItem; rcTmp.bottom = rcTmp.top + 1; FillRect(hdc, &rcTmp, GetSysColorBrush(COLOR_BTNSHADOW)); } RECT rc = lps->rcItem; rc.left += 5; rc.right -= 5; rc.top += 2; SelectObject(hdc, m_hfntBold); rc.top += DrawText(hdc, Utf2T(pNote->GetTitle()), -1, &rc, DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS); SelectObject(hdc, m_hfntNormal); if (pNote->GetFrom()) { wchar_t buf[256]; mir_snwprintf(buf, TranslateT("From: %s"), pNote->GetFrom()); rc.top += DrawText(hdc, buf, -1, &rc, DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS); } rc.top += DrawText(hdc, pNote->GetText(), -1, &rc, DT_NOPREFIX | DT_WORDBREAK | DT_EXPANDTABS | DT_END_ELLIPSIS); SelectObject(hdc, m_hfntSmall); rc.top += DrawText(hdc, Utf2T(pNote->GetTagsStr()), -1, &rc, DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS); rc.top += 5; int h = min(255, max(0, rc.bottom - rc.top)); if (SendMessage(m_hwnd, LB_GETITEMHEIGHT, lps->itemID, 0) != h) SendMessage(m_hwnd, LB_SETITEMHEIGHT, lps->itemID, h); return TRUE; } BOOL OnMeasureItem(MEASUREITEMSTRUCT *lps) { if (m_adding) return FALSE; if (lps->itemID == -1) return TRUE; if (!lps->itemData) return TRUE; HDC hdc = GetDC(m_hwnd); CNoteItem *pNote = (CNoteItem *)lps->itemData; RECT rcTmp, rc; GetClientRect(m_hwnd, &rc); rc.bottom = 0; SelectObject(hdc, m_hfntBold); rcTmp = rc; DrawText(hdc, Utf2T(pNote->GetTitle()), -1, &rcTmp, DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS | DT_CALCRECT); lps->itemHeight += rcTmp.bottom; SelectObject(hdc, m_hfntNormal); if (pNote->GetFrom()) { wchar_t buf[256]; mir_snwprintf(buf, TranslateT("From: %s"), pNote->GetFrom()); rcTmp = rc; DrawText(hdc, buf, -1, &rcTmp, DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS | DT_CALCRECT); lps->itemHeight += rcTmp.bottom; } rcTmp = rc; DrawText(hdc, pNote->GetText(), -1, &rcTmp, DT_NOPREFIX | DT_WORDBREAK | DT_EXPANDTABS | DT_END_ELLIPSIS | DT_CALCRECT); lps->itemHeight += rcTmp.bottom; SelectObject(hdc, m_hfntSmall); rcTmp = rc; DrawText(hdc, Utf2T(pNote->GetTagsStr()), -1, &rcTmp, DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS | DT_CALCRECT); lps->itemHeight += rcTmp.bottom; lps->itemHeight += 5; ReleaseDC(m_hwnd, hdc); lps->itemWidth = rc.right; lps->itemHeight = min(255, lps->itemHeight); // listbox can't make items taller then 255px return TRUE; } }; class CJabberDlgNotes : public CJabberDlgBase { typedef CJabberDlgBase CSuper; CCtrlMButton m_btnAdd; CCtrlMButton m_btnEdit; CCtrlMButton m_btnRemove; CCtrlNotebookList m_lstNotes; CCtrlTreeView m_tvFilter; CCtrlButton m_btnSave; HFONT m_hfntNormal, m_hfntSmall, m_hfntBold; void EnableControls() { m_btnSave.Enable(m_proto->m_bJabberOnline && m_proto->m_notes.IsModified()); m_btnEdit.Enable(m_lstNotes.GetCurSel() != LB_ERR); m_btnRemove.Enable(m_lstNotes.GetCurSel() != LB_ERR); } void InsertTag(HTREEITEM htiRoot, const char *tag, bool bSelect) { Utf2T wszTag(tag); TVINSERTSTRUCT tvi = {}; tvi.hParent = htiRoot; tvi.hInsertAfter = TVI_LAST; tvi.itemex.mask = TVIF_TEXT | TVIF_PARAM; tvi.itemex.pszText = wszTag; tvi.itemex.lParam = (LPARAM)mir_strdup(tag); HTREEITEM hti = m_tvFilter.InsertItem(&tvi); if (bSelect) m_tvFilter.SelectItem(hti); } void PopulateTags(HTREEITEM htiRoot, const char *szActiveTag) { LIST tagSet(5, strcmp); for (auto &it : m_proto->m_notes) { char *tags = it->GetTags(); for (auto *tag = tags; tag && *tag; tag = tag + mir_strlen(tag) + 1) if (!tagSet.find(tag)) tagSet.insert(tag); } bool selected = false; for (auto &it : tagSet) { bool select = !mir_strcmp(szActiveTag, it); selected |= select; InsertTag(htiRoot, it, select); } if (!selected) m_tvFilter.SelectItem(htiRoot); } void RebuildTree() { TVITEMEX tvi = { 0 }; tvi.mask = TVIF_HANDLE | TVIF_PARAM; tvi.hItem = m_tvFilter.GetSelection(); m_tvFilter.GetItem(&tvi); char *szActiveTag = mir_strdup((char*)tvi.lParam); m_tvFilter.DeleteAllItems(); TVINSERTSTRUCT tvis = {}; tvis.hInsertAfter = TVI_LAST; tvis.itemex.mask = TVIF_TEXT | TVIF_PARAM | TVIF_STATE; tvis.itemex.stateMask = tvis.itemex.state = TVIS_BOLD | TVIS_EXPANDED; tvis.itemex.pszText = TranslateT("All tags"); PopulateTags(m_tvFilter.InsertItem(&tvis), szActiveTag); mir_free(szActiveTag); } void InsertItem(CNoteItem &item) { m_lstNotes.AddString((wchar_t *)item.GetTitle(), (LPARAM)&item); EnableControls(); } void ListItems(const char *tag) { m_lstNotes.ResetContent(); for (auto &it : m_proto->m_notes) if (it->HasTag(tag)) InsertItem(*it); EnableControls(); } public: CJabberDlgNotes(CJabberProto *proto) : CSuper(proto, IDD_NOTEBOOK), m_btnAdd(this, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add")), m_btnEdit(this, IDC_EDIT, SKINICON_OTHER_RENAME, LPGEN("Edit")), m_btnRemove(this, IDC_REMOVE, SKINICON_OTHER_DELETE, LPGEN("Remove")), m_lstNotes(this, IDC_LST_NOTES), m_tvFilter(this, IDC_TV_FILTER), m_btnSave(this, IDC_APPLY) { m_btnAdd.OnClick = Callback(this, &CJabberDlgNotes::btnAdd_OnClick); m_btnEdit.OnClick = Callback(this, &CJabberDlgNotes::btnEdit_OnClick); m_btnRemove.OnClick = Callback(this, &CJabberDlgNotes::btnRemove_OnClick); m_lstNotes.OnDblClick = Callback(this, &CJabberDlgNotes::btnEdit_OnClick); m_lstNotes.OnSelChange = Callback(this, &CJabberDlgNotes::lstNotes_OnSelChange); m_btnSave.OnClick = Callback(this, &CJabberDlgNotes::btnSave_OnClick); m_tvFilter.OnSelChanged = Callback(this, &CJabberDlgNotes::tvFilter_OnSelChanged); m_tvFilter.OnDeleteItem = Callback(this, &CJabberDlgNotes::tvFilter_OnDeleteItem); } bool OnInitDialog() override { CSuper::OnInitDialog(); Window_SetIcon_IcoLib(m_hwnd, g_plugin.getIconHandle(IDI_NOTES)); LOGFONT lf, lfTmp; m_hfntNormal = (HFONT)GetStockObject(DEFAULT_GUI_FONT); GetObject(m_hfntNormal, sizeof(lf), &lf); lfTmp = lf; lfTmp.lfWeight = FW_BOLD; m_hfntBold = CreateFontIndirect(&lfTmp); lfTmp = lf; lfTmp.lfHeight *= 0.8; m_hfntSmall = CreateFontIndirect(&lfTmp); m_lstNotes.SetFonts(m_hfntNormal, m_hfntSmall, m_hfntBold); Utils_RestoreWindowPosition(m_hwnd, 0, m_proto->m_szModuleName, "notesWnd_"); return true; } bool OnClose() override { if (m_proto->m_notes.IsModified()) if (IDYES != MessageBox(m_hwnd, TranslateT("Notes are not saved, close this window without uploading data to server?"), TranslateT("Are you sure?"), MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2)) return false; return true; } void OnDestroy() override { Utils_SaveWindowPosition(m_hwnd, 0, m_proto->m_szModuleName, "notesWnd_"); DeleteObject(m_hfntSmall); DeleteObject(m_hfntBold); m_tvFilter.DeleteAllItems(); m_proto->m_pDlgNotes = nullptr; CSuper::OnDestroy(); } int Resizer(UTILRESIZECONTROL *urc) override { switch (urc->wId) { case IDC_TV_FILTER: return RD_ANCHORX_LEFT | RD_ANCHORY_HEIGHT; case IDC_LST_NOTES: return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; case IDC_APPLY: case IDCANCEL: return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; case IDC_ADD: case IDC_EDIT: case IDC_REMOVE: return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; } return CSuper::Resizer(urc); } void OnProtoCheckOnline(WPARAM, LPARAM) override { EnableControls(); } void btnAdd_OnClick(CCtrlButton*) { CNoteItem *pNote = new CNoteItem(); CJabberDlgNoteItem dlg(this, pNote); dlg.DoModal(); if (pNote->IsNotEmpty()) { m_proto->m_notes.insert(pNote); m_proto->m_notes.Modify(); UpdateData(); } else { delete pNote; return; } EnableControls(); } void btnEdit_OnClick(CCtrlButton*) { int idx = m_lstNotes.GetCurSel(); if (idx != LB_ERR) { if (CNoteItem *pItem = (CNoteItem *)m_lstNotes.GetItemData(idx)) { CJabberDlgNoteItem dlg(this, pItem); if (dlg.DoModal()) { m_proto->m_notes.Modify(); RebuildTree(); } } } EnableControls(); } void btnRemove_OnClick(CCtrlButton*) { int idx = m_lstNotes.GetCurSel(); if (idx != LB_ERR) { if (CNoteItem *pItem = (CNoteItem *)m_lstNotes.GetItemData(idx)) { m_lstNotes.DeleteString(idx); m_proto->m_notes.remove(pItem); } RebuildTree(); } EnableControls(); } void lstNotes_OnSelChange(CCtrlListBox *) { EnableControls(); } void tvFilter_OnDeleteItem(CCtrlTreeView::TEventInfo *e) { char *szText = (char*)e->nmtv->itemNew.lParam; mir_free(szText); EnableControls(); } void tvFilter_OnSelChanged(CCtrlTreeView::TEventInfo *e) { char *szText = (char*)e->nmtv->itemNew.lParam; ListItems(szText); EnableControls(); } void btnSave_OnClick(CCtrlButton*) { XmlNodeIq iq("set"); TiXmlElement *query = iq << XQUERY(JABBER_FEAT_PRIVATE_STORAGE); TiXmlElement *storage = query << XCHILDNS("storage", JABBER_FEAT_MIRANDA_NOTES); m_proto->m_notes.SaveXml(storage); m_proto->m_ThreadInfo->send(iq); EnableControls(); } void UpdateData() { RebuildTree(); EnableControls(); } }; ///////////////////////////////////////////////////////////////////////////////////////// // Launches the incoming note window void CJabberProto::ProcessIncomingNote(CNoteItem *pNote, bool ok) { if (ok && pNote->IsNotEmpty()) { m_notes.insert(pNote); XmlNodeIq iq("set"); TiXmlElement *query = iq << XQUERY(JABBER_FEAT_PRIVATE_STORAGE); TiXmlElement *storage = query << XCHILDNS("storage", JABBER_FEAT_MIRANDA_NOTES); m_notes.SaveXml(storage); m_ThreadInfo->send(iq); } else delete pNote; } void CJabberProto::ProcessOutgoingNote(CNoteItem *pNote, bool ok) { if (!ok || !pNote->IsNotEmpty()) { delete pNote; return; } char buf[1024]; mir_snprintf(buf, "Incoming note: %s\n\n%s\nTags: %s", pNote->GetTitle(), pNote->GetText(), pNote->GetTagsStr()); JabberCapsBits jcb = GetResourceCapabilities(pNote->GetFrom()); if (jcb & JABBER_RESOURCE_CAPS_ERROR) jcb = JABBER_RESOURCE_CAPS_NONE; int nMsgId = SerialNext(); XmlNode m("message"); m << XATTR("type", "chat") << XATTR("to", pNote->GetFrom()) << XATTRID(nMsgId); m << XCHILD("body", buf); TiXmlElement *hXmlItem = m << XCHILDNS("x", JABBER_FEAT_MIRANDA_NOTES) << XCHILD("note"); hXmlItem << XATTR("tags", pNote->GetTagsStr()); hXmlItem << XCHILD("title", pNote->GetTitle()); hXmlItem << XCHILD("text", T2Utf(pNote->GetText())); // message receipts XEP priority if (jcb & JABBER_CAPS_MESSAGE_RECEIPTS) m << XCHILDNS("request", JABBER_FEAT_MESSAGE_RECEIPTS); else nMsgId = -1; m_ThreadInfo->send(m); delete pNote; } bool CJabberProto::OnIncomingNote(const char *szFrom, const TiXmlElement *hXml) { if (!m_bAcceptNotes) return false; if (!szFrom || !hXml) return true; CNoteItem *pItem = new CNoteItem(hXml, szFrom); if (!pItem->IsNotEmpty()) { delete pItem; return true; } if (m_bAutosaveNotes && HContactFromJID(szFrom)) { ProcessIncomingNote(pItem, true); return false; } char szService[256]; mir_snprintf(szService, "%s%s", m_szModuleName, JS_INCOMING_NOTE_EVENT); CLISTEVENT cle = {}; cle.hIcon = g_plugin.getIcon(IDI_NOTES); cle.flags = CLEF_PROTOCOLGLOBAL | CLEF_UNICODE; cle.hDbEvent = -99; cle.lParam = (LPARAM)pItem; cle.pszService = szService; cle.szTooltip.w = TranslateT("Incoming note"); g_clistApi.pfnAddEvent(&cle); return true; } INT_PTR __cdecl CJabberProto::OnIncomingNoteEvent(WPARAM, LPARAM lParam) { CLISTEVENT *pCle = (CLISTEVENT *)lParam; CNoteItem *pNote = (CNoteItem *)pCle->lParam; if (!pNote) return 0; CJabberDlgBase *pDlg = new CJabberDlgNoteItem(this, pNote, &CJabberProto::ProcessIncomingNote); pDlg->Show(); return 0; } ///////////////////////////////////////////////////////////////////////////////////////// // Menu handling INT_PTR __cdecl CJabberProto::OnMenuHandleNotes(WPARAM, LPARAM) { UI_SAFE_OPEN_EX(CJabberDlgNotes, m_pDlgNotes, pDlg); pDlg->UpdateData(); return 0; } INT_PTR __cdecl CJabberProto::OnMenuSendNote(WPARAM wParam, LPARAM) { if (wParam) { CNoteItem *pItem = new CNoteItem(nullptr, ptrA(getUStringA(wParam, "jid"))); CJabberDlgBase *pDlg = new CJabberDlgNoteItem(this, pItem, &CJabberProto::ProcessOutgoingNote); pDlg->Show(); } return 0; }