/* 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-14 Miranda NG project 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 "jabber.h" #include "jabber_list.h" #include "jabber_iq.h" #include "jabber_caps.h" #include "jabber_privacy.h" #include "jabber_notes.h" static TCHAR *StrTrimCopy(TCHAR *str) { if (!str) return 0; while (*str && _istspace(*str)) ++str; if (!*str) return mir_tstrdup(str); TCHAR *res = mir_tstrdup(str); for (TCHAR *p = res + lstrlen(res) - 1; p >= res; --p) { if (_istspace(*p)) *p = 0; else break; } return res; } CNoteItem::CNoteItem() { m_szTitle = m_szFrom = m_szText = m_szTags = m_szTagsStr = NULL; } CNoteItem::CNoteItem(HXML hXml, TCHAR *szFrom) { m_szTitle = m_szFrom = m_szText = m_szTags = m_szTagsStr = NULL; SetData( XPathT(hXml, "title"), szFrom ? szFrom : XPathT(hXml, "@from"), XPathT(hXml, "text"), XPathT(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(TCHAR *title, TCHAR *from, TCHAR *text, TCHAR *tags) { mir_free(m_szTitle); mir_free(m_szFrom); mir_free(m_szText); mir_free(m_szTags); mir_free(m_szTagsStr); m_szTitle = StrTrimCopy(title); m_szText = JabberStrFixLines(text); m_szFrom = StrTrimCopy(from); const TCHAR *szTags = tags; TCHAR *p = m_szTags = (TCHAR *)mir_alloc((lstrlen(szTags) + 2 /*for double zero*/) * sizeof(TCHAR)); TCHAR *q = m_szTagsStr = (TCHAR *)mir_alloc((lstrlen(szTags) + 1) * sizeof(TCHAR)); for (; szTags && *szTags; ++szTags) { if (_istspace(*szTags)) continue; if (*szTags == _T(',')) { *q++ = _T(','); *p++ = 0; continue; } *q++ = *p++ = *szTags; } q[0] = p[0] = p[1] = 0; } bool CNoteItem::HasTag(const TCHAR *szTag) { if (!szTag || !*szTag) return true; for (TCHAR *p = m_szTags; p && *p; p = p + lstrlen(p) + 1) if (!lstrcmp(p, szTag)) return true; return false; } int CNoteItem::cmp(const CNoteItem *p1, const CNoteItem *p2) { int ret = 0; if (ret = lstrcmp(p1->m_szTitle, p2->m_szTitle)) return ret; if (ret = lstrcmp(p1->m_szText, p2->m_szText)) return ret; if (ret = lstrcmp(p1->m_szTagsStr, p2->m_szTagsStr)) return ret; if (p1 < p2) return -1; if (p1 > p2) return 1; return 0; } void CNoteList::AddNote(HXML hXml, TCHAR *szFrom) { m_bIsModified = true; insert(new CNoteItem(hXml, szFrom)); } void CNoteList::LoadXml(HXML hXml) { destroy(); m_bIsModified = false; int count = xmlGetChildCount(hXml); for (int i=0; i < count; i++) { CNoteItem *pNote = new CNoteItem(xi.getChild(hXml, i)); if (pNote->IsNotEmpty()) insert(pNote); else delete pNote; } } void CNoteList::SaveXml(HXML hXmlParent) { m_bIsModified = false; CNoteList &me = *this; for (int i=0; i < getCount(); i++) { HXML hXmlItem = hXmlParent << XCHILD(_T("note")); hXmlItem << XATTR(_T("from"), me[i].GetFrom()) << XATTR(_T("tags"), me[i].GetTagsStr()); hXmlItem << XCHILD(_T("title"), me[i].GetTitle()); hXmlItem << XCHILD(_T("text"), me[i].GetText()); } } ///////////////////////////////////////////////////////////////////////////////////////// // Single note editor class CJabberDlgNoteItem : public CJabberDlgBase { typedef CJabberDlgBase CSuper; typedef void (CJabberProto::*TFnProcessNote)(CNoteItem *, bool ok); public: CJabberDlgNoteItem(CJabberDlgBase *parent, CNoteItem *pNote); CJabberDlgNoteItem(CJabberProto *proto, CNoteItem *pNote, TFnProcessNote fnProcess); protected: void OnInitDialog(); int Resizer(UTILRESIZECONTROL *urc); private: CNoteItem *m_pNote; TFnProcessNote m_fnProcess; CCtrlEdit m_txtTitle; CCtrlEdit m_txtText; CCtrlEdit m_txtTags; CCtrlButton m_btnOk; void btnOk_OnClick(CCtrlButton *) { TCHAR *szTitle = m_txtTitle.GetText(); TCHAR *szText = m_txtText.GetText(); TCHAR *szTags = m_txtTags.GetText(); TCHAR *szFrom = mir_tstrdup(m_pNote->GetFrom()); m_pNote->SetData(szTitle, szFrom, szText, szTags); mir_free(szTitle); mir_free(szText); mir_free(szTags); mir_free(szFrom); m_autoClose = false; if (m_fnProcess) (m_proto->*m_fnProcess)(m_pNote, true); EndDialog(m_hwnd, TRUE); } void OnClose() { if (m_fnProcess) (m_proto->*m_fnProcess)(m_pNote, false); CSuper::OnClose(); } }; CJabberDlgNoteItem::CJabberDlgNoteItem(CJabberDlgBase *parent, CNoteItem *pNote): CSuper(parent->GetProto(), IDD_NOTE_EDIT, parent->GetHwnd()), m_pNote(pNote), m_fnProcess(NULL), m_txtTitle(this, IDC_TXT_TITLE), m_txtText(this, IDC_TXT_TEXT), m_txtTags(this, IDC_TXT_TAGS), m_btnOk(this, IDOK) { m_btnOk.OnClick = Callback(this, &CJabberDlgNoteItem::btnOk_OnClick); } CJabberDlgNoteItem::CJabberDlgNoteItem(CJabberProto *proto, CNoteItem *pNote, TFnProcessNote fnProcess): CSuper(proto, IDD_NOTE_EDIT, NULL), m_pNote(pNote), m_fnProcess(fnProcess), m_txtTitle(this, IDC_TXT_TITLE), m_txtText(this, IDC_TXT_TEXT), m_txtTags(this, IDC_TXT_TAGS), m_btnOk(this, IDOK) { m_btnOk.OnClick = Callback(this, &CJabberDlgNoteItem::btnOk_OnClick); } void CJabberDlgNoteItem::OnInitDialog() { CSuper::OnInitDialog(); WindowSetIcon(m_hwnd, m_proto, "notes"); if (m_fnProcess) { CMString 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(m_pNote->GetTitle()); m_txtText.SetText(m_pNote->GetText()); m_txtTags.SetText(m_pNote->GetTagsStr()); } int CJabberDlgNoteItem::Resizer(UTILRESIZECONTROL *urc) { 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); } ///////////////////////////////////////////////////////////////////////////////////////// // 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) {} void SetFonts(HFONT hfntNormal, HFONT hfntSmall, HFONT hfntBold) { m_hfntNormal = hfntNormal; m_hfntSmall = hfntSmall; m_hfntBold = hfntBold; } int AddString(TCHAR *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, NULL, NULL, 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, pNote->GetTitle(), -1, &rc, DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS); SelectObject(hdc, m_hfntNormal); if (pNote->GetFrom()) { TCHAR buf[256]; mir_sntprintf(buf, SIZEOF(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, 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); int maxHeight = rc.bottom - 10; rc.bottom = 0; SelectObject(hdc, m_hfntBold); rcTmp = rc; DrawText(hdc, pNote->GetTitle(), -1, &rcTmp, DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS | DT_CALCRECT); lps->itemHeight += rcTmp.bottom; SelectObject(hdc, m_hfntNormal); if (pNote->GetFrom()) { TCHAR buf[256]; mir_sntprintf(buf, SIZEOF(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, 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; public: CJabberDlgNotes(CJabberProto *proto); void UpdateData(); protected: void OnInitDialog(); void OnClose(); void OnDestroy(); int Resizer(UTILRESIZECONTROL *urc); void OnProtoCheckOnline(WPARAM wParam, LPARAM lParam); void OnProtoRefresh(WPARAM wParam, LPARAM lParam); private: 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 TCHAR *tag, bool bSelect) { TVINSERTSTRUCT tvi = { 0 }; tvi.hParent = htiRoot; tvi.hInsertAfter = TVI_LAST; tvi.itemex.mask = TVIF_TEXT | TVIF_PARAM; tvi.itemex.pszText = (TCHAR *)tag; tvi.itemex.lParam = (LPARAM)mir_tstrdup(tag); HTREEITEM hti = m_tvFilter.InsertItem(&tvi); if (bSelect) m_tvFilter.SelectItem(hti); } void PopulateTags(HTREEITEM htiRoot, TCHAR *szActiveTag) { LIST tagSet(5, _tcscmp); for (int i = 0; i < m_proto->m_notes.getCount(); i++) { TCHAR *tags = m_proto->m_notes[i].GetTags(); for (TCHAR *tag = tags; tag && *tag; tag = tag + lstrlen(tag) + 1) if (!tagSet.find(tag)) tagSet.insert(tag); } bool selected = false; for (int j = 0; j < tagSet.getCount(); ++j) { bool select = !lstrcmp(szActiveTag, tagSet[j]); selected |= select; InsertTag(htiRoot, tagSet[j], 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); TCHAR *szActiveTag = mir_tstrdup((TCHAR *)tvi.lParam); m_tvFilter.DeleteAllItems(); TVINSERTSTRUCT tvis = { 0 }; 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((TCHAR *)item.GetTitle(), (LPARAM)&item); EnableControls(); } void ListItems(const TCHAR *tag) { m_lstNotes.ResetContent(); for (int i=0; i < m_proto->m_notes.getCount(); i++) if (m_proto->m_notes[i].HasTag(tag)) InsertItem(m_proto->m_notes[i]); EnableControls(); } void btnAdd_OnClick(CCtrlFilterListView *) { 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(CCtrlFilterListView *) { 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(CCtrlFilterListView *) { 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) { TCHAR *szText = (TCHAR *)e->nmtv->itemOld.lParam; mir_free(szText); EnableControls(); } void tvFilter_OnSelChanged(CCtrlTreeView::TEventInfo *e) { TCHAR *szText = (TCHAR *)e->nmtv->itemNew.lParam; ListItems(szText); EnableControls(); } void btnSave_OnClick(CCtrlButton *) { XmlNodeIq iq(_T("set")); HXML query = iq << XQUERY(JABBER_FEAT_PRIVATE_STORAGE); HXML storage = query << XCHILDNS(_T("storage"), JABBER_FEAT_MIRANDA_NOTES); m_proto->m_notes.SaveXml(storage); m_proto->m_ThreadInfo->send(iq); EnableControls(); } }; CJabberDlgNotes::CJabberDlgNotes(CJabberProto *proto) : CSuper(proto, IDD_NOTEBOOK, NULL), 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); } void CJabberDlgNotes::UpdateData() { RebuildTree(); //ListItems(NULL); EnableControls(); } void CJabberDlgNotes::OnInitDialog() { CSuper::OnInitDialog(); WindowSetIcon(m_hwnd, m_proto, "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, NULL, m_proto->m_szModuleName, "notesWnd_"); } void CJabberDlgNotes::OnClose() { 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)) { m_lresult = TRUE; return; } } Utils_SaveWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "notesWnd_"); DeleteObject(m_hfntSmall); DeleteObject(m_hfntBold); CSuper::OnClose(); } void CJabberDlgNotes::OnDestroy() { m_tvFilter.DeleteAllItems(); m_proto->m_pDlgNotes = NULL; CSuper::OnDestroy(); } void CJabberDlgNotes::OnProtoCheckOnline(WPARAM, LPARAM) { EnableControls(); } void CJabberDlgNotes::OnProtoRefresh(WPARAM, LPARAM) { } int CJabberDlgNotes::Resizer(UTILRESIZECONTROL *urc) { 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); } ///////////////////////////////////////////////////////////////////////////////////////// // Launches the incoming note window void CJabberProto::ProcessIncomingNote(CNoteItem *pNote, bool ok) { if (ok && pNote->IsNotEmpty()) { m_notes.insert(pNote); XmlNodeIq iq(_T("set")); HXML query = iq << XQUERY(JABBER_FEAT_PRIVATE_STORAGE); HXML storage = query << XCHILDNS(_T("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; } TCHAR buf[1024]; mir_sntprintf(buf, SIZEOF(buf), _T("Incoming note: %s\n\n%s\nTags: %s"), pNote->GetTitle(), pNote->GetText(), pNote->GetTagsStr()); JabberCapsBits jcb = GetResourceCapabilites(pNote->GetFrom(), TRUE); if (jcb & JABBER_RESOURCE_CAPS_ERROR) jcb = JABBER_RESOURCE_CAPS_NONE; int nMsgId = SerialNext(); XmlNode m(_T("message")); m << XATTR(_T("type"), _T("chat")) << XATTR(_T("to"), pNote->GetFrom()) << XATTRID(nMsgId); m << XCHILD(_T("body"), buf); HXML hXmlItem = m << XCHILDNS(_T("x"), JABBER_FEAT_MIRANDA_NOTES) << XCHILD(_T("note")); hXmlItem << XATTR(_T("tags"), pNote->GetTagsStr()); hXmlItem << XCHILD(_T("title"), pNote->GetTitle()); hXmlItem << XCHILD(_T("text"), pNote->GetText()); // message receipts XEP priority if (jcb & JABBER_CAPS_MESSAGE_RECEIPTS) m << XCHILDNS(_T("request"), JABBER_FEAT_MESSAGE_RECEIPTS); else if (jcb & JABBER_CAPS_MESSAGE_EVENTS) { HXML x = m << XCHILDNS(_T("x"), JABBER_FEAT_MESSAGE_EVENTS); x << XCHILD(_T("delivered")); x << XCHILD(_T("offline")); } else nMsgId = -1; m_ThreadInfo->send(m); delete pNote; } bool CJabberProto::OnIncomingNote(const TCHAR *szFrom, HXML hXml) { if (!m_options.AcceptNotes) return false; if (!szFrom || !hXml) return true; CNoteItem *pItem = new CNoteItem(hXml, (TCHAR *)szFrom); if (!pItem->IsNotEmpty()) { delete pItem; return true; } if (m_options.AutosaveNotes && HContactFromJID(szFrom)) { ProcessIncomingNote(pItem, true); return false; } CLISTEVENT cle = { 0 }; char szService[256]; mir_snprintf(szService, sizeof(szService), "%s%s", m_szModuleName, JS_INCOMING_NOTE_EVENT); cle.cbSize = sizeof(CLISTEVENT); cle.hIcon = (HICON)LoadIconEx("notes"); cle.flags = CLEF_PROTOCOLGLOBAL | CLEF_TCHAR; cle.hDbEvent = (HANDLE)("test"); cle.lParam = (LPARAM)pItem; cle.pszService = szService; cle.ptszTooltip = TranslateT("Incoming note"); CallService(MS_CLIST_ADDEVENT, 0, (LPARAM)&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(NULL, ptrT(getTStringA(wParam, "jid"))); CJabberDlgBase *pDlg = new CJabberDlgNoteItem(this, pItem, &CJabberProto::ProcessOutgoingNote); pDlg->Show(); } return 0; }