/* Basic History plugin Copyright (C) 2011-2012 Krzysztof Kral 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" #include "HistoryWindow.h" #include "resource.h" #include "Options.h" #include "HotkeyHelper.h" #include "ImageDataObject.h" #include "ExportManager.h" extern HCURSOR hCurSplitNS, hCurSplitWE; extern int iconsNum; extern bool g_SmileyAddAvail; extern char* metaContactProto; extern IconItem iconList[]; #define DM_HREBUILD (WM_USER+11) #define DM_SPLITTERMOVED (WM_USER+15) #define MIN_PANELHEIGHT 40 void ResetCList(HWND hWnd); HistoryWindow::HistoryWindow(MCONTACT hContact) : isDestroyed(true), splitterY(0), splitterOrgY(0), splitterX(0), splitterOrgX(0), plusIco(nullptr), minusIco(nullptr), findNextIco(nullptr), findPrevIco(nullptr), configIco(nullptr), deleteIco(nullptr), isContactList(false), isLoading(false), isGroupImages(false), allIconNumber(0), eventIcons(nullptr), bkBrush(nullptr), bkFindBrush(nullptr), hSystem(NULL), splitterXhWnd(nullptr), splitterYhWnd(nullptr), isStartSelect(true) { searcher.SetContect(this); m_hContact = hContact; selected = -1; searcher.SetMatchCase(Options::instance->searchMatchCase); searcher.SetMatchWholeWords(Options::instance->searchMatchWhole); searcher.SetOnlyIn(Options::instance->searchOnlyIn); searcher.SetOnlyOut(Options::instance->searchOnlyOut); searcher.SetOnlyGroup(Options::instance->searchOnlyGroup); searcher.SetAllUsers(Options::instance->searchAllContacts); searcher.SetSearchForInLG(Options::instance->searchForInList); searcher.SetSearchForInMes(Options::instance->searchForInMess); } HistoryWindow::~HistoryWindow() { if (eventIcons != nullptr) { for (int i = 0; i < iconsNum; ++i) if (eventIcons[i] != nullptr) IcoLib_ReleaseIcon(eventIcons[i]); delete[] eventIcons; } if (plusIco != nullptr) IcoLib_ReleaseIcon(plusIco); if (minusIco != nullptr) IcoLib_ReleaseIcon(minusIco); if (findNextIco != nullptr) IcoLib_ReleaseIcon(findNextIco); if (findPrevIco != nullptr) IcoLib_ReleaseIcon(findPrevIco); if (himlSmall != nullptr) ImageList_Destroy(himlSmall); if (himlNone != nullptr) ImageList_Destroy(himlNone); if (bkBrush != nullptr) DeleteObject(bkBrush); if (bkFindBrush != nullptr) DeleteObject(bkFindBrush); } std::map HistoryWindow::windows; std::vector HistoryWindow::freeWindows; void HistoryWindow::Deinit() { bool destroyed = true; std::vector keys; for (auto it = windows.begin(); it != windows.end(); ++it) if (!it->second->isDestroyed) keys.push_back(it->first); for (auto it = keys.begin(); it != keys.end(); ++it) { auto it1 = windows.find(*it); if (it1 != windows.end()) { DestroyWindow(it1->second->m_hWnd); it1 = windows.find(*it); destroyed &= it1 == windows.end(); } } std::vector keys1; for (auto it = freeWindows.begin(); it != freeWindows.end(); ++it) if (!(*it)->isDestroyed) keys1.push_back(*it); for (auto it = keys1.begin(); it != keys1.end(); ++it) DestroyWindow((*it)->m_hWnd); for (auto it = freeWindows.begin(); it != freeWindows.end(); ++it) { if (!(*it)->isDestroyed) { destroyed = false; break; } } if (destroyed) { for (auto it = windows.begin(); it != windows.end(); ++it) delete it->second; windows.clear(); for (auto it = freeWindows.begin(); it != freeWindows.end(); ++it) delete *it; freeWindows.clear(); } } void HistoryWindow::Open(MCONTACT hContact) { if (hContact == NULL) { HistoryWindow *hw = new HistoryWindow(hContact); freeWindows.push_back(hw); hw->Show(); } else { auto it = windows.find(hContact); if (it != windows.end()) it->second->Focus(); else { windows[hContact] = new HistoryWindow(hContact); windows[hContact]->Show(); } } } void HistoryWindow::Close(MCONTACT hContact) { auto it = windows.find(hContact); if (it != windows.end()) { if (it->second->isDestroyed) { delete it->second; windows.erase(it); } else DestroyWindow(it->second->m_hWnd); } } void HistoryWindow::Close(HistoryWindow* historyWindow) { if (!historyWindow->isDestroyed) { DestroyWindow(historyWindow->m_hWnd); return; } auto it = windows.find(historyWindow->m_hContact); if (it != windows.end() && it->second == historyWindow) { delete it->second; windows.erase(it); } else { for (auto iw = freeWindows.begin(); iw != freeWindows.end(); ++iw) { if (*iw == historyWindow) { freeWindows.erase(iw); delete historyWindow; return; } } } } void HistoryWindow::RebuildEvents(MCONTACT hContact) { if (hContact != NULL) { auto it = windows.find(hContact); if (it != windows.end() && !it->second->isDestroyed) SendMessage(it->second->m_hWnd, DM_HREBUILD, 0, 0); } for (auto it = freeWindows.begin(); it != freeWindows.end(); ++it) if ((*it)->m_hContact == hContact && !(*it)->isDestroyed) SendMessage((*it)->m_hWnd, DM_HREBUILD, 0, 0); } void HistoryWindow::ChangeToFreeWindow(HistoryWindow* historyWindow) { auto it = windows.find(historyWindow->m_hContact); if (it != windows.end() && it->second == historyWindow) { windows.erase(it); freeWindows.push_back(historyWindow); } } void HistoryWindow::Show() { CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_HISTORY), nullptr, HistoryWindow::DlgProcHistory, (LPARAM)this); } void HistoryWindow::Focus() { if (IsIconic(m_hWnd)) ShowWindow(m_hWnd, SW_RESTORE); else { SetForegroundWindow(m_hWnd); SetFocus(m_hWnd); } SendMessage(m_hWnd, DM_HREBUILD, 0, 0); } int HistoryWindow::FontsChanged(WPARAM, LPARAM) { for (auto it = windows.begin(); it != windows.end(); ++it) if (!it->second->isDestroyed) it->second->FontsChanged(); for (auto it = freeWindows.begin(); it != freeWindows.end(); ++it) if (!(*it)->isDestroyed) (*it)->FontsChanged(); return 0; } void HistoryWindow::FontsChanged() { if (bkBrush != nullptr) DeleteObject(bkBrush); if (bkFindBrush != nullptr) DeleteObject(bkFindBrush); bkBrush = CreateSolidBrush(Options::instance->GetColor(Options::WindowBackground)); bkFindBrush = CreateSolidBrush(Options::instance->GetColor(Options::FindBackground)); ResetCList(m_hWnd); COLORREF bkColor = Options::instance->GetColor(Options::GroupListBackground); ListView_SetBkColor(listWindow, bkColor); ListView_SetTextBkColor(listWindow, bkColor); LOGFONT font; ListView_SetTextColor(listWindow, Options::instance->GetFont(Options::GroupList, &font)); InvalidateRect(listWindow, nullptr, TRUE); InvalidateRect(m_hWnd, nullptr, TRUE); SelectEventGroup(selected); } void OptionsGroupChanged() { HistoryWindow::OptionsGroupChanged(); } void HistoryWindow::OptionsGroupChanged() { for (auto it = windows.begin(); it != windows.end(); ++it) { if (!it->second->isDestroyed) { it->second->GroupImagesChanged(); SendMessage(it->second->m_hWnd, DM_HREBUILD, 0, 0); } } for (auto it = freeWindows.begin(); it != freeWindows.end(); ++it) { if (!(*it)->isDestroyed) { (*it)->GroupImagesChanged(); SendMessage((*it)->m_hWnd, DM_HREBUILD, 0, 0); } } } void OptionsMainChanged() { HistoryWindow::OptionsMainChanged(); } void HistoryWindow::OptionsMainChanged() { for (auto it = windows.begin(); it != windows.end(); ++it) if (!it->second->isDestroyed) it->second->ReloadMainOptions(); for (auto it = freeWindows.begin(); it != freeWindows.end(); ++it) if (!(*it)->isDestroyed) (*it)->ReloadMainOptions(); } void HistoryWindow::ReloadMainOptions() { SendDlgItemMessage(m_hWnd, IDC_LIST_CONTACTS, CLM_SETUSEGROUPS, Options::instance->showContactGroups, 0); SendMessage(m_hWnd, DM_HREBUILD, 0, 0); } void OptionsMessageChanged() { HistoryWindow::FontsChanged(0, 0); } void OptionsSearchingChanged() { HistoryWindow::OptionsSearchingChanged(); } void HistoryWindow::OptionsSearchingChanged() { for (auto it = windows.begin(); it != windows.end(); ++it) { if (!it->second->isDestroyed) { it->second->searcher.SetSearchForInLG(Options::instance->searchForInList); it->second->searcher.SetSearchForInMes(Options::instance->searchForInMess); } } for (auto it = freeWindows.begin(); it != freeWindows.end(); ++it) { if (!(*it)->isDestroyed) { (*it)->searcher.SetSearchForInLG(Options::instance->searchForInList); (*it)->searcher.SetSearchForInMes(Options::instance->searchForInMess); } } } bool HistoryWindow::IsInList(HWND hWnd) { for (auto it = windows.begin(); it != windows.end(); ++it) if (!it->second->isDestroyed) if (it->second->m_hWnd == hWnd) return true; for (auto it = freeWindows.begin(); it != freeWindows.end(); ++it) if (!(*it)->isDestroyed) if ((*it)->m_hWnd == hWnd) return true; return false; } void ClickLink(HWND hwnd, ENLINK *penLink) { wchar_t buf[1024]; if (penLink->msg != WM_LBUTTONUP) return; if (penLink->chrg.cpMin >= 0 && penLink->chrg.cpMax > penLink->chrg.cpMin) { // selection int len = penLink->chrg.cpMax - penLink->chrg.cpMin; if (len < 1023) { TEXTRANGE tr; CHARRANGE sel; SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)& sel); if (sel.cpMin != sel.cpMax) return; tr.chrg = penLink->chrg; tr.lpstrText = buf; SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM)& tr); Utils_OpenUrlW(tr.lpstrText, penLink->nmhdr.code == IDM_OPENNEW); } } } void ConvertSize(HWND hwndSrc, HWND hwndDest, RECT& rc) { POINT pt; pt.x = rc.left; pt.y = rc.top; ClientToScreen(hwndSrc, &pt); ScreenToClient(hwndDest, &pt); rc.left = pt.x; rc.top = pt.y; pt.x = rc.right; pt.y = rc.bottom; ClientToScreen(hwndSrc, &pt); ScreenToClient(hwndDest, &pt); rc.right = pt.x; rc.bottom = pt.y; } #define DlgReturn(ret){\ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (ret));\ return (ret);\ } struct ShowMessageData { ShowMessageData(MCONTACT hContact) : m_hContact(hContact) { } ShowMessageData(MCONTACT hContact, const std::wstring &str) : m_hContact(hContact), m_str(str) { } MCONTACT m_hContact; std::wstring m_str; }; void __stdcall ShowMessageWindow(void* arg) { ShowMessageData* dt = (ShowMessageData*)arg; if (dt->m_str.empty()) CallService(MS_MSG_SENDMESSAGE, dt->m_hContact, 0); else CallService(MS_MSG_SENDMESSAGEW, dt->m_hContact, (LPARAM)dt->m_str.c_str()); delete dt; } INT_PTR CALLBACK HistoryWindow::DlgProcHistory(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { HistoryWindow *historyWindow = (HistoryWindow*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); LPNMHDR pNmhdr; switch (msg) { case WM_INITDIALOG: TranslateDialogDefault(hwndDlg); historyWindow = (HistoryWindow*)lParam; historyWindow->m_hWnd = hwndDlg; historyWindow->m_isWnd = true; SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); historyWindow->Initialise(); DlgReturn(TRUE); case WM_GETMINMAXINFO: ((MINMAXINFO*)lParam)->ptMinTrackSize.x = 500; ((MINMAXINFO*)lParam)->ptMinTrackSize.y = 380; case WM_SIZE: Utils_ResizeDialog(hwndDlg, g_plugin.getInst(), MAKEINTRESOURCEA(IDD_HISTORY), HistoryWindow::HistoryDlgResizer); ListView_SetColumnWidth(GetDlgItem(hwndDlg, IDC_LIST), 0, LVSCW_AUTOSIZE_USEHEADER); DlgReturn(TRUE); case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: DestroyWindow(hwndDlg); DlgReturn(TRUE); case IDM_FIND: historyWindow->searcher.Find(); DlgReturn(TRUE); case IDM_CONFIG: g_plugin.openOptions(nullptr, LPGENW("History")); DlgReturn(TRUE); case IDM_DELETE: historyWindow->Delete(0); DlgReturn(TRUE); case IDC_FIND_TEXT: if (HIWORD(wParam) == EN_CHANGE) historyWindow->searcher.ClearFind(); DlgReturn(TRUE); case IDC_SHOWHIDE: if (HIWORD(wParam) == BN_CLICKED) { if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_SHOWHIDE)) & BST_CHECKED) { SendDlgItemMessage(hwndDlg, IDC_SHOWHIDE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)historyWindow->minusIco); SendDlgItemMessage(hwndDlg, IDC_SHOWHIDE, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Hide contacts"), BATF_UNICODE); historyWindow->isContactList = true; ShowWindow(GetDlgItem(hwndDlg, IDC_LIST_CONTACTS), SW_SHOW); ShowWindow(historyWindow->splitterYhWnd, SW_SHOW); } else { SendDlgItemMessage(hwndDlg, IDC_SHOWHIDE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)historyWindow->plusIco); SendDlgItemMessage(hwndDlg, IDC_SHOWHIDE, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Show contacts"), BATF_UNICODE); historyWindow->isContactList = false; ShowWindow(GetDlgItem(hwndDlg, IDC_LIST_CONTACTS), SW_HIDE); ShowWindow(historyWindow->splitterYhWnd, SW_HIDE); } SendMessage(hwndDlg, WM_SIZE, 0, 0); } DlgReturn(TRUE); } break; case WM_NOTIFY: pNmhdr = (LPNMHDR)lParam; switch (pNmhdr->idFrom) { case IDC_LIST_CONTACTS: if (pNmhdr->code == CLN_LISTREBUILT) { if (historyWindow != nullptr) historyWindow->ReloadContacts(); DlgReturn(TRUE); } else if (pNmhdr->code == CLN_MYSELCHANGED) { if (historyWindow->ContactChanged()) { MSGFILTER* msgFilter = (MSGFILTER *)lParam; if (msgFilter->msg == WM_LBUTTONDOWN) SendMessage(pNmhdr->hwndFrom, WM_LBUTTONUP, msgFilter->wParam, msgFilter->lParam); } DlgReturn(TRUE); } else if (pNmhdr->code == CLN_OPTIONSCHANGED) { ResetCList(hwndDlg); return FALSE; } __fallthrough; case IDC_SHOWHIDE: case IDC_FIND_TEXT: case IDC_EDIT: if (pNmhdr->code == EN_LINK) { ClickLink(GetDlgItem(hwndDlg, IDC_EDIT), (ENLINK *)lParam); return FALSE; } if (pNmhdr->code == EN_SELCHANGE) historyWindow->searcher.ClearFind(); else if (pNmhdr->code == EN_MSGFILTER) { MSGFILTER* msgFilter = (MSGFILTER *)lParam; if (msgFilter->msg == WM_KEYDOWN || msgFilter->msg == WM_SYSKEYDOWN) { if (historyWindow->DoHotkey(msgFilter->msg, msgFilter->lParam, msgFilter->wParam, pNmhdr->idFrom)) DlgReturn(TRUE); } else if (msgFilter->msg == WM_RBUTTONDOWN || msgFilter->msg == WM_RBUTTONDBLCLK || msgFilter->msg == WM_NCRBUTTONUP || msgFilter->msg == WM_NCRBUTTONDBLCLK || msgFilter->msg == WM_NCRBUTTONDOWN) DlgReturn(TRUE); if (msgFilter->msg == WM_RBUTTONUP) { POINT clicked; HWND window = historyWindow->editWindow; POINTL p; POINT scrool; LVHITTESTINFO info = { 0 }; p.x = clicked.x = info.pt.x = GET_X_LPARAM(msgFilter->lParam); p.y = clicked.y = info.pt.y = GET_Y_LPARAM(msgFilter->lParam); ClientToScreen(window, &clicked); SetFocus(window); int selChar = SendMessage(window, EM_CHARFROMPOS, 0, (LPARAM)&p); CHARRANGE chrg; SendMessage(window, EM_EXGETSEL, 0, (LPARAM)&chrg); SendMessage(window, EM_GETSCROLLPOS, 0, (LPARAM)&scrool); if (selChar < chrg.cpMin || selChar > chrg.cpMax) chrg.cpMin = chrg.cpMax = selChar; if (chrg.cpMin == chrg.cpMax) { CHARRANGE chrgNew; chrgNew.cpMin = chrg.cpMin; chrgNew.cpMax = chrg.cpMax + 1; SendMessage(window, EM_EXSETSEL, 0, (LPARAM)&chrgNew); } CHARFORMAT2 chf; memset(&chf, 0, sizeof(CHARFORMAT2)); chf.cbSize = sizeof(CHARFORMAT2); chf.dwMask = CFM_LINK; SendMessage(window, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&chf); if (chrg.cpMin == chrg.cpMax) SendMessage(window, EM_EXSETSEL, 0, (LPARAM)&chrg); HMENU hPopupMenu = CreatePopupMenu(); if (hPopupMenu != nullptr) { if (chf.dwEffects & CFE_LINK) { AppendMenu(hPopupMenu, MF_STRING, IDM_OPENNEW, TranslateT("Open in &new window")); AppendMenu(hPopupMenu, MF_STRING, IDM_OPENEXISTING, TranslateT("&Open in existing window")); AppendMenu(hPopupMenu, MF_STRING, IDM_COPYLINK, TranslateT("&Copy link")); } else { AppendMenu(hPopupMenu, MF_STRING, IDM_COPY, TranslateT("Copy")); AppendMenu(hPopupMenu, MF_STRING, IDM_DELETE, TranslateT("Delete")); AppendMenu(hPopupMenu, MFT_SEPARATOR, 0, nullptr); AppendMenu(hPopupMenu, MF_STRING, IDM_MESSAGE, TranslateT("Send message")); AppendMenu(hPopupMenu, MF_STRING, IDM_QUOTE, TranslateT("Reply "ed")); AppendMenu(hPopupMenu, MF_STRING, IDM_DELETEGROUP, TranslateT("Delete group")); AppendMenu(hPopupMenu, MF_STRING, IDM_DELETEUSER, TranslateT("Delete all contact history")); } int selected = TrackPopupMenu(hPopupMenu, TPM_RETURNCMD, clicked.x, clicked.y, 0, hwndDlg, nullptr); switch (selected) { case IDM_COPY: if (chrg.cpMax == chrg.cpMin && historyWindow->currentGroup.size() > 0) { size_t start = 0; while (start < historyWindow->currentGroup.size() && chrg.cpMin >= historyWindow->currentGroup[start].endPos) ++start; if (start < historyWindow->currentGroup.size()) { CHARRANGE chrgNew; chrgNew.cpMin = 0; if (start > 0) chrgNew.cpMin = historyWindow->currentGroup[start - 1].endPos; chrgNew.cpMax = historyWindow->currentGroup[start].endPos; SendMessage(window, EM_EXSETSEL, 0, (LPARAM)&chrgNew); SendMessage(window, WM_COPY, 0, 0); SendMessage(window, EM_EXSETSEL, 0, (LPARAM)&chrg); } } else SendMessage(window, WM_COPY, 0, 0); break; case IDM_MESSAGE: CallFunctionAsync(ShowMessageWindow, new ShowMessageData(historyWindow->m_hContact)); break; case IDM_QUOTE: if (historyWindow->currentGroup.size() > 0) { std::wstring quote; if (chrg.cpMax == chrg.cpMin) { size_t start = 0; while (start < historyWindow->currentGroup.size() && chrg.cpMin >= historyWindow->currentGroup[start].endPos) ++start; if (start < historyWindow->currentGroup.size()) historyWindow->FormatQuote(quote, historyWindow->currentGroup[start], historyWindow->currentGroup[start].description); } else { size_t start = 0; while (start < historyWindow->currentGroup.size() && chrg.cpMin >= historyWindow->currentGroup[start].endPos) ++start; size_t end = 0; while (end < historyWindow->currentGroup.size() && chrg.cpMax > historyWindow->currentGroup[end].endPos) ++end; if (end >= historyWindow->currentGroup.size()) end = historyWindow->currentGroup.size() - 1; if (start == end && start < historyWindow->currentGroup.size()) { int iStart = historyWindow->currentGroup[start].startPos; if (chrg.cpMin > iStart) iStart = chrg.cpMin; int iEnd = historyWindow->currentGroup[start].endPos; if (chrg.cpMax < iEnd) iEnd = chrg.cpMax; if (iEnd > iStart) { TEXTRANGE tr; tr.chrg.cpMin = iStart; tr.chrg.cpMax = iEnd; tr.lpstrText = new wchar_t[iEnd - iStart + 1]; SendMessage(historyWindow->editWindow, EM_GETTEXTRANGE, 0, (LPARAM)& tr); historyWindow->FormatQuote(quote, historyWindow->currentGroup[start], tr.lpstrText); delete[] tr.lpstrText; } } else { while (start <= end) { historyWindow->FormatQuote(quote, historyWindow->currentGroup[start], historyWindow->currentGroup[start].description); ++start; } } } if (!quote.empty()) CallFunctionAsync(ShowMessageWindow, new ShowMessageData(historyWindow->m_hContact, quote)); } break; case IDM_DELETE: historyWindow->Delete(0); break; case IDM_DELETEGROUP: historyWindow->Delete(1); break; case IDM_DELETEUSER: historyWindow->Delete(2); break; case IDM_OPENNEW: case IDM_OPENEXISTING: case IDM_COPYLINK: { int start = chrg.cpMin, end = chrg.cpMin; CHARRANGE chrgNew; chrgNew.cpMin = start - 1; chrgNew.cpMax = start; do { memset(&chf, 0, sizeof(CHARFORMAT2)); chf.cbSize = sizeof(CHARFORMAT2); chf.dwMask = CFM_LINK; int sel = SendMessage(window, EM_EXSETSEL, 0, (LPARAM)&chrgNew); if (sel != chrgNew.cpMax) break; SendMessage(window, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&chf); --chrgNew.cpMin; --chrgNew.cpMax; --start; } while (start >= 0 && chf.dwEffects & CFE_LINK); ++start; chrgNew.cpMin = end; chrgNew.cpMax = end + 1; do { memset(&chf, 0, sizeof(CHARFORMAT2)); chf.cbSize = sizeof(CHARFORMAT2); chf.dwMask = CFM_LINK; int sel = SendMessage(window, EM_EXSETSEL, 0, (LPARAM)&chrgNew); if (sel != chrgNew.cpMax) break; SendMessage(window, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&chf); ++chrgNew.cpMin; ++chrgNew.cpMax; ++end; } while (chf.dwEffects & CFE_LINK); --end; if (selected == IDM_COPYLINK) { chrgNew.cpMin = start; chrgNew.cpMax = end; SendMessage(window, EM_EXSETSEL, 0, (LPARAM)&chrgNew); SendMessage(window, WM_COPY, 0, 0); SendMessage(window, EM_EXSETSEL, 0, (LPARAM)&chrg); } else { ENLINK link; link.chrg.cpMin = start; link.chrg.cpMax = end; link.msg = WM_LBUTTONUP; link.nmhdr.code = selected; SendMessage(window, EM_EXSETSEL, 0, (LPARAM)&chrg); ClickLink(window, &link); } } break; } DestroyMenu(hPopupMenu); } SendMessage(window, EM_SETSCROLLPOS, 0, (LPARAM)&scrool); DlgReturn(TRUE); } } break; case IDC_LIST: if (pNmhdr->code == LVN_ITEMCHANGED) { NMLISTVIEW *nmlv = (NMLISTVIEW*)lParam; if ((nmlv->uChanged & LVIF_STATE) && (nmlv->uNewState & LVIS_SELECTED) && historyWindow->selected != nmlv->iItem && nmlv->iItem >= 0) { historyWindow->SelectEventGroup(nmlv->iItem); DlgReturn(TRUE); } } else if (pNmhdr->code == LVN_KEYDOWN) { LPNMLVKEYDOWN nmlv = (LPNMLVKEYDOWN)lParam; if (historyWindow->DoHotkey(WM_KEYDOWN, 0, nmlv->wVKey, IDC_LIST)) DlgReturn(TRUE); } else if (pNmhdr->code == NM_RCLICK) { POINT clicked; LPNMITEMACTIVATE nmlv = (LPNMITEMACTIVATE)lParam; HWND window = historyWindow->listWindow; LVHITTESTINFO info = { 0 }; clicked.x = info.pt.x = nmlv->ptAction.x; clicked.y = info.pt.y = nmlv->ptAction.y; ClientToScreen(window, &clicked); int newSel = SendMessage(window, LVM_SUBITEMHITTEST, 0, (LPARAM)&info); if (newSel >= 0) { HMENU hPopupMenu = CreatePopupMenu(); if (hPopupMenu != nullptr) { AppendMenu(hPopupMenu, MF_STRING, IDM_COPY, TranslateT("Copy")); AppendMenu(hPopupMenu, MF_STRING, IDM_DELETEGROUP, TranslateT("Delete group")); AppendMenu(hPopupMenu, MFT_SEPARATOR, 0, nullptr); AppendMenu(hPopupMenu, MF_STRING, IDM_MESSAGE, TranslateT("Send message")); AppendMenu(hPopupMenu, MF_STRING, IDM_DELETEUSER, TranslateT("Delete all contact history")); int selected = TrackPopupMenu(hPopupMenu, TPM_RETURNCMD, clicked.x, clicked.y, 0, hwndDlg, nullptr); switch (selected) { case IDM_COPY: { CHARRANGE chrg; SendMessage(historyWindow->editWindow, EM_EXGETSEL, 0, (LPARAM)&chrg); CHARRANGE chrgNew; chrgNew.cpMin = 0; chrgNew.cpMax = -1; SendMessage(historyWindow->editWindow, EM_EXSETSEL, 0, (LPARAM)&chrgNew); SendMessage(historyWindow->editWindow, WM_COPY, 0, 0); SendMessage(historyWindow->editWindow, EM_EXSETSEL, 0, (LPARAM)&chrg); } break; case IDM_MESSAGE: CallService(MS_MSG_SENDMESSAGE, historyWindow->m_hContact, 0); break; case IDM_DELETEGROUP: historyWindow->Delete(1); break; case IDM_DELETEUSER: historyWindow->Delete(2); break; } DestroyMenu(hPopupMenu); } } DlgReturn(TRUE); } break; case IDC_TOOLBAR: if (pNmhdr->code == TBN_DROPDOWN) { LPNMTOOLBAR lpnmTB = (LPNMTOOLBAR)lParam; if (lpnmTB->iItem == IDM_FIND) historyWindow->FindToolbarClicked(lpnmTB); else if (lpnmTB->iItem == IDM_CONFIG) historyWindow->ConfigToolbarClicked(lpnmTB); else if (lpnmTB->iItem == IDM_DELETE) historyWindow->DeleteToolbarClicked(lpnmTB); DlgReturn(TBDDRET_DEFAULT); } else if (pNmhdr->code == NM_KEYDOWN) { LPNMKEY nmlv = (LPNMKEY)lParam; if (historyWindow->DoHotkey(WM_KEYDOWN, 0, nmlv->nVKey, IDC_TOOLBAR)) DlgReturn(TRUE); } } break; case WM_CTLCOLORDLG: DlgReturn((LONG_PTR)historyWindow->bkBrush); case WM_CTLCOLORSTATIC: { HWND curhWnd = (HWND)lParam; if (historyWindow->splitterXhWnd == curhWnd || historyWindow->splitterYhWnd == curhWnd) DlgReturn((LONG_PTR)historyWindow->bkBrush); break; } case WM_CTLCOLOREDIT: { HWND curhWnd = (HWND)lParam; if (historyWindow->findWindow == curhWnd) { HDC edithdc = (HDC)wParam; LOGFONT font; SetTextColor(edithdc, Options::instance->GetFont(Options::Find, &font)); SetBkColor(edithdc, Options::instance->GetColor(Options::FindBackground)); DlgReturn((LONG_PTR)historyWindow->bkFindBrush); } } break; case DM_SPLITTERMOVED: historyWindow->SplitterMoved((HWND)lParam, wParam, true); break; case DM_HREBUILD: if (!historyWindow->isLoading) { historyWindow->isLoading = true; historyWindow->ReloadContacts(); mir_forkThread(HistoryWindow::FillHistoryThread, historyWindow); } DlgReturn(TRUE); case WM_DESTROY: SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); historyWindow->Destroy(); DlgReturn(TRUE); } return FALSE; } void HistoryWindow::Initialise() { splitterXhWnd = GetDlgItem(m_hWnd, IDC_SPLITTER); splitterYhWnd = GetDlgItem(m_hWnd, IDC_SPLITTERV); mir_subclassWindow(splitterXhWnd, SplitterSubclassProc); mir_subclassWindow(splitterYhWnd, SplitterSubclassProc); editWindow = GetDlgItem(m_hWnd, IDC_EDIT); findWindow = GetDlgItem(m_hWnd, IDC_FIND_TEXT); toolbarWindow = GetDlgItem(m_hWnd, IDC_TOOLBAR); listWindow = GetDlgItem(m_hWnd, IDC_LIST); RECT rc; POINT pt; GetWindowRect(splitterXhWnd, &rc); pt.y = (rc.top + rc.bottom) / 2; pt.x = 0; ScreenToClient(m_hWnd, &pt); splitterOrgY = pt.y; splitterY = pt.y; GetWindowRect(splitterYhWnd, &rc); pt.y = 0; pt.x = (rc.left + rc.right) / 2; ScreenToClient(m_hWnd, &pt); splitterOrgX = pt.x; splitterX = pt.x; GetWindowRect(GetDlgItem(m_hWnd, IDC_LIST_CONTACTS), &rc); pt.y = rc.top; pt.x = rc.left; ScreenToClient(m_hWnd, &pt); listOryginalPos = pt.x; plusIco = LoadIconEx(IDI_SHOW, 1); minusIco = LoadIconEx(IDI_HIDE, 1); SendDlgItemMessage(m_hWnd, IDC_SHOWHIDE, BUTTONSETASPUSHBTN, TRUE, 0); SendDlgItemMessage(m_hWnd, IDC_SHOWHIDE, BUTTONSETASFLATBTN, TRUE, 0); if (m_hContact == NULL || Options::instance->showContacts) { SendDlgItemMessage(m_hWnd, IDC_SHOWHIDE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)minusIco); SendDlgItemMessage(m_hWnd, IDC_SHOWHIDE, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Hide contacts"), BATF_UNICODE); Button_SetCheck(GetDlgItem(m_hWnd, IDC_SHOWHIDE), BST_CHECKED); isContactList = true; } else { SendDlgItemMessage(m_hWnd, IDC_SHOWHIDE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)plusIco); SendDlgItemMessage(m_hWnd, IDC_SHOWHIDE, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Show contacts"), BATF_UNICODE); Button_SetCheck(GetDlgItem(m_hWnd, IDC_SHOWHIDE), BST_UNCHECKED); ShowWindow(GetDlgItem(m_hWnd, IDC_LIST_CONTACTS), SW_HIDE); ShowWindow(splitterYhWnd, SW_HIDE); isContactList = false; } RegisterHotkeyControl(GetDlgItem(m_hWnd, IDC_SHOWHIDE)); RegisterHotkeyControl(GetDlgItem(m_hWnd, IDC_LIST_CONTACTS)); ResetCList(m_hWnd); RestorePos(); Window_SetSkinIcon_IcoLib(m_hWnd, SKINICON_OTHER_HISTORY); SendMessage(editWindow, EM_AUTOURLDETECT, TRUE, 0); SendMessage(editWindow, EM_SETEVENTMASK, 0, ENM_LINK | ENM_SELCHANGE | ENM_KEYEVENTS | ENM_MOUSEEVENTS); SendMessage(editWindow, EM_SETEDITSTYLE, SES_EXTENDBACKCOLOR, SES_EXTENDBACKCOLOR); himlSmall = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 2, 2); himlNone = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 2, 2); ImageList_SetIconSize(himlNone, 0, 16); if (himlSmall) { allIconNumber = iconsNum + 3; eventIcons = new HICON[allIconNumber]; for (int i = 0; i < iconsNum; ++i) { eventIcons[i] = IcoLib_GetIconByHandle(iconList[i].hIcolib); ImageList_AddIcon(himlSmall, eventIcons[i]); } int id = iconsNum; eventIcons[id] = Skin_LoadIcon(SKINICON_EVENT_FILE); ImageList_AddIcon(himlSmall, eventIcons[id]); eventIcons[++id] = Skin_LoadIcon(SKINICON_EVENT_URL); ImageList_AddIcon(himlSmall, eventIcons[id]); eventIcons[++id] = Skin_LoadIcon(SKINICON_OTHER_WINDOWS); ImageList_AddIcon(himlSmall, eventIcons[id]); if ((isGroupImages = Options::instance->groupShowEvents) != false) ListView_SetImageList(listWindow, himlSmall, LVSIL_SMALL); } bkBrush = CreateSolidBrush(Options::instance->GetColor(Options::WindowBackground)); bkFindBrush = CreateSolidBrush(Options::instance->GetColor(Options::FindBackground)); LVCOLUMN col = { 0 }; col.mask = LVCF_WIDTH | LVCF_TEXT; col.cx = 470; col.pszText = L""; ListView_InsertColumn(listWindow, 0, &col); ListView_SetColumnWidth(listWindow, 0, LVSCW_AUTOSIZE_USEHEADER); ListView_SetExtendedListViewStyleEx(listWindow, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT); COLORREF bkColor = Options::instance->GetColor(Options::GroupListBackground); ListView_SetBkColor(listWindow, bkColor); ListView_SetTextBkColor(listWindow, bkColor); LOGFONT font; ListView_SetTextColor(listWindow, Options::instance->GetFont(Options::GroupList, &font)); Edit_LimitText(findWindow, 100); RegisterHotkeyControl(findWindow); HIMAGELIST himlButtons = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 3, 3); if (himlButtons) { findNextIco = LoadIconEx(IDI_FINDNEXT); ImageList_AddIcon(himlButtons, findNextIco); findPrevIco = LoadIconEx(IDI_FINDPREV); ImageList_AddIcon(himlButtons, findPrevIco); configIco = Skin_LoadIcon(SKINICON_OTHER_OPTIONS); ImageList_AddIcon(himlButtons, configIco); deleteIco = Skin_LoadIcon(SKINICON_OTHER_DELETE); ImageList_AddIcon(himlButtons, deleteIco); // Set the image list. SendMessage(toolbarWindow, TB_SETIMAGELIST, 0, (LPARAM)himlButtons); // Load the button images. SendMessage(toolbarWindow, TB_LOADIMAGES, IDB_STD_SMALL_COLOR, (LPARAM)HINST_COMMCTRL); } TBBUTTON tbButtons[] = { { 0, IDM_FIND, TBSTATE_ENABLED, BTNS_DROPDOWN, {0}, 0, (INT_PTR)TranslateT("Find next") }, { 3, IDM_DELETE, TBSTATE_ENABLED, BTNS_DROPDOWN, {0}, 0, (INT_PTR)TranslateT("Delete")}, { 2, IDM_CONFIG, TBSTATE_ENABLED, BTNS_DROPDOWN, {0}, 0, (INT_PTR)TranslateT("Options")}, }; SendMessage(toolbarWindow, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0); SendMessage(toolbarWindow, TB_ADDBUTTONS, _countof(tbButtons), (LPARAM)&tbButtons); SendMessage(toolbarWindow, TB_SETBUTTONSIZE, 0, MAKELPARAM(16, 16)); SendMessage(toolbarWindow, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS); SendMessage(toolbarWindow, TB_SETMAXTEXTROWS, 0, 0); SetDefFilter(Options::instance->defFilter); InvalidateRect(listWindow, nullptr, TRUE); InvalidateRect(m_hWnd, nullptr, TRUE); SendMessage(m_hWnd, DM_SETDEFID, IDM_FIND, 0); SendMessage(m_hWnd, WM_SIZE, 0, 0); SendMessage(m_hWnd, DM_HREBUILD, 0, 0); isDestroyed = false; } void HistoryWindow::Destroy() { Window_FreeIcon_IcoLib(m_hWnd); isDestroyed = true; HistoryWindow::Close(this); } void HistoryWindow::SplitterMoved(HWND splitter, LONG pos, bool screenPos) { POINT pt; RECT rc1; RECT rc2; POINT pt1; POINT pt2; if (splitter == splitterXhWnd) { GetWindowRect(listWindow, &rc1); GetWindowRect(editWindow, &rc2); pt.x = 0; pt.y = pos; pt1.x = rc1.left; pt1.y = rc1.top; pt2.x = rc2.right; pt2.y = rc2.bottom; if (screenPos) ScreenToClient(m_hWnd, &pt); ScreenToClient(m_hWnd, &pt1); ScreenToClient(m_hWnd, &pt2); if ((pt.y >= pt1.y + MIN_PANELHEIGHT) && (pt.y < pt2.y - MIN_PANELHEIGHT)) { splitterY = pt.y; if (!screenPos) SendMessage(m_hWnd, WM_SIZE, 0, 0); } } else { GetWindowRect(GetDlgItem(m_hWnd, IDC_LIST_CONTACTS), &rc1); GetWindowRect(listWindow, &rc2); pt.x = pos; pt.y = 0; pt1.x = rc1.left; pt1.y = rc1.top; pt2.x = rc2.right; pt2.y = rc2.bottom; if (screenPos) ScreenToClient(m_hWnd, &pt); ScreenToClient(m_hWnd, &pt1); ScreenToClient(m_hWnd, &pt2); if ((pt.x >= pt1.x + MIN_PANELHEIGHT) && (pt.x < pt2.x - MIN_PANELHEIGHT)) { splitterX = pt.x; if (!screenPos) SendMessage(m_hWnd, WM_SIZE, 0, 0); } } } int HistoryWindow::HistoryDlgResizer(HWND hwnd, LPARAM, UTILRESIZECONTROL *urc) { HistoryWindow* historyWindow = (HistoryWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA); switch (urc->wId) { case IDC_LIST: urc->rcItem.bottom += historyWindow->splitterY - historyWindow->splitterOrgY; urc->rcItem.left += historyWindow->splitterX - historyWindow->splitterOrgX; if (!historyWindow->isContactList) urc->rcItem.left = historyWindow->listOryginalPos; return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP; case IDC_LIST_CONTACTS: urc->rcItem.right += historyWindow->splitterX - historyWindow->splitterOrgX; return RD_ANCHORX_LEFT | RD_ANCHORY_HEIGHT; case IDC_SPLITTER: urc->rcItem.top += historyWindow->splitterY - historyWindow->splitterOrgY; urc->rcItem.bottom += historyWindow->splitterY - historyWindow->splitterOrgY; urc->rcItem.left += historyWindow->splitterX - historyWindow->splitterOrgX; if (!historyWindow->isContactList) urc->rcItem.left = 0; return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP; case IDC_SPLITTERV: urc->rcItem.left += historyWindow->splitterX - historyWindow->splitterOrgX; urc->rcItem.right += historyWindow->splitterX - historyWindow->splitterOrgX; return RD_ANCHORX_LEFT | RD_ANCHORY_HEIGHT; case IDC_EDIT: urc->rcItem.top += historyWindow->splitterY - historyWindow->splitterOrgY; urc->rcItem.left += historyWindow->splitterX - historyWindow->splitterOrgX; if (!historyWindow->isContactList) urc->rcItem.left = historyWindow->listOryginalPos; return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; case IDC_FIND_TEXT: return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP; case IDC_TOOLBAR: return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP; } return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; } void HistoryWindow::FillHistoryThread(HistoryWindow *hInfo) { HWND hwndList = hInfo->listWindow; ListView_DeleteAllItems(hwndList); hInfo->SelectEventGroup(-1); hInfo->EnableWindows(FALSE); bool isNewOnTop = Options::instance->groupNewOnTop; hInfo->RefreshEventList(); LVITEM item = { 0 }; item.mask = LVIF_STATE; item.iItem = 0; item.state = LVIS_SELECTED; item.stateMask = LVIS_SELECTED; if (!isNewOnTop) { item.iItem = ListView_GetItemCount(hwndList) - 1; if (item.iItem < 0) item.iItem = 0; } ListView_SetItem(hwndList, &item); ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE_USEHEADER); ListView_EnsureVisible(hwndList, item.iItem, FALSE); hInfo->EnableWindows(TRUE); SetFocus(hwndList); } void HistoryWindow::AddGroup(bool, const std::wstring &time, const std::wstring &user, const std::wstring &eventText, int ico) { wchar_t msg[256]; msg[0] = 0; if (Options::instance->groupShowTime) wcscpy_s(msg, time.c_str()); if (Options::instance->groupShowName) { if (msg[0] != 0) wcscat_s(msg, L" "); wcscat_s(msg, user.c_str()); } if (Options::instance->groupShowMessage) { if (msg[0] != 0) wcscat_s(msg, L" "); wcscat_s(msg, eventText.c_str()); } LVITEM item = { 0 }; item.mask = LVIF_TEXT | LVIF_IMAGE; item.iItem = MAXINT; item.pszText = msg; item.iImage = ico; ListView_InsertItem(listWindow, &item); } void HistoryWindow::ReplaceIcons(HWND hwndDlg, int selStart, BOOL isSent) { if (!g_SmileyAddAvail) return; CHARRANGE sel; sel.cpMin = selStart; sel.cpMax = -1; SMADD_RICHEDIT smadd = { 0 }; smadd.hwndRichEditControl = hwndDlg; smadd.Protocolname = Proto_GetBaseAccountName(m_hContact); smadd.hContact = m_hContact; smadd.flags = isSent ? SAFLRE_OUTGOING : 0; if (selStart > 0) smadd.rangeToReplace = &sel; CallService(MS_SMILEYADD_REPLACESMILEYS, 0, (LPARAM)&smadd); } void SetFontFromOptions(ITextFont *TextFont, int caps, Options::Fonts fontId) { LOGFONT font; COLORREF fontColor = Options::instance->GetFont(fontId, &font); BSTR bstrFont = SysAllocString(font.lfFaceName); TextFont->SetName(bstrFont); SysFreeString(bstrFont); TextFont->SetForeColor(fontColor); TextFont->SetWeight(font.lfWeight); font.lfHeight = (font.lfHeight * 72) / caps; TextFont->SetSize(font.lfHeight < 0 ? -font.lfHeight : font.lfHeight); TextFont->SetItalic(font.lfItalic ? tomTrue : tomFalse); TextFont->SetItalic(font.lfUnderline ? tomTrue : tomFalse); TextFont->SetItalic(font.lfStrikeOut ? tomTrue : tomFalse); } void HistoryWindow::SelectEventGroup(int sel) { SetWindowText(editWindow, L""); currentGroup.clear(); selected = sel; if (sel < 0 || sel >= (int)m_eventList.size()) return; wchar_t _str[MAXSELECTSTR + 8]; // for safety reason wchar_t *str = _str + sizeof(int) / sizeof(wchar_t); BSTR pStr = str; unsigned int *strLen = (unsigned int*)_str; str[0] = 0; bool isFirst = true; bool lastMe = false; long startAt, endAt; long cnt; std::wstring strStl; IRichEditOle* RichEditOle; EventData data; if (SendMessage(editWindow, EM_GETOLEINTERFACE, 0, (LPARAM)&RichEditOle) == 0) return; ITextDocument* TextDocument; if (RichEditOle->QueryInterface(IID_ITextDocument, (void**)&TextDocument) != S_OK) { RichEditOle->Release(); return; } ITextSelection* TextSelection; ITextFont *TextFont; SendMessage(editWindow, EM_SETREADONLY, FALSE, 0); TextDocument->Freeze(&cnt); TextDocument->GetSelection(&TextSelection); HDC hDC = GetDC(nullptr); int caps = GetDeviceCaps(hDC, LOGPIXELSY); std::deque revDeq; std::deque &deq = m_eventList[sel]; if (Options::instance->messagesNewOnTop) { revDeq.insert(revDeq.begin(), deq.rbegin(), deq.rend()); deq = revDeq; } struct tm lastTime = { 0 }; COLORREF backColor = GetSysColor(COLOR_WINDOW); for (std::deque::iterator it = deq.begin(); it != deq.end(); ++it) { EventIndex hDbEvent = *it; if (GetEventData(hDbEvent, data)) { bool isUser = Options::instance->messagesShowName && (isFirst || (!lastMe && data.isMe) || (lastMe && !data.isMe)); lastMe = data.isMe; backColor = Options::instance->GetColor(lastMe ? Options::OutBackground : Options::InBackground); if (Options::instance->messagesShowEvents) { str[0] = '>'; str[1] = 0; *strLen = 1 * sizeof(wchar_t); TextSelection->SetStart(MAXLONG); TextSelection->GetFont(&TextFont); TextFont->SetBackColor(backColor); TextSelection->SetText(pStr); TextFont->Release(); int imId; HICON ico; if (GetEventIcon(lastMe, data.eventType, imId)) ico = eventIcons[imId]; else { ico = GetEventCoreIcon(hDbEvent); if (ico == nullptr) ico = eventIcons[imId]; } ImageDataObject::InsertIcon(RichEditOle, ico, backColor, 16, 16); } wchar_t* formatDate = Options::instance->messagesShowSec ? (isUser ? L"d s " : L"d s\n") : (isUser ? L"d t " : L"d t\n"); if (!Options::instance->messagesShowDate) { if (isFirst) { isFirst = false; formatDate = Options::instance->messagesShowSec ? (isUser ? L"s " : L"s\n") : (isUser ? L"t " : L"t\n"); time_t tt = data.timestamp; localtime_s(&lastTime, &tt); } else { time_t tt = data.timestamp; tm t; localtime_s(&t, &tt); if (lastTime.tm_yday == t.tm_yday && lastTime.tm_year == t.tm_year) formatDate = Options::instance->messagesShowSec ? (isUser ? L"s " : L"s\n") : (isUser ? L"t " : L"t\n"); } } TimeZone_PrintTimeStamp(NULL, data.timestamp, formatDate, str, MAXSELECTSTR, 0); *strLen = (unsigned int)mir_wstrlen(str) * sizeof(wchar_t); TextSelection->SetStart(MAXLONG); TextSelection->GetFont(&TextFont); SetFontFromOptions(TextFont, caps, lastMe ? Options::OutTimestamp : Options::InTimestamp); TextFont->SetBackColor(backColor); TextSelection->SetText(pStr); TextFont->Release(); if (isUser) { mir_snwprintf(str, MAXSELECTSTR, L"%s\n", (lastMe) ? m_myName : m_contactName); *strLen = (unsigned int)mir_wstrlen(str) * sizeof(wchar_t); TextSelection->SetStart(MAXLONG); TextSelection->GetFont(&TextFont); SetFontFromOptions(TextFont, caps, lastMe ? Options::OutName : Options::InName); TextSelection->SetText(pStr); TextFont->Release(); } GetEventMessage(hDbEvent, str); strStl = str; size_t i = strStl.length(); if (i + 1 >= MAXSELECTSTR) continue; str[i++] = '\n'; str[i] = 0; *strLen = (unsigned int)i * sizeof(wchar_t); TextSelection->SetStart(MAXLONG); TextSelection->GetFont(&TextFont); SetFontFromOptions(TextFont, caps, lastMe ? Options::OutMessages : Options::InMessages); TextFont->SetBackColor(backColor); TextSelection->GetStart(&startAt); TextSelection->SetText(pStr); TextFont->Release(); if (Options::instance->messagesUseSmileys) ReplaceIcons(editWindow, startAt, lastMe); TextSelection->SetStart(MAXLONG); TextSelection->GetStart(&endAt); currentGroup.push_back(MessageData(strStl, startAt, endAt, lastMe, data.timestamp)); } } TextSelection->SetRange(0, 0); TextSelection->Release(); TextDocument->Unfreeze(&cnt); TextDocument->Release(); RichEditOle->Release(); SendMessage(editWindow, EM_SETREADONLY, TRUE, 0); SendMessage(editWindow, EM_SETBKGNDCOLOR, 0, backColor); if (cnt == 0) UpdateWindow(editWindow); if (isStartSelect && !Options::instance->messagesNewOnTop) { CHARRANGE ch; ch.cpMin = ch.cpMax = MAXLONG; SendMessage(editWindow, EM_EXSETSEL, 0, (LPARAM)&ch); SendMessage(editWindow, EM_SCROLLCARET, 0, 0); } isStartSelect = false; } LRESULT CALLBACK HistoryWindow::SplitterSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HWND hwndParent = GetParent(hwnd); HistoryWindow *dat = (HistoryWindow*)GetWindowLongPtr(hwndParent, GWLP_USERDATA); if (dat == nullptr) return FALSE; switch (msg) { case WM_NCHITTEST: return HTCLIENT; case WM_SETCURSOR: { RECT rc; GetClientRect(hwnd, &rc); SetCursor(rc.right > rc.bottom ? hCurSplitNS : hCurSplitWE); } return TRUE; case WM_LBUTTONDOWN: SetCapture(hwnd); return 0; case WM_MOUSEMOVE: if (GetCapture() == hwnd) { RECT rc; GetClientRect(hwnd, &rc); SendMessage(hwndParent, DM_SPLITTERMOVED, rc.right > rc.bottom ? (short)HIWORD(GetMessagePos()) + rc.bottom / 2 : (short)LOWORD(GetMessagePos()) + rc.right / 2, (LPARAM)hwnd); } return 0; case WM_ERASEBKGND: return 1; case WM_LBUTTONUP: ReleaseCapture(); SendMessage(hwndParent, WM_SIZE, 0, 0); RedrawWindow(hwndParent, nullptr, nullptr, RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW); return 0; } return mir_callNextSubclass(hwnd, HistoryWindow::SplitterSubclassProc, msg, wParam, lParam); } void HistoryWindow::EnableWindows(BOOL enable) { EnableWindow(GetDlgItem(m_hWnd, IDC_LIST_CONTACTS), enable); EnableWindow(listWindow, enable); EnableWindow(findWindow, enable); EnableWindow(toolbarWindow, enable); isLoading = !enable; } void HistoryWindow::ReloadContacts() { HWND contactList = GetDlgItem(m_hWnd, IDC_LIST_CONTACTS); if (HistoryEventList::GetContactMessageNumber(NULL)) { if (hSystem == NULL) { CLCINFOITEM cii = { sizeof(cii) }; cii.flags = CLCIIF_GROUPFONT | CLCIIF_BELOWCONTACTS; cii.pszText = TranslateT("System"); hSystem = (MCONTACT)SendMessage(contactList, CLM_ADDINFOITEM, 0, (LPARAM)& cii); } } else { if (hSystem != NULL) { SendMessage(contactList, CLM_DELETEITEM, (WPARAM)hSystem, 0); hSystem = NULL; } } for (auto &_hContact : Contacts()) { if (HistoryEventList::GetContactMessageNumber(_hContact) && (metaContactProto == nullptr || !db_mc_isSub(_hContact))) { HANDLE hItem = (HANDLE)SendMessage(contactList, CLM_FINDCONTACT, (WPARAM)_hContact, 0); if (hItem == nullptr) SendMessage(contactList, CLM_ADDCONTACT, (WPARAM)_hContact, 0); } else { HANDLE hItem = (HANDLE)SendMessage(contactList, CLM_FINDCONTACT, (WPARAM)_hContact, 0); if (hItem != nullptr) SendMessage(contactList, CLM_DELETEITEM, (WPARAM)_hContact, 0); } } if (m_hContact != NULL) { HANDLE hItem = (HANDLE)SendMessage(contactList, CLM_FINDCONTACT, m_hContact, 0); if (hItem != nullptr) { SendMessage(contactList, CLM_ENSUREVISIBLE, (WPARAM)hItem, 0); SendMessage(contactList, CLM_SELECTITEM, (WPARAM)hItem, 0); } } else if (hSystem != NULL) { SendMessage(contactList, CLM_ENSUREVISIBLE, (WPARAM)hSystem, 0); SendMessage(contactList, CLM_SELECTITEM, (WPARAM)hSystem, 0); } } bool HistoryWindow::DoHotkey(UINT msg, LPARAM lParam, WPARAM wParam, int window) { MSG message; message.hwnd = m_hWnd; message.message = msg; message.lParam = lParam; message.wParam = wParam; LRESULT mim_hotkey_check = Hotkey_Check(&message, "History"); switch (mim_hotkey_check) { case HISTORY_HK_FIND: SetFocus(findWindow); Edit_SetSel(findWindow, 0, -1); break; case HISTORY_HK_FINDNEXT: searcher.ChangeFindDirection(false); break; case HISTORY_HK_FINDPREV: searcher.ChangeFindDirection(true); break; case HISTORY_HK_MATCHCASE: searcher.SetMatchCase(!searcher.IsMatchCase()); break; case HISTORY_HK_MATCHWHOLE: searcher.SetMatchWholeWords(!searcher.IsMatchWholeWords()); break; case HISTORY_HK_SHOWCONTACTS: Button_SetCheck(GetDlgItem(m_hWnd, IDC_SHOWHIDE), Button_GetCheck(GetDlgItem(m_hWnd, IDC_SHOWHIDE)) & BST_CHECKED ? BST_UNCHECKED : BST_CHECKED); SendMessage(m_hWnd, WM_COMMAND, MAKELONG(IDC_SHOWHIDE, BN_CLICKED), NULL); break; case HISTORY_HK_ONLYIN: searcher.SetOnlyIn(!searcher.IsOnlyIn()); break; case HISTORY_HK_ONLYOUT: searcher.SetOnlyOut(!searcher.IsOnlyOut()); break; case HISTORY_HK_ONLYGROUP: searcher.SetOnlyGroup(!searcher.IsOnlyGroup()); break; case HISTORY_HK_ALLCONTACTS: searcher.SetAllUsers(!searcher.IsAllUsers()); break; case HISTORY_HK_EXRHTML: ExportManager(m_hWnd, m_hContact, GetFilterNr()).Export(IExport::RichHtml); break; case HISTORY_HK_EXPHTML: ExportManager(m_hWnd, m_hContact, GetFilterNr()).Export(IExport::PlainHtml); break; case HISTORY_HK_EXTXT: ExportManager(m_hWnd, m_hContact, GetFilterNr()).Export(IExport::Txt); break; case HISTORY_HK_EXBIN: ExportManager(m_hWnd, m_hContact, GetFilterNr()).Export(IExport::Binary); break; case HISTORY_HK_EXDAT: ExportManager(m_hWnd, m_hContact, GetFilterNr()).Export(IExport::Dat); break; case HISTORY_HK_IMPBIN: DoImport(IImport::Binary); break; case HISTORY_HK_IMPDAT: DoImport(IImport::Dat); break; case HISTORY_HK_DELETE: { int what = window == IDC_EDIT ? 0 : (window == IDC_LIST ? 1 : (window == IDC_LIST_CONTACTS ? 2 : -1)); Delete(what); return what != -1; } break; default: return false; } return true; } void HistoryWindow::RestorePos() { MCONTACT contactToLoad = m_hContact; if (m_hContact == NULL) { Utils_RestoreWindowPosition(m_hWnd, NULL, MODULENAME, "history_"); contactToLoad = NULL; } else if (Utils_RestoreWindowPosition(m_hWnd, m_hContact, MODULENAME, "history_") != 0) { Utils_RestoreWindowPosition(m_hWnd, NULL, MODULENAME, "history_"); contactToLoad = NULL; } if (g_plugin.getByte(contactToLoad, "history_ismax")) ShowWindow(m_hWnd, SW_SHOWMAXIMIZED); LONG pos = g_plugin.getDword(contactToLoad, "history_splitterv"); if (pos > 0) SplitterMoved(splitterYhWnd, pos, false); pos = g_plugin.getDword(contactToLoad, "history_splitter"); if (pos > 0) SplitterMoved(splitterXhWnd, pos, false); } void HistoryWindow::SavePos(bool all) { MCONTACT contactToSave = m_hContact; if (all) { for (auto &_hContact : Contacts()) { g_plugin.delSetting(_hContact, "history_x"); g_plugin.delSetting(_hContact, "history_y"); g_plugin.delSetting(_hContact, "history_width"); g_plugin.delSetting(_hContact, "history_height"); g_plugin.delSetting(_hContact, "history_ismax"); g_plugin.delSetting(_hContact, "history_splitterv"); g_plugin.delSetting(_hContact, "history_splitter"); } contactToSave = NULL; } Utils_SaveWindowPosition(m_hWnd, contactToSave, MODULENAME, "history_"); WINDOWPLACEMENT wp; wp.length = sizeof(wp); GetWindowPlacement(m_hWnd, &wp); g_plugin.setByte(contactToSave, "history_ismax", wp.showCmd == SW_MAXIMIZE ? 1 : 0); g_plugin.setDword(contactToSave, "history_splitterv", splitterX); g_plugin.setDword(contactToSave, "history_splitter", splitterY); } #define DEF_FILTERS_START 50000 void HistoryWindow::FindToolbarClicked(LPNMTOOLBAR lpnmTB) { RECT rc; SendMessage(lpnmTB->hdr.hwndFrom, TB_GETRECT, (WPARAM)lpnmTB->iItem, (LPARAM)&rc); MapWindowPoints(lpnmTB->hdr.hwndFrom, HWND_DESKTOP, (LPPOINT)&rc, 2); HMENU hPopupMenu = CreatePopupMenu(); if (hPopupMenu != nullptr) { AppendMenu(hPopupMenu, MF_STRING, IDM_FINDNEXT, TranslateT("Find next")); AppendMenu(hPopupMenu, MF_STRING, IDM_FINDPREV, TranslateT("Find previous")); AppendMenu(hPopupMenu, MFT_SEPARATOR, 0, nullptr); AppendMenu(hPopupMenu, searcher.IsMatchCase() ? MF_STRING | MF_CHECKED : MF_STRING, IDM_MATCHCASE, TranslateT("Match case")); AppendMenu(hPopupMenu, searcher.IsMatchWholeWords() ? MF_STRING | MF_CHECKED : MF_STRING, IDM_MATCHWHOLE, TranslateT("Match whole word")); AppendMenu(hPopupMenu, searcher.IsOnlyIn() ? MF_STRING | MF_CHECKED : MF_STRING, IDM_ONLYIN, TranslateT("Only incoming messages")); AppendMenu(hPopupMenu, searcher.IsOnlyOut() ? MF_STRING | MF_CHECKED : MF_STRING, IDM_ONLYOUT, TranslateT("Only outgoing messages")); AppendMenu(hPopupMenu, searcher.IsOnlyGroup() ? MF_STRING | MF_CHECKED : MF_STRING, IDM_ONLYGROUP, TranslateT("Only selected group")); AppendMenu(hPopupMenu, searcher.IsAllUsers() ? MF_STRING | MF_CHECKED : MF_STRING, IDM_ALLUSERS, TranslateT("All contacts")); AppendMenu(hPopupMenu, MFT_SEPARATOR, 0, nullptr); HMENU hFilterMenu = CreatePopupMenu(); uint32_t filter = GetFilterNr(); AppendMenu(hFilterMenu, filter == 0 ? MF_STRING | MF_CHECKED : MF_STRING, IDM_FILTERDEF, TranslateT("Default history events")); AppendMenu(hFilterMenu, filter == 1 ? MF_STRING | MF_CHECKED : MF_STRING, IDM_FILTERALL, TranslateT("All events")); for (size_t i = 0; i < Options::instance->customFilters.size(); ++i) { uint32_t flags = MF_STRING; if (filter - 2 == i) flags |= MF_CHECKED; AppendMenu(hFilterMenu, flags, DEF_FILTERS_START + i, Options::instance->customFilters[i].name.c_str()); } AppendMenu(hPopupMenu, MF_STRING | MF_POPUP, (UINT_PTR)hFilterMenu, TranslateT("Filters")); if (searcher.IsFindBack()) SetMenuDefaultItem(hPopupMenu, IDM_FINDPREV, FALSE); else SetMenuDefaultItem(hPopupMenu, IDM_FINDNEXT, FALSE); int iResult = TrackPopupMenu(hPopupMenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hWnd, nullptr); switch (iResult) { case IDM_FINDNEXT: searcher.ChangeFindDirection(false); break; case IDM_FINDPREV: searcher.ChangeFindDirection(true); break; case IDM_MATCHCASE: searcher.SetMatchCase(!searcher.IsMatchCase()); break; case IDM_MATCHWHOLE: searcher.SetMatchWholeWords(!searcher.IsMatchWholeWords()); break; case IDM_ONLYIN: searcher.SetOnlyIn(!searcher.IsOnlyIn()); break; case IDM_ONLYOUT: searcher.SetOnlyOut(!searcher.IsOnlyOut()); break; case IDM_ONLYGROUP: searcher.SetOnlyGroup(!searcher.IsOnlyGroup()); break; case IDM_ALLUSERS: searcher.SetAllUsers(!searcher.IsAllUsers()); break; case IDM_FILTERDEF: SetDefFilter(0); SendMessage(m_hWnd, DM_HREBUILD, 0, 0); break; case IDM_FILTERALL: SetDefFilter(1); SendMessage(m_hWnd, DM_HREBUILD, 0, 0); break; default: if (iResult >= DEF_FILTERS_START) { SetDefFilter(iResult - DEF_FILTERS_START + 2); SendMessage(m_hWnd, DM_HREBUILD, 0, 0); } break; } DestroyMenu(hFilterMenu); DestroyMenu(hPopupMenu); } } void HistoryWindow::ConfigToolbarClicked(LPNMTOOLBAR lpnmTB) { RECT rc; SendMessage(lpnmTB->hdr.hwndFrom, TB_GETRECT, (WPARAM)lpnmTB->iItem, (LPARAM)&rc); MapWindowPoints(lpnmTB->hdr.hwndFrom, HWND_DESKTOP, (LPPOINT)&rc, 2); HMENU hPopupMenu = CreatePopupMenu(); if (hPopupMenu != nullptr) { AppendMenu(hPopupMenu, MF_STRING, IDM_OPTIONS, TranslateT("Options")); AppendMenu(hPopupMenu, MF_STRING, IDM_FONTS, TranslateT("Fonts and colors")); AppendMenu(hPopupMenu, MF_STRING, IDM_ICONS, TranslateT("Icons")); AppendMenu(hPopupMenu, MF_STRING, IDM_HOTKEYS, TranslateT("Hotkeys")); AppendMenu(hPopupMenu, MFT_SEPARATOR, 0, nullptr); HMENU hExportMenu = CreatePopupMenu(); AppendMenu(hExportMenu, MF_STRING, IDM_EXPORTRHTML, TranslateT("Rich HTML")); AppendMenu(hExportMenu, MF_STRING, IDM_EXPORTPHTML, TranslateT("Plain HTML")); AppendMenu(hExportMenu, MF_STRING, IDM_EXPORTTXT, TranslateT("Txt")); AppendMenu(hExportMenu, MF_STRING, IDM_EXPORTBINARY, TranslateT("Binary")); AppendMenu(hExportMenu, MF_STRING, IDM_EXPORTDAT, TranslateT("Dat (mContacts)")); HMENU hImportMenu = CreatePopupMenu(); AppendMenu(hImportMenu, MF_STRING, IDM_IMPORTBINARY, TranslateT("Binary")); AppendMenu(hImportMenu, MF_STRING, IDM_IMPORTDAT, TranslateT("Dat (mContacts)")); AppendMenu(hPopupMenu, MF_STRING | MF_POPUP, (UINT_PTR)hExportMenu, TranslateT("Export")); AppendMenu(hPopupMenu, MF_STRING | MF_POPUP, (UINT_PTR)hImportMenu, TranslateT("Import")); AppendMenu(hPopupMenu, MFT_SEPARATOR, 0, nullptr); AppendMenu(hPopupMenu, MF_STRING, IDM_SAVEPOS, TranslateT("Save window position as default")); AppendMenu(hPopupMenu, MF_STRING, IDM_SAVEPOSALL, TranslateT("Save window position for all contacts")); SetMenuDefaultItem(hPopupMenu, IDM_OPTIONS, FALSE); int iResult = TrackPopupMenu(hPopupMenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hWnd, nullptr); switch (iResult) { case IDM_OPTIONS: SendMessage(m_hWnd, WM_COMMAND, IDM_CONFIG, 0); break; case IDM_FONTS: g_plugin.openOptions(L"Customize", L"Fonts and colors"); break; case IDM_ICONS: g_plugin.openOptions(L"Icons"); break; case IDM_HOTKEYS: g_plugin.openOptions(L"Customize", L"Hotkeys"); break; case IDM_SAVEPOS: SavePos(false); break; case IDM_SAVEPOSALL: SavePos(true); break; case IDM_EXPORTRHTML: ExportManager(m_hWnd, m_hContact, GetFilterNr()).Export(IExport::RichHtml); break; case IDM_EXPORTPHTML: ExportManager(m_hWnd, m_hContact, GetFilterNr()).Export(IExport::PlainHtml); break; case IDM_EXPORTTXT: ExportManager(m_hWnd, m_hContact, GetFilterNr()).Export(IExport::Txt); break; case IDM_EXPORTBINARY: ExportManager(m_hWnd, m_hContact, GetFilterNr()).Export(IExport::Binary); break; case IDM_EXPORTDAT: ExportManager(m_hWnd, m_hContact, GetFilterNr()).Export(IExport::Dat); break; case IDM_IMPORTBINARY: DoImport(IImport::Binary); break; case IDM_IMPORTDAT: DoImport(IImport::Dat); break; } DestroyMenu(hExportMenu); DestroyMenu(hImportMenu); DestroyMenu(hPopupMenu); } } void HistoryWindow::DoImport(IImport::ImportType type) { ExportManager exp(m_hWnd, m_hContact, GetFilterNr()); std::vector messages; std::wstring err; std::vector contacts; for (auto &_hContact : Contacts()) contacts.push_back(_hContact); bool changeContact = false; MCONTACT lastContact = m_hContact; int i = 1; do { bool differentContact = false; if (exp.Import(type, messages, &err, &differentContact, &contacts)) { int act = MessageBox(m_hWnd, TranslateT("Do you want to save imported messages to local profile?"), TranslateT("Import"), MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON2); if (act == IDYES) { MargeMessages(messages); if (!changeContact) HistoryWindow::RebuildEvents(m_hContact); } else if (act == IDNO) { HistoryEventList::AddImporter(m_hContact, type, exp.GetFileName()); if (!changeContact) HistoryWindow::RebuildEvents(m_hContact); } } else if (differentContact) { int act = MessageBox(m_hWnd, TranslateT("File contains history for different contact. Do you want to change contact and import?"), TranslateT("Error"), MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2); if (act == IDYES) { changeContact = true; m_hContact = exp.m_hContact; } } else if (!err.empty()) MessageBox(m_hWnd, err.c_str(), TranslateT("Error"), MB_ICONERROR); } while (changeContact && i--); if (changeContact) { m_hContact = lastContact; ReloadContacts(); SelectContact(exp.m_hContact); HistoryWindow::RebuildEvents(exp.m_hContact); } } void HistoryWindow::DeleteToolbarClicked(LPNMTOOLBAR lpnmTB) { RECT rc; SendMessage(lpnmTB->hdr.hwndFrom, TB_GETRECT, (WPARAM)lpnmTB->iItem, (LPARAM)&rc); MapWindowPoints(lpnmTB->hdr.hwndFrom, HWND_DESKTOP, (LPPOINT)&rc, 2); HMENU hPopupMenu = CreatePopupMenu(); if (hPopupMenu != nullptr) { AppendMenu(hPopupMenu, MF_STRING, IDM_DELETE, TranslateT("Delete")); AppendMenu(hPopupMenu, MF_STRING, IDM_DELETEGROUP, TranslateT("Delete group")); AppendMenu(hPopupMenu, MF_STRING, IDM_DELETEUSER, TranslateT("Delete all contact history")); SetMenuDefaultItem(hPopupMenu, IDM_DELETE, FALSE); int iResult = TrackPopupMenu(hPopupMenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hWnd, nullptr); switch (iResult) { case IDM_DELETE: Delete(0); break; case IDM_DELETEGROUP: Delete(1); break; case IDM_DELETEUSER: Delete(2); break; } DestroyMenu(hPopupMenu); } } void HistoryWindow::Delete(int what) { int toDelete = 1; size_t start = 0; size_t end = 0; if (selected < 0 || selected >= (int)m_eventList.size() || what > 2 || what < 0) return; if (what == 0) { CHARRANGE chrg; SendMessage(editWindow, EM_EXGETSEL, 0, (LPARAM)&chrg); if (chrg.cpMin == 0 && chrg.cpMax == -1) toDelete = (int)currentGroup.size(); else { while (start < currentGroup.size() && chrg.cpMin >= currentGroup[start].endPos) ++start; end = start; while (end < currentGroup.size() && chrg.cpMax > currentGroup[end].endPos) ++end; if (start >= currentGroup.size()) return; if (end < currentGroup.size()) ++end; toDelete = (int)(end - start); } } else if (what == 1) { end = currentGroup.size(); toDelete = (int)end; } else { CallService(MS_HISTORY_EMPTY, m_hContact, 0); return; } if (toDelete == 0) return; wchar_t message[256]; mir_snwprintf(message, TranslateT("Number of history items to delete: %d.\nAre you sure you want to do this?"), toDelete); if (MessageBox(m_hWnd, message, TranslateT("Are you sure?"), MB_OKCANCEL | MB_ICONERROR) != IDOK) return; bool areImpMessages = false; bool rebuild = false; for (size_t i = start; i < end; ++i) { EventIndex& ev = m_eventList[selected][i]; DeleteEvent(ev); areImpMessages |= ev.isExternal; } rebuild = (start == 0 && end == currentGroup.size()); if (areImpMessages) { wchar_t *ptszMsg = TranslateT("Do you want to delete all imported messages for this contact?\nNote that next scheduler task import this messages again."); if (MessageBox(m_hWnd, ptszMsg, TranslateT("Are you sure?"), MB_YESNO | MB_ICONERROR) == IDYES) { HistoryEventList::DeleteImporter(m_hContact); rebuild = true; } } if (rebuild) RebuildEvents(m_hContact); else { RebuildGroup(selected); SelectEventGroup(selected); } } bool HistoryWindow::ContactChanged(bool sync) { if (!isLoading) { MCONTACT hItem = (MCONTACT)SendDlgItemMessage(m_hWnd, IDC_LIST_CONTACTS, CLM_GETSELECTION, 0, 0); if (hItem != NULL) { int typeOf = SendDlgItemMessage(m_hWnd, IDC_LIST_CONTACTS, CLM_GETITEMTYPE, (WPARAM)hItem, 0); if (typeOf == CLCIT_CONTACT) { if (m_hContact != hItem) { ChangeToFreeWindow(this); isLoading = true; m_hContact = hItem; ReloadContacts(); if (sync) FillHistoryThread(this); else mir_forkThread(HistoryWindow::FillHistoryThread, this); return true; } } else if (typeOf == CLCIT_INFO && hSystem == hItem) { if (m_hContact != NULL) { ChangeToFreeWindow(this); isLoading = true; m_hContact = NULL; ReloadContacts(); if (sync) FillHistoryThread(this); else mir_forkThread(HistoryWindow::FillHistoryThread, this); return true; } } } } return false; } void HistoryWindow::GroupImagesChanged() { if (isGroupImages != Options::instance->groupShowEvents) { isGroupImages = Options::instance->groupShowEvents; if (isGroupImages) ListView_SetImageList(listWindow, himlSmall, LVSIL_SMALL); else ListView_SetImageList(listWindow, himlNone, LVSIL_SMALL); } } void HistoryWindow::FormatQuote(std::wstring "e, const MessageData &md, const std::wstring &msg) { if (md.isMe) quote += m_myName; else quote += m_contactName; wchar_t str[32]; TimeZone_PrintTimeStamp(nullptr, md.timestamp, L"d t", str, 32, 0); quote += L", "; quote += str; quote += L"\n"; int f = 0; do { int nf = (int)msg.find_first_of(L"\r\n", f); if (nf >= 0 && nf < (int)msg.length()) { if (nf - f >= 0) { quote += L">"; quote += msg.substr(f, nf - f); quote += L"\n"; } f = nf + 1; if (msg[nf] == '\r' && f < (int)msg.length() && msg[f] == '\n') ++f; } else if (msg.length() - f > 0) { quote += L">"; quote += msg.substr(f, msg.length() - f); quote += L"\n"; f = -1; } else f = -1; } while (f > 0 && f < (int)msg.length()); } MCONTACT HistoryWindow::GetNextContact(MCONTACT hContact, int adder) { HWND contactList = GetDlgItem(m_hWnd, IDC_LIST_CONTACTS); if (adder > 0) { if (hContact != NULL) { for (auto &cc : Contacts()) if (SendMessage(contactList, CLM_FINDCONTACT, cc, 0) != NULL) return cc; if (HistoryEventList::GetContactMessageNumber(NULL)) return NULL; } for (auto &cc : Contacts()) { if (cc == hContact) break; if (SendMessage(contactList, CLM_FINDCONTACT, cc, 0) != NULL) return cc; } } else { MCONTACT lastContact = NULL; for (auto &cc : Contacts()) { if (cc == hContact) break; if (SendMessage(contactList, CLM_FINDCONTACT, cc, 0) != NULL) lastContact = cc; } if (hContact != NULL) { if (lastContact == NULL && !HistoryEventList::GetContactMessageNumber(NULL)) { for (auto &cc : Contacts()) if (SendMessage(contactList, CLM_FINDCONTACT, cc, 0) != NULL) lastContact = cc; } if (lastContact != NULL || HistoryEventList::GetContactMessageNumber(NULL)) return lastContact; } else if (lastContact != NULL) return lastContact; } return hContact; } void HistoryWindow::SelectContact(MCONTACT hContact) { if (m_hContact != hContact) { HWND contactList = GetDlgItem(m_hWnd, IDC_LIST_CONTACTS); if (hContact != NULL) { HANDLE hItem = (HANDLE)SendMessage(contactList, CLM_FINDCONTACT, hContact, 0); if (hItem != nullptr) { SendMessage(contactList, CLM_ENSUREVISIBLE, (WPARAM)hItem, 0); SendMessage(contactList, CLM_SELECTITEM, (WPARAM)hItem, 0); } } else { SendMessage(contactList, CLM_ENSUREVISIBLE, (WPARAM)hSystem, 0); SendMessage(contactList, CLM_SELECTITEM, (WPARAM)hSystem, 0); } while (isLoading) Sleep(100); ContactChanged(true); } } void ResetCList(HWND hWnd) { COLORREF bkCLColor = Options::instance->GetColor(Options::ContactListBackground); SendDlgItemMessage(hWnd, IDC_LIST_CONTACTS, CLM_SETBKCOLOR, bkCLColor, 0); SendDlgItemMessage(hWnd, IDC_LIST_CONTACTS, CLM_SETUSEGROUPS, Options::instance->showContactGroups, 0); }