From d109da9838f3f3ec4872612e8da04c75671905f5 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Tue, 7 Apr 2020 16:45:57 +0300 Subject: NewStory: code cleaning --- plugins/NewStory/src/history.cpp | 46 +- plugins/NewStory/src/history.h | 5 +- plugins/NewStory/src/history_control.cpp | 1446 +++++++++++++++--------------- plugins/NewStory/src/history_control.h | 3 + 4 files changed, 741 insertions(+), 759 deletions(-) (limited to 'plugins/NewStory') diff --git a/plugins/NewStory/src/history.cpp b/plugins/NewStory/src/history.cpp index 8cb8868891..143246d1be 100644 --- a/plugins/NewStory/src/history.cpp +++ b/plugins/NewStory/src/history.cpp @@ -220,7 +220,7 @@ class CHistoryDlg : public CDlgBase // main controls HWND m_hwndTimeTree; - HWND m_hwndLog; + // searchbar HWND m_hwndBtnCloseSearch; // statusbar @@ -386,7 +386,7 @@ class CHistoryDlg : public CDlgBase hSearch += WND_SPACING; } - hDwp = DeferWindowPos(hDwp, m_hwndLog, 0, + hDwp = DeferWindowPos(hDwp, m_histControl.GetHwnd(), 0, WND_SPACING, hToolBar + hFilterBar + WND_SPACING, w - WND_SPACING * 2, h - WND_SPACING * 2 - hFilterBar - hToolBar - hSearch - hStatus, SWP_NOZORDER); @@ -394,6 +394,7 @@ class CHistoryDlg : public CDlgBase EndDeferWindowPos(hDwp); } + CCtrlBase m_histControl; CCtrlEdit edtSearchText; CCtrlMButton btnUserInfo, btnSendMsg, btnUserMenu, btnCopy, btnOptions, btnFilter; CCtrlMButton btnCalendar, btnSearch, btnExport, btnClose, btnFindNext, btnFindPrev; @@ -404,6 +405,7 @@ public: CDlgBase(g_plugin, IDD_HISTORY), m_hContact(_hContact), m_timeTree(this, IDC_TIMETREE), + m_histControl(this, IDC_ITEMS2), edtSearchText(this, IDC_SEARCHTEXT), btnCopy(this, IDC_COPY, g_plugin.getIcon(ICO_COPY), LPGEN("Copy")), btnClose(this, IDC_CLOSE, g_plugin.getIcon(ICO_CLOSE), LPGEN("Close")), @@ -475,7 +477,6 @@ public: m_hwndBtnToolbar[TBTN_DATEPOPUP] = btnCalendar.GetHwnd(); m_hwndBtnToolbar[TBTN_CLOSE] = btnClose.GetHwnd(); - m_hwndLog = GetDlgItem(m_hwnd, IDC_ITEMS2); m_hwndBtnCloseSearch = GetDlgItem(m_hwnd, IDC_SEARCHICON); m_hwndStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0, m_hwnd, NULL, g_plugin.getInst(), NULL); SendMessage(m_hwndStatus, SB_SETMINHEIGHT, GetSystemMetrics(SM_CYSMICON), 0); @@ -560,9 +561,9 @@ public: } if (m_hContact != INVALID_CONTACT_ID) - PostMessage(GetDlgItem(m_hwnd, IDC_ITEMS2), WM_USER, (WPARAM)m_hContact, 0); + PostMessage(m_histControl.GetHwnd(), WM_USER, m_hContact, 0); - SendMessage(m_hwnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)g_plugin.getIcon(ICO_NEWSTORY)); + Window_SetIcon_IcoLib(m_hwnd, g_plugin.getIconHandle(ICO_NEWSTORY)); SendMessage(GetDlgItem(m_hwnd, IDC_SEARCHICON), STM_SETICON, (WPARAM)g_plugin.getIcon(ICO_SEARCH), 0); @@ -582,7 +583,7 @@ public: SendMessage(ibTotal.hwndIcoIn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(ICO_MSGIN)); SendMessage(ibTotal.hwndIcoOut, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(ICO_MSGOUT)); - SetFocus(GetDlgItem(m_hwnd, IDC_ITEMS2)); + SetFocus(m_histControl.GetHwnd()); ShowHideControls(); return true; @@ -590,11 +591,11 @@ public: void OnDestroy() override { - WindowList_Remove(hNewstoryWindows, m_hwnd); - g_plugin.setDword(m_hContact, "showFlags", showFlags); Utils_SaveWindowPosition(m_hwnd, m_hContact, MODULENAME, "wnd_"); + Window_FreeIcon_IcoLib(m_hwnd); + WindowList_Remove(hNewstoryWindows, m_hwnd); } void onClick_Calendar(CCtrlButton *pButton) @@ -604,7 +605,7 @@ public: time_t tm_jump = CalendarTool_Show(m_hwnd, rc.left, rc.bottom); if (tm_jump) - PostMessage(m_hwnd, UM_JUMP2TIME, tm_jump, 0); + m_histControl.SendMsg(NSM_SEEKTIME, tm_jump, 0); } void onClick_Close(CCtrlButton *) @@ -614,7 +615,7 @@ public: void onClick_Copy(CCtrlButton *) { - SendMessage(GetDlgItem(m_hwnd, IDC_ITEMS2), NSM_COPY, 0, 0); + m_histControl.SendMsg(NSM_COPY, 0, 0); } void onClick_Export(CCtrlButton *) @@ -636,12 +637,12 @@ public: void onClick_FindNext(CCtrlButton *) { - SendMessage(GetDlgItem(m_hwnd, IDC_ITEMS2), NSM_FINDNEXT, ptrW(edtSearchText.GetText()), 0); + m_histControl.SendMsg(NSM_FINDNEXT, ptrW(edtSearchText.GetText()), 0); } void onClick_FindPrev(CCtrlButton *) { - SendMessage(GetDlgItem(m_hwnd, IDC_ITEMS2), NSM_FINDPREV, ptrW(edtSearchText.GetText()), 0); + m_histControl.SendMsg(NSM_FINDPREV, ptrW(edtSearchText.GetText()), 0); } @@ -709,7 +710,7 @@ public: INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override { if ((msg >= NSM_FIRST) && (msg < NSM_LAST)) { - LPARAM result = SendMessage(GetDlgItem(m_hwnd, IDC_ITEMS2), msg, wParam, lParam); + LPARAM result = m_histControl.SendMsg(msg, wParam, lParam); SetWindowLongPtr(m_hwnd, DWLP_MSGRESULT, result); return result; } @@ -858,25 +859,6 @@ public: // ShowWindow(GetDlgItem(m_hwnd, IDC_TIMETREE), SW_SHOW); // ShowWindow(GetDlgItem(m_hwnd, IDC_ITEMS2), SW_SHOW); // ShowWindow(GetDlgItem(m_hwnd, IDC_SEARCHICON), SW_SHOW); - - /* - case UM_JUMP2TIME: - { - for (int i = 0; i < eventCount; i++) - { - ItemData *idata = (ItemData *)SendMessage(GetDlgItem(m_hwnd, IDC_ITEMS), LB_GETITEMDATA, i, 0); - if (idbe->timestamp >= wParam) - { - SendMessage(GetDlgItem(m_hwnd, IDC_ITEMS), LB_SETCARETINDEX, i, 0); - SendMessage(GetDlgItem(m_hwnd, IDC_ITEMS), LB_SETTOPINDEX, i, 0); - SendMessage(GetDlgItem(m_hwnd, IDC_ITEMS), LB_SELITEMRANGE, FALSE, MAKELPARAM(0,eventCount)); - SendMessage(GetDlgItem(m_hwnd, IDC_ITEMS), LB_SELITEMRANGE, TRUE, MAKELPARAM(i,i)); - break; - } - } - return TRUE; - } - */ }; INT_PTR svcShowNewstory(WPARAM hContact, LPARAM) diff --git a/plugins/NewStory/src/history.h b/plugins/NewStory/src/history.h index ff0104c790..00e9ccfded 100644 --- a/plugins/NewStory/src/history.h +++ b/plugins/NewStory/src/history.h @@ -24,10 +24,7 @@ enum UM_GETEVENT, UM_GETEVENTTEXT, UM_GETEVENTCONTACT, - UM_GETEVENTHANDLE, - // UM_GETEVENTDATA, - - UM_JUMP2TIME + UM_GETEVENTHANDLE }; extern MWindowList hNewstoryWindows; diff --git a/plugins/NewStory/src/history_control.cpp b/plugins/NewStory/src/history_control.cpp index d7fb75cc37..da8dcecd5b 100644 --- a/plugins/NewStory/src/history_control.cpp +++ b/plugins/NewStory/src/history_control.cpp @@ -1,23 +1,7 @@ #include "stdafx.h" -LRESULT CALLBACK NewstoryListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); - HANDLE htuLog = 0; -void InitNewstoryControl() -{ - htuLog = MTextRegister("Newstory", MTEXT_FANCY_DEFAULT | MTEXT_SYSTEM_HICONS); - - WNDCLASS wndclass = {}; - wndclass.style = /*CS_HREDRAW | CS_VREDRAW | */CS_DBLCLKS | CS_GLOBALCLASS; - wndclass.lpfnWndProc = NewstoryListWndProc; - wndclass.cbWndExtra = sizeof(void *); - wndclass.hInstance = g_plugin.getInst(); - wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); - wndclass.lpszClassName = _T(NEWSTORYLIST_CLASS); - RegisterClass(&wndclass); -} - ///////////////////////////////////////////////////////////////////////// // Control utilities, types and constants @@ -25,11 +9,11 @@ struct NewstoryListData : public MZeroedObject { HistoryArray items; - DWORD scrollTopItem; // topmost item + int scrollTopItem; // topmost item int scrollTopPixel; // y coord of topmost item, this should be negative or zero - DWORD caret; + int caret; int cachedWindowHeight; - DWORD cachedMaxTopItem; // the largest ID of top item to avoid empty space + int cachedMaxTopItem; // the largest ID of top item to avoid empty space int cachedMaxTopPixel; RECT rcLastPaint; @@ -40,13 +24,6 @@ struct NewstoryListData : public MZeroedObject #define AVERAGE_ITEM_HEIGHT 100 -//static HFONT CopyFont(HFONT hfnt) -//{ -// LOGFONT lf; -// GetObject(hfnt, sizeof(lf), &lf); -// return CreateFontIndirect(&lf); -//} - static void ScrollListBy(HWND hwnd, NewstoryListData *data, int scrollItems, int scrollPixels); static void EnsureVisible(HWND hwnd, NewstoryListData *data, int item); static void FixScrollPosition(HWND hwnd, NewstoryListData *data); @@ -56,7 +33,6 @@ static void EndEditItem(HWND hwnd, NewstoryListData *data); static int LayoutItem(HWND hwnd, HistoryArray *items, int index); static int PaintItem(HDC hdc, HistoryArray *items, int index, int top, int width); - // Edit box static WNDPROC OldEditWndProc; static LRESULT CALLBACK HistoryEditWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) @@ -94,817 +70,841 @@ static LRESULT CALLBACK HistoryEditWndProc(HWND hwnd, UINT msg, WPARAM wParam, L } ///////////////////////////////////////////////////////////////////////// -// WndProc -LRESULT CALLBACK NewstoryListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - NewstoryListData *data = (NewstoryListData *)GetWindowLongPtr(hwnd, 0); - - switch (msg) { - case WM_CREATE: - data = new NewstoryListData; - SetWindowLongPtr(hwnd, 0, (LONG_PTR)data); - RecalcScrollBar(hwnd, data); - break; - - case WM_USER: - data->items.addHistory((MCONTACT)wParam); - RecalcScrollBar(hwnd, data); - data->scrollTopItem = data->items.getCount(); - FixScrollPosition(hwnd, data); - InvalidateRect(hwnd, 0, FALSE); - break; - - // History list control messages - case NSM_GETCOUNT: - return data->items.getCount(); - - case NSM_SELECTITEMS: - { - int start = min(data->items.getCount() - 1, max(0, wParam)); - int end = min(data->items.getCount() - 1, max(0, lParam)); - if (start > end) { - start ^= end; - end ^= start; - start ^= end; - } - for (int i = start; i <= end; ++i) - data->items.get(i, ELM_NOTHING)->flags |= HIF_SELECTED; - InvalidateRect(hwnd, 0, FALSE); - return 0; - } - - case NSM_TOGGLEITEMS: - { - int start = min(data->items.getCount() - 1, max(0, wParam)); - int end = min(data->items.getCount() - 1, max(0, lParam)); - if (start > end) { - start ^= end; - end ^= start; - start ^= end; - } - for (int i = start; i <= end; ++i) { - if (data->items.get(i, ELM_NOTHING)->flags & HIF_SELECTED) { - data->items.get(i, ELM_NOTHING)->flags &= ~HIF_SELECTED; - } - else { - data->items.get(i, ELM_NOTHING)->flags |= HIF_SELECTED; - } - } - InvalidateRect(hwnd, 0, FALSE); - return 0; - } - - case NSM_SELECTITEMS2: - { - int start = min(data->items.getCount() - 1, max(0, wParam)); - int end = min(data->items.getCount() - 1, max(0, lParam)); - if (start > end) { - start ^= end; - end ^= start; - start ^= end; - } - int count = data->items.getCount(); - for (int i = 0; i < count; ++i) { - if ((i >= start) && (i <= end)) { - data->items.get(i, ELM_NOTHING)->flags |= HIF_SELECTED; - } - else { - data->items.get(i, ELM_NOTHING)->flags &= ~((DWORD)HIF_SELECTED); - } - } - InvalidateRect(hwnd, 0, FALSE); - return 0; - } +// Utilities - case NSM_DESELECTITEMS: - { - int start = min(data->items.getCount() - 1, max(0, wParam)); - int end = min(data->items.getCount() - 1, max(0, lParam)); - if (start > end) { - start ^= end; - end ^= start; - start ^= end; +static void ScrollListBy(HWND hwnd, NewstoryListData *data, int scrollItems, int scrollPixels) +{ + if (scrollItems) { + data->scrollTopItem += scrollItems; + data->scrollTopPixel = 0; + } + else if (scrollPixels) { + data->scrollTopPixel += scrollPixels; + if (data->scrollTopPixel > 0) { + while ((data->scrollTopPixel > 0) && data->scrollTopItem) { + data->scrollTopItem--; + int itemHeight = LayoutItem(hwnd, &data->items, data->scrollTopItem); + data->scrollTopPixel -= itemHeight; } - for (int i = start; i <= end; ++i) - data->items.get(i, ELM_NOTHING)->flags &= ~((DWORD)HIF_SELECTED); - InvalidateRect(hwnd, 0, FALSE); - return 0; - } - - case NSM_ENSUREVISIBLE: - EnsureVisible(hwnd, data, wParam); - return 0; - case NSM_GETITEMFROMPIXEL: - { - RECT rc; - GetClientRect(hwnd, &rc); - int height = rc.bottom - rc.top; - DWORD count = data->items.getCount(); - DWORD current = data->scrollTopItem; - int top = data->scrollTopPixel; - int bottom = top + LayoutItem(hwnd, &data->items, current); - while (top <= height) { - if ((lParam >= top) && (lParam <= bottom)) - return current; - if (++current >= count) - return -1; - top = bottom; - bottom = top + LayoutItem(hwnd, &data->items, current); + if (data->scrollTopPixel > 0) { + data->scrollTopPixel = 0; } - return -1; } - - case NSM_SETCARET: - { - if ((wParam >= 0) && (wParam < data->items.getCount())) { - data->caret = wParam; - if (lParam) { - SendMessage(hwnd, NSM_ENSUREVISIBLE, data->caret, 0); - } + else if (data->scrollTopPixel < 0) { + int maxItem = data->items.getCount(); + int itemHeight = LayoutItem(hwnd, &data->items, data->scrollTopItem); + while ((-data->scrollTopPixel > itemHeight) && (data->scrollTopItem < maxItem)) { + data->scrollTopPixel += itemHeight; + data->scrollTopItem++; + itemHeight = LayoutItem(hwnd, &data->items, data->scrollTopItem); } } + } - case NSM_GETCARET: - { - return data->caret; - } + FixScrollPosition(hwnd, data); +} - case NSM_FINDNEXT: - { - int id = data->items.FindNext(SendMessage(hwnd, NSM_GETCARET, 0, 0), HistoryArray::Filter(HistoryArray::Filter::EVENTONLY, (wchar_t *)wParam)); - if (id >= 0) { - SendMessage(hwnd, NSM_SELECTITEMS2, id, id); - SendMessage(hwnd, NSM_SETCARET, id, TRUE); +static void EnsureVisible(HWND hwnd, NewstoryListData *data, int item) +{ + if (data->scrollTopItem >= item) { + data->scrollTopItem = item; + data->scrollTopPixel = 0; + } + else { + RECT rc; GetClientRect(hwnd, &rc); + int height = rc.bottom - rc.top; + int top = data->scrollTopPixel; + int idx = data->scrollTopItem; + int itemHeight = LayoutItem(hwnd, &data->items, idx); + bool found = false; + while (top < height) { + if (idx == item) { + itemHeight = LayoutItem(hwnd, &data->items, idx); + if (top + itemHeight > height) + ScrollListBy(hwnd, data, 0, height - top - itemHeight); + found = true; + break; } - return id; + top += itemHeight; + idx++; + itemHeight = LayoutItem(hwnd, &data->items, idx); } - - case NSM_FINDPREV: - { - int id = data->items.FindPrev(SendMessage(hwnd, NSM_GETCARET, 0, 0), HistoryArray::Filter(HistoryArray::Filter::EVENTONLY, (wchar_t *)wParam)); - if (id >= 0) { - SendMessage(hwnd, NSM_SELECTITEMS2, id, id); - SendMessage(hwnd, NSM_SETCARET, id, TRUE); - } - return id; + if (!found) { + data->scrollTopItem = item; + data->scrollTopPixel = 0; } + } + FixScrollPosition(hwnd, data); +} - case NSM_COPY: - { - CMStringW res; +static void FixScrollPosition(HWND hwnd, NewstoryListData *data) +{ + EndEditItem(hwnd, data); - int eventCount = data->items.getCount(); - for (int i = 0; i < eventCount; i++) { - HistoryArray::ItemData *item = data->items.get(i, ELM_NOTHING); - if (item->flags & HIF_SELECTED) - res.Append(ptrW(TplFormatString(TPL_COPY_MESSAGE, item->hContact, item))); - } + RECT rc; + GetWindowRect(hwnd, &rc); + int windowHeight = rc.bottom - rc.top; - CopyText(hwnd, res); - } - __fallthrough; - // End of history list control messages + if (windowHeight != data->cachedWindowHeight) { + int maxTopItem = 0; + int tmp = 0; + for (maxTopItem = data->items.getCount(); (maxTopItem > 0) && (tmp < windowHeight); maxTopItem--) + tmp += LayoutItem(hwnd, &data->items, maxTopItem - 1); + data->cachedMaxTopItem = maxTopItem; + data->cachedWindowHeight = windowHeight; + data->cachedMaxTopPixel = (windowHeight < tmp) ? windowHeight - tmp : 0; + } - case WM_SIZE: - InvalidateRect(hwnd, 0, FALSE); - break; + if (data->scrollTopItem < 0) { + data->scrollTopItem = 0; + } - case WM_ERASEBKGND: - return 1; + if ((data->scrollTopItem > data->cachedMaxTopItem) || + ((data->scrollTopItem == data->cachedMaxTopItem) && (data->scrollTopPixel < data->cachedMaxTopPixel))) { + data->scrollTopItem = data->cachedMaxTopItem; + data->scrollTopPixel = data->cachedMaxTopPixel; + } + RecalcScrollBar(hwnd, data); +} - case WM_PRINTCLIENT: - { - // PaintClc(hwnd, dat, (HDC) wParam, NULL); - break; - } - /* - case WM_NCPAINT: - { - RECT rc; - GetWindowRect(hwnd, &rc); - - HDC hdc; - hdc = GetDCEx(hwnd, (HRGN)wParam, DCX_WINDOW|DCX_INTERSECTRGN); - FrameRect(hdc, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH)); - ReleaseDC(hwnd, hdc); - } - */ - /* - case WM_NCPAINT: - { - if (wParam == 1) - break; - { - POINT ptTopLeft = { 0, 0 }; - HRGN hClientRgn; - ClientToScreen(hwnd, &ptTopLeft); - hClientRgn = CreateRectRgn(0, 0, 1, 1); - CombineRgn(hClientRgn, (HRGN) wParam, NULL, RGN_COPY); - OffsetRgn(hClientRgn, -ptTopLeft.x, -ptTopLeft.y); - InvalidateRgn(hwnd, hClientRgn, FALSE); - DeleteObject(hClientRgn); - UpdateWindow(hwnd); - } - break; - } - */ - case WM_PAINT: - { - HDC hdcWindow; - PAINTSTRUCT ps; - hdcWindow = BeginPaint(hwnd, &ps); - - /* we get so many InvalidateRect()'s that there is no point painting, - Windows in theory shouldn't queue up WM_PAINTs in this case but it does so - we'll just ignore them */ - if (IsWindowVisible(hwnd)) { - RECT rc; - GetClientRect(hwnd, &rc); - - HDC hdc = CreateCompatibleDC(hdcWindow); - HBITMAP hbmSave = (HBITMAP)SelectObject(hdc, CreateCompatibleBitmap(hdcWindow, rc.right - rc.left, rc.bottom - rc.top)); - - GetClientRect(hwnd, &rc); - int height = rc.bottom - rc.top; - int width = rc.right - rc.left; - int top = data->scrollTopPixel; - int idx = data->scrollTopItem; - while ((top < height) && (idx < data->items.getCount())) - top += PaintItem(hdc, &data->items, idx++, top, width); - - if (top <= height) { - RECT rc2; - SetRect(&rc2, 0, top, width, height); - - HBRUSH hbr; - hbr = CreateSolidBrush(RGB(0xff, 0xff, 0xff)); - FillRect(hdc, &rc2, hbr); - DeleteObject(hbr); - } - - GetWindowRect(hwnd, &rc); - rc.right -= rc.left; rc.left = 0; - rc.bottom -= rc.top; rc.top = 0; - DrawEdge(hdc, &rc, BDR_SUNKENOUTER, BF_RECT); +static void RecalcScrollBar(HWND hwnd, NewstoryListData *data) +{ + SCROLLINFO si = { 0 }; + RECT clRect; + GetClientRect(hwnd, &clRect); + si.cbSize = sizeof(si); + si.fMask = SIF_ALL; + si.nMin = 0; + si.nMax = data->items.getCount() * AVERAGE_ITEM_HEIGHT; + si.nPage = clRect.bottom; + si.nPos = data->scrollTopItem * AVERAGE_ITEM_HEIGHT; + SetScrollInfo(hwnd, SB_VERT, &si, TRUE); +} - BitBlt(hdcWindow, 0, 0, rc.right, rc.bottom, hdc, 0, 0, SRCCOPY); - DeleteObject(SelectObject(hdc, hbmSave)); - DeleteDC(hdc); - } +static void BeginEditItem(HWND hwnd, NewstoryListData *data, int index) +{ + if (data->hwndEditBox) + EndEditItem(hwnd, data); - EndPaint(hwnd, &ps); - } - break; + if (data->scrollTopItem > index) + return; - case WM_SETFOCUS: - return 0; + RECT rc; GetClientRect(hwnd, &rc); + int height = rc.bottom - rc.top; - case WM_GETDLGCODE: - if (lParam) { - MSG *msg2 = (MSG *)lParam; - if (msg2->message == WM_KEYDOWN) { - if (msg2->wParam == VK_TAB) - return 0; - if (msg2->wParam == VK_ESCAPE && !data->hwndEditBox) - return 0; - } - else if (msg2->message == WM_CHAR) { - if (msg2->wParam == '\t') - return 0; - if (msg2->wParam == 27 && !data->hwndEditBox) - return 0; - } - } - return DLGC_WANTMESSAGE; + int top = data->scrollTopPixel; + int idx = data->scrollTopItem; + int itemHeight = LayoutItem(hwnd, &data->items, idx); + while (top < height) { + if (idx == index) { + HistoryArray::ItemData *item = data->items.get(index, ELM_DATA); - case WM_KEYDOWN: - { - switch (wParam) { - case VK_UP: - SendMessage(hwnd, NSM_SELECTITEMS2, data->caret - 1, data->caret - 1); - SendMessage(hwnd, NSM_SETCARET, data->caret - 1, TRUE); + int tpl; + int fontid; + int colorid; + switch (item->dbe.eventType) { + case EVENTTYPE_MESSAGE: + tpl = TPL_COPY_MESSAGE; + fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INMSG : FONT_OUTMSG; + colorid = !(item->dbe.flags & DBEF_SENT) ? COLOR_INMSG : COLOR_OUTMSG; break; - case VK_DOWN: - SendMessage(hwnd, NSM_SELECTITEMS2, data->caret + 1, data->caret + 1); - SendMessage(hwnd, NSM_SETCARET, data->caret + 1, TRUE); + case EVENTTYPE_FILE: + tpl = TPL_COPY_FILE; + fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INFILE : FONT_OUTFILE; + colorid = !(item->dbe.flags & DBEF_SENT) ? COLOR_INFILE : COLOR_OUTFILE; break; - case VK_PRIOR: + case EVENTTYPE_STATUSCHANGE: + tpl = TPL_COPY_SIGN; + fontid = FONT_STATUS; + colorid = COLOR_STATUS; break; - case VK_NEXT: + case EVENTTYPE_AUTHREQUEST: + tpl = TPL_COPY_AUTH; + fontid = FONT_INOTHER; + colorid = COLOR_INOTHER; break; - case VK_HOME: + case EVENTTYPE_ADDED: + tpl = TPL_COPY_ADDED; + fontid = FONT_INOTHER; + colorid = COLOR_INOTHER; break; - case VK_END: + case EVENTTYPE_JABBER_PRESENCE: + tpl = TPL_COPY_PRESENCE; + fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INOTHER : FONT_OUTOTHER; + colorid = !(item->dbe.flags & DBEF_SENT) ? COLOR_INOTHER : COLOR_OUTOTHER; break; - case VK_F2: - BeginEditItem(hwnd, data, data->caret); + default: + tpl = TPL_COPY_OTHER; + fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INOTHER : FONT_OUTOTHER; + colorid = !(item->dbe.flags & DBEF_SENT) ? COLOR_INOTHER : COLOR_OUTOTHER; break; } + + ptrW text(TplFormatString(tpl, item->hContact, item)); + data->hwndEditBox = CreateWindow(L"EDIT", text, WS_CHILD | WS_BORDER | ES_READONLY | ES_MULTILINE | ES_AUTOVSCROLL, 0, top, rc.right - rc.left, itemHeight, hwnd, NULL, g_plugin.getInst(), NULL); + OldEditWndProc = (WNDPROC)SetWindowLongPtr(data->hwndEditBox, GWLP_WNDPROC, (LONG_PTR)HistoryEditWndProc); + SendMessage(data->hwndEditBox, WM_SETFONT, (WPARAM)fonts[fontid].hfnt, 0); + SendMessage(data->hwndEditBox, EM_SETMARGINS, EC_RIGHTMARGIN, 100); + SendMessage(data->hwndEditBox, EM_SETSEL, 0, (LPARAM)(-1)); + ShowWindow(data->hwndEditBox, SW_SHOW); + SetFocus(data->hwndEditBox); break; } + top += itemHeight; + idx++; + itemHeight = LayoutItem(hwnd, &data->items, idx); + } +} - case WM_SYSCHAR: - case WM_CHAR: - if (wParam == 27) { - if (data->hwndEditBox) - EndEditItem(hwnd, data); - } - else { - char ch = MapVirtualKey((lParam >> 16) & 0xff, 1); - if (((ch == 'C') || (ch == VK_INSERT)) && (GetKeyState(VK_CONTROL) & 0x80)) { - PostMessage(hwnd, NSM_COPY, 0, 0); - } - else if ((ch == 'A') && (GetKeyState(VK_CONTROL) & 0x80)) { - SendMessage(hwnd, NSM_SELECTITEMS, 0, data->items.getCount()); - // } else - // if (ch == VK_ESCAPE) - // { - // PostMessage(GetParent(hwnd), WM_CLOSE, 0, 0); - } - } - break; +static void EndEditItem(HWND, NewstoryListData *data) +{ + DestroyWindow(data->hwndEditBox); + data->hwndEditBox = 0; +} - case WM_LBUTTONDOWN: - { - int item = SendMessage(hwnd, NSM_GETITEMFROMPIXEL, LOWORD(lParam), HIWORD(lParam)); - if (item >= 0) { - if (data->caret != item) - EndEditItem(hwnd, data); +static int LayoutItem(HWND hwnd, HistoryArray *items, int index) +{ + HDC hdc = GetDC(hwnd); + RECT rc; GetClientRect(hwnd, &rc); + int width = rc.right - rc.left; - if (wParam & MK_CONTROL) { - SendMessage(hwnd, NSM_TOGGLEITEMS, item, item); - SendMessage(hwnd, NSM_SETCARET, item, TRUE); - } - else if (wParam & MK_SHIFT) { - SendMessage(hwnd, NSM_SELECTITEMS, data->caret, item); - SendMessage(hwnd, NSM_SETCARET, item, TRUE); - } - else { - if (data->caret == item) { - BeginEditItem(hwnd, data, item); - } - else { - SendMessage(hwnd, NSM_SELECTITEMS2, item, item); - SendMessage(hwnd, NSM_SETCARET, item, TRUE); - } - } - } - } - SetFocus(hwnd); - return 0; + HistoryArray::ItemData *item = items->get(index, ELM_DATA); + if (!item) return 0; - case WM_MOUSEWHEEL: - { - DWORD s_scrollTopItem = data->scrollTopItem; - int s_scrollTopPixel = data->scrollTopPixel; + int tpl; + int fontid; + switch (item->dbe.eventType) { + case EVENTTYPE_MESSAGE: + tpl = TPL_MESSAGE; + fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INMSG : FONT_OUTMSG; + break; - UINT scrollLines; - if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, FALSE)) - scrollLines = 3; - ScrollListBy(hwnd, data, 0, (short)HIWORD(wParam) * 10 * (signed)scrollLines / WHEEL_DELTA); - - if ((s_scrollTopItem != data->scrollTopItem) || (s_scrollTopPixel != data->scrollTopPixel)) - InvalidateRect(hwnd, 0, FALSE); - } - return TRUE; - - case WM_VSCROLL: - { - DWORD s_scrollTopItem = data->scrollTopItem; - int s_scrollTopPixel = data->scrollTopPixel; + case EVENTTYPE_FILE: + tpl = TPL_FILE; + fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INFILE : FONT_OUTFILE; + break; - switch (LOWORD(wParam)) { - case SB_LINEUP: - ScrollListBy(hwnd, data, 0, 10); - break; - case SB_LINEDOWN: - ScrollListBy(hwnd, data, 0, -10); - break; - case SB_PAGEUP: - ScrollListBy(hwnd, data, -10, 0); - break; - case SB_PAGEDOWN: - ScrollListBy(hwnd, data, 10, 0); - break; - case SB_BOTTOM: - data->scrollTopItem = data->items.getCount() - 1; - data->scrollTopPixel = 0; - break; - case SB_TOP: - data->scrollTopItem = 0; - data->scrollTopPixel = 0; - break; - case SB_THUMBTRACK: - { - SCROLLINFO si; - si.cbSize = sizeof(si); - si.fMask = SIF_TRACKPOS | SIF_RANGE; - GetScrollInfo(hwnd, SB_VERT, &si); - int pos = si.nTrackPos; + case EVENTTYPE_STATUSCHANGE: + tpl = TPL_SIGN; + fontid = FONT_STATUS; + break; - if (pos == si.nMax) { - data->scrollTopItem = data->items.getCount(); - data->scrollTopPixel = -1000; - } - else { - data->scrollTopItem = pos / AVERAGE_ITEM_HEIGHT; - int itemHeight = LayoutItem(hwnd, &data->items, data->scrollTopItem); - data->scrollTopPixel = -pos % AVERAGE_ITEM_HEIGHT * itemHeight / AVERAGE_ITEM_HEIGHT; - } - FixScrollPosition(hwnd, data); - } - break; + case EVENTTYPE_AUTHREQUEST: + tpl = TPL_AUTH; + fontid = FONT_INOTHER; + break; - default: - return 0; - } + case EVENTTYPE_ADDED: + tpl = TPL_ADDED; + fontid = FONT_INOTHER; + break; - if ((s_scrollTopItem != data->scrollTopItem) || (s_scrollTopPixel != data->scrollTopPixel)) - InvalidateRect(hwnd, 0, FALSE); - break; - } + case EVENTTYPE_JABBER_PRESENCE: + tpl = TPL_PRESENCE; + fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INOTHER : FONT_OUTOTHER; + break; - case WM_DESTROY: - delete data; - SetWindowLongPtr(hwnd, 0, 0); + default: + tpl = TPL_OTHER; + fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INOTHER : FONT_OUTOTHER; break; } - return DefWindowProc(hwnd, msg, wParam, lParam); -} + HFONT hfnt = (HFONT)SelectObject(hdc, fonts[fontid].hfnt); + if (!item->data) + item->data = MTextCreateW(htuLog, ptrW(TplFormatString(tpl, item->hContact, item))); -///////////////////////////////////////////////////////////////////////// -// Utilities -static void ScrollListBy(HWND hwnd, NewstoryListData *data, int scrollItems, int scrollPixels) -{ - if (scrollItems) { - data->scrollTopItem += scrollItems; - data->scrollTopPixel = 0; - } - else if (scrollPixels) { - data->scrollTopPixel += scrollPixels; - if (data->scrollTopPixel > 0) { - while ((data->scrollTopPixel > 0) && data->scrollTopItem) { - data->scrollTopItem--; - int itemHeight = LayoutItem(hwnd, &data->items, data->scrollTopItem); - data->scrollTopPixel -= itemHeight; - } + SIZE sz; + sz.cx = width - 6; + MTextMeasure(hdc, &sz, (HANDLE)item->data); - if (data->scrollTopPixel > 0) { - data->scrollTopPixel = 0; - } - } - else if (data->scrollTopPixel < 0) { - int maxItem = data->items.getCount(); - int itemHeight = LayoutItem(hwnd, &data->items, data->scrollTopItem); - while ((-data->scrollTopPixel > itemHeight) && (data->scrollTopItem < maxItem)) { - data->scrollTopPixel += itemHeight; - data->scrollTopItem++; - itemHeight = LayoutItem(hwnd, &data->items, data->scrollTopItem); - } - } - } + SelectObject(hdc, hfnt); - FixScrollPosition(hwnd, data); + ReleaseDC(hwnd, hdc); + return sz.cy + 5; } -static void EnsureVisible(HWND hwnd, NewstoryListData *data, int item) +static int PaintItem(HDC hdc, HistoryArray *items, int index, int top, int width) { - if (data->scrollTopItem >= item) { - data->scrollTopItem = item; - data->scrollTopPixel = 0; + if (!items) return 0; + HistoryArray::ItemData *item = items->get(index, ELM_DATA); + + // LOGFONT lfText; + COLORREF clText, clBack, clLine; + int tpl; + int fontid; + int colorid; + switch (item->dbe.eventType) { + case EVENTTYPE_MESSAGE: + tpl = TPL_MESSAGE; + fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INMSG : FONT_OUTMSG; + colorid = !(item->dbe.flags & DBEF_SENT) ? COLOR_INMSG : COLOR_OUTMSG; + break; + + case EVENTTYPE_FILE: + tpl = TPL_FILE; + fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INFILE : FONT_OUTFILE; + colorid = !(item->dbe.flags & DBEF_SENT) ? COLOR_INFILE : COLOR_OUTFILE; + break; + + case EVENTTYPE_STATUSCHANGE: + tpl = TPL_SIGN; + fontid = FONT_STATUS; + colorid = COLOR_STATUS; + break; + + case EVENTTYPE_AUTHREQUEST: + tpl = TPL_AUTH; + fontid = FONT_INOTHER; + colorid = COLOR_INOTHER; + break; + + case EVENTTYPE_ADDED: + tpl = TPL_ADDED; + fontid = FONT_INOTHER; + colorid = COLOR_INOTHER; + break; + + case EVENTTYPE_JABBER_PRESENCE: + tpl = TPL_PRESENCE; + fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INOTHER : FONT_OUTOTHER; + colorid = !(item->dbe.flags & DBEF_SENT) ? COLOR_INOTHER : COLOR_OUTOTHER; + break; + + default: + tpl = TPL_OTHER; + fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INOTHER : FONT_OUTOTHER; + colorid = !(item->dbe.flags & DBEF_SENT) ? COLOR_INOTHER : COLOR_OUTOTHER; + break; + } + clText = fonts[fontid].cl; + if (item->flags & HIF_SELECTED) { + MTextSendMessage(0, item->data, EM_SETSEL, 0, -1); + clText = colors[COLOR_SELTEXT].cl; + clLine = GetSysColor(COLOR_HIGHLIGHTTEXT); + clBack = GetSysColor(COLOR_HIGHLIGHT); } else { - RECT rc; GetClientRect(hwnd, &rc); - int height = rc.bottom - rc.top; - int top = data->scrollTopPixel; - int idx = data->scrollTopItem; - int itemHeight = LayoutItem(hwnd, &data->items, idx); - bool found = false; - while (top < height) { - if (idx == item) { - itemHeight = LayoutItem(hwnd, &data->items, idx); - if (top + itemHeight > height) - ScrollListBy(hwnd, data, 0, height - top - itemHeight); - found = true; - break; - } - top += itemHeight; - idx++; - itemHeight = LayoutItem(hwnd, &data->items, idx); - } - if (!found) { - data->scrollTopItem = item; - data->scrollTopPixel = 0; - } + MTextSendMessage(0, item->data, EM_SETSEL, 0, 0); + clLine = colors[COLOR_SELECTED].cl; + clBack = colors[colorid].cl; } - FixScrollPosition(hwnd, data); -} -static void FixScrollPosition(HWND hwnd, NewstoryListData *data) -{ - EndEditItem(hwnd, data); + if (!item->data) { + wchar_t *buf = TplFormatString(tpl, item->hContact, item); + item->data = MTextCreateW(htuLog, buf); + mir_free(buf); + if (!item->data) + return 0; + } + + SIZE sz; + sz.cx = width - 6; + HFONT hfnt = (HFONT)SelectObject(hdc, fonts[fontid].hfnt); + MTextMeasure(hdc, &sz, (HANDLE)item->data); + SelectObject(hdc, hfnt); + int height = sz.cy + 5; RECT rc; - GetWindowRect(hwnd, &rc); - int windowHeight = rc.bottom - rc.top; + SetRect(&rc, 0, top, width, top + height); - if (windowHeight != data->cachedWindowHeight) { - int maxTopItem = 0; - int tmp = 0; - for (maxTopItem = data->items.getCount(); (maxTopItem > 0) && (tmp < windowHeight); maxTopItem--) - tmp += LayoutItem(hwnd, &data->items, maxTopItem - 1); - data->cachedMaxTopItem = maxTopItem; - data->cachedWindowHeight = windowHeight; - data->cachedMaxTopPixel = (windowHeight < tmp) ? windowHeight - tmp : 0; - } + HBRUSH hbr; + hbr = CreateSolidBrush(clBack); + FillRect(hdc, &rc, hbr); - if (data->scrollTopItem < 0) { - data->scrollTopItem = 0; - } + SetTextColor(hdc, clText); + SetBkMode(hdc, TRANSPARENT); - if ((data->scrollTopItem > data->cachedMaxTopItem) || - ((data->scrollTopItem == data->cachedMaxTopItem) && (data->scrollTopPixel < data->cachedMaxTopPixel))) { - data->scrollTopItem = data->cachedMaxTopItem; - data->scrollTopPixel = data->cachedMaxTopPixel; - } - RecalcScrollBar(hwnd, data); -} + POINT pos; + pos.x = 3; + pos.y = top + 2; + hfnt = (HFONT)SelectObject(hdc, fonts[fontid].hfnt); + MTextDisplay(hdc, pos, sz, (HANDLE)item->data); + SelectObject(hdc, hfnt); -static void RecalcScrollBar(HWND hwnd, NewstoryListData *data) -{ - SCROLLINFO si = { 0 }; - RECT clRect; - GetClientRect(hwnd, &clRect); - si.cbSize = sizeof(si); - si.fMask = SIF_ALL; - si.nMin = 0; - si.nMax = data->items.getCount() * AVERAGE_ITEM_HEIGHT; - si.nPage = clRect.bottom; - si.nPos = data->scrollTopItem * AVERAGE_ITEM_HEIGHT; - SetScrollInfo(hwnd, SB_VERT, &si, TRUE); + DeleteObject(hbr); + + HPEN hpn = (HPEN)SelectObject(hdc, CreatePen(PS_SOLID, 1, clLine)); + MoveToEx(hdc, rc.left, rc.bottom - 1, 0); + LineTo(hdc, rc.right, rc.bottom - 1); + DeleteObject(SelectObject(hdc, hpn)); + + return height; } -static void BeginEditItem(HWND hwnd, NewstoryListData *data, int index) +///////////////////////////////////////////////////////////////////////// +// WndProc + +LRESULT CALLBACK NewstoryListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - if (data->hwndEditBox) - EndEditItem(hwnd, data); + NewstoryListData *data = (NewstoryListData *)GetWindowLongPtr(hwnd, 0); + + switch (msg) { + case WM_CREATE: + data = new NewstoryListData; + SetWindowLongPtr(hwnd, 0, (LONG_PTR)data); + RecalcScrollBar(hwnd, data); + break; + + case WM_USER: + data->items.addHistory((MCONTACT)wParam); + RecalcScrollBar(hwnd, data); + data->scrollTopItem = data->items.getCount(); + FixScrollPosition(hwnd, data); + InvalidateRect(hwnd, 0, FALSE); + break; + + // History list control messages + case NSM_GETCOUNT: + return data->items.getCount(); + + case NSM_SELECTITEMS: + { + int start = min(data->items.getCount() - 1, max(0, wParam)); + int end = min(data->items.getCount() - 1, max(0, lParam)); + if (start > end) { + start ^= end; + end ^= start; + start ^= end; + } + for (int i = start; i <= end; ++i) + data->items.get(i, ELM_NOTHING)->flags |= HIF_SELECTED; + InvalidateRect(hwnd, 0, FALSE); + return 0; + } + + case NSM_TOGGLEITEMS: + { + int start = min(data->items.getCount() - 1, max(0, wParam)); + int end = min(data->items.getCount() - 1, max(0, lParam)); + if (start > end) { + start ^= end; + end ^= start; + start ^= end; + } + for (int i = start; i <= end; ++i) { + if (data->items.get(i, ELM_NOTHING)->flags & HIF_SELECTED) { + data->items.get(i, ELM_NOTHING)->flags &= ~HIF_SELECTED; + } + else { + data->items.get(i, ELM_NOTHING)->flags |= HIF_SELECTED; + } + } + InvalidateRect(hwnd, 0, FALSE); + return 0; + } + + case NSM_SELECTITEMS2: + { + int start = min(data->items.getCount() - 1, max(0, wParam)); + int end = min(data->items.getCount() - 1, max(0, lParam)); + if (start > end) { + start ^= end; + end ^= start; + start ^= end; + } + int count = data->items.getCount(); + for (int i = 0; i < count; ++i) { + if ((i >= start) && (i <= end)) { + data->items.get(i, ELM_NOTHING)->flags |= HIF_SELECTED; + } + else { + data->items.get(i, ELM_NOTHING)->flags &= ~((DWORD)HIF_SELECTED); + } + } + InvalidateRect(hwnd, 0, FALSE); + return 0; + } + + case NSM_DESELECTITEMS: + { + int start = min(data->items.getCount() - 1, max(0, wParam)); + int end = min(data->items.getCount() - 1, max(0, lParam)); + if (start > end) { + start ^= end; + end ^= start; + start ^= end; + } + for (int i = start; i <= end; ++i) + data->items.get(i, ELM_NOTHING)->flags &= ~((DWORD)HIF_SELECTED); + InvalidateRect(hwnd, 0, FALSE); + return 0; + } + + case NSM_ENSUREVISIBLE: + EnsureVisible(hwnd, data, wParam); + return 0; - if (data->scrollTopItem > index) - return; + case NSM_GETITEMFROMPIXEL: + { + RECT rc; + GetClientRect(hwnd, &rc); + int height = rc.bottom - rc.top; + DWORD count = data->items.getCount(); + DWORD current = data->scrollTopItem; + int top = data->scrollTopPixel; + int bottom = top + LayoutItem(hwnd, &data->items, current); + while (top <= height) { + if ((lParam >= top) && (lParam <= bottom)) + return current; + if (++current >= count) + return -1; + top = bottom; + bottom = top + LayoutItem(hwnd, &data->items, current); + } + return -1; + } - RECT rc; GetClientRect(hwnd, &rc); - int height = rc.bottom - rc.top; + case NSM_SETCARET: + if ((wParam >= 0) && (wParam < data->items.getCount())) { + data->caret = wParam; + if (lParam) + SendMessage(hwnd, NSM_ENSUREVISIBLE, data->caret, 0); + } - int top = data->scrollTopPixel; - int idx = data->scrollTopItem; - int itemHeight = LayoutItem(hwnd, &data->items, idx); - while (top < height) { - if (idx == index) { - HistoryArray::ItemData *item = data->items.get(index, ELM_DATA); + case NSM_GETCARET: + return data->caret; - int tpl; - int fontid; - int colorid; - switch (item->dbe.eventType) { - case EVENTTYPE_MESSAGE: - tpl = TPL_COPY_MESSAGE; - fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INMSG : FONT_OUTMSG; - colorid = !(item->dbe.flags & DBEF_SENT) ? COLOR_INMSG : COLOR_OUTMSG; - break; + case NSM_FINDNEXT: + { + int id = data->items.FindNext(SendMessage(hwnd, NSM_GETCARET, 0, 0), HistoryArray::Filter(HistoryArray::Filter::EVENTONLY, (wchar_t *)wParam)); + if (id >= 0) { + SendMessage(hwnd, NSM_SELECTITEMS2, id, id); + SendMessage(hwnd, NSM_SETCARET, id, TRUE); + } + return id; + } - case EVENTTYPE_FILE: - tpl = TPL_COPY_FILE; - fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INFILE : FONT_OUTFILE; - colorid = !(item->dbe.flags & DBEF_SENT) ? COLOR_INFILE : COLOR_OUTFILE; - break; + case NSM_FINDPREV: + { + int id = data->items.FindPrev(SendMessage(hwnd, NSM_GETCARET, 0, 0), HistoryArray::Filter(HistoryArray::Filter::EVENTONLY, (wchar_t *)wParam)); + if (id >= 0) { + SendMessage(hwnd, NSM_SELECTITEMS2, id, id); + SendMessage(hwnd, NSM_SETCARET, id, TRUE); + } + return id; + } - case EVENTTYPE_STATUSCHANGE: - tpl = TPL_COPY_SIGN; - fontid = FONT_STATUS; - colorid = COLOR_STATUS; - break; + case NSM_SEEKTIME: + { + int eventCount = data->items.getCount(); + for (int i = 0; i < eventCount; i++) { + auto *item = data->items.get(i, ELM_NOTHING); + if (item->dbe.timestamp >= wParam) { + SendMessage(hwnd, NSM_SETCARET, i, TRUE); + break; + } + } + } + return TRUE; - case EVENTTYPE_AUTHREQUEST: - tpl = TPL_COPY_AUTH; - fontid = FONT_INOTHER; - colorid = COLOR_INOTHER; - break; + case NSM_COPY: + { + CMStringW res; - case EVENTTYPE_ADDED: - tpl = TPL_COPY_ADDED; - fontid = FONT_INOTHER; - colorid = COLOR_INOTHER; - break; + int eventCount = data->items.getCount(); + for (int i = 0; i < eventCount; i++) { + HistoryArray::ItemData *item = data->items.get(i, ELM_NOTHING); + if (item->flags & HIF_SELECTED) + res.Append(ptrW(TplFormatString(TPL_COPY_MESSAGE, item->hContact, item))); + } - case EVENTTYPE_JABBER_PRESENCE: - tpl = TPL_COPY_PRESENCE; - fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INOTHER : FONT_OUTOTHER; - colorid = !(item->dbe.flags & DBEF_SENT) ? COLOR_INOTHER : COLOR_OUTOTHER; - break; + CopyText(hwnd, res); + } + __fallthrough; + // End of history list control messages - default: - tpl = TPL_COPY_OTHER; - fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INOTHER : FONT_OUTOTHER; - colorid = !(item->dbe.flags & DBEF_SENT) ? COLOR_INOTHER : COLOR_OUTOTHER; - break; - } + case WM_SIZE: + InvalidateRect(hwnd, 0, FALSE); + break; - ptrW text(TplFormatString(tpl, item->hContact, item)); - data->hwndEditBox = CreateWindow(L"EDIT", text, WS_CHILD | WS_BORDER | ES_READONLY | ES_MULTILINE | ES_AUTOVSCROLL, 0, top, rc.right - rc.left, itemHeight, hwnd, NULL, g_plugin.getInst(), NULL); - OldEditWndProc = (WNDPROC)SetWindowLongPtr(data->hwndEditBox, GWLP_WNDPROC, (LONG_PTR)HistoryEditWndProc); - SendMessage(data->hwndEditBox, WM_SETFONT, (WPARAM)fonts[fontid].hfnt, 0); - SendMessage(data->hwndEditBox, EM_SETMARGINS, EC_RIGHTMARGIN, 100); - SendMessage(data->hwndEditBox, EM_SETSEL, 0, (LPARAM)(-1)); - ShowWindow(data->hwndEditBox, SW_SHOW); - SetFocus(data->hwndEditBox); + case WM_ERASEBKGND: + return 1; + + case WM_PRINTCLIENT: + { + // PaintClc(hwnd, dat, (HDC) wParam, NULL); break; } - top += itemHeight; - idx++; - itemHeight = LayoutItem(hwnd, &data->items, idx); - } -} + /* + case WM_NCPAINT: + { + RECT rc; + GetWindowRect(hwnd, &rc); -static void EndEditItem(HWND, NewstoryListData *data) -{ - DestroyWindow(data->hwndEditBox); - data->hwndEditBox = 0; -} + HDC hdc; + hdc = GetDCEx(hwnd, (HRGN)wParam, DCX_WINDOW|DCX_INTERSECTRGN); + FrameRect(hdc, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH)); + ReleaseDC(hwnd, hdc); + } + */ + /* + case WM_NCPAINT: + { + if (wParam == 1) + break; + { + POINT ptTopLeft = { 0, 0 }; + HRGN hClientRgn; + ClientToScreen(hwnd, &ptTopLeft); + hClientRgn = CreateRectRgn(0, 0, 1, 1); + CombineRgn(hClientRgn, (HRGN) wParam, NULL, RGN_COPY); + OffsetRgn(hClientRgn, -ptTopLeft.x, -ptTopLeft.y); + InvalidateRgn(hwnd, hClientRgn, FALSE); + DeleteObject(hClientRgn); + UpdateWindow(hwnd); + } + break; + } + */ + case WM_PAINT: + { + HDC hdcWindow; + PAINTSTRUCT ps; + hdcWindow = BeginPaint(hwnd, &ps); -static int LayoutItem(HWND hwnd, HistoryArray *items, int index) -{ - HDC hdc = GetDC(hwnd); - RECT rc; GetClientRect(hwnd, &rc); - int width = rc.right - rc.left; + /* we get so many InvalidateRect()'s that there is no point painting, + Windows in theory shouldn't queue up WM_PAINTs in this case but it does so + we'll just ignore them */ + if (IsWindowVisible(hwnd)) { + RECT rc; + GetClientRect(hwnd, &rc); - HistoryArray::ItemData *item = items->get(index, ELM_DATA); - if (!item) return 0; + HDC hdc = CreateCompatibleDC(hdcWindow); + HBITMAP hbmSave = (HBITMAP)SelectObject(hdc, CreateCompatibleBitmap(hdcWindow, rc.right - rc.left, rc.bottom - rc.top)); - int tpl; - int fontid; - switch (item->dbe.eventType) { - case EVENTTYPE_MESSAGE: - tpl = TPL_MESSAGE; - fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INMSG : FONT_OUTMSG; - break; + GetClientRect(hwnd, &rc); + int height = rc.bottom - rc.top; + int width = rc.right - rc.left; + int top = data->scrollTopPixel; + int idx = data->scrollTopItem; + while ((top < height) && (idx < data->items.getCount())) + top += PaintItem(hdc, &data->items, idx++, top, width); - case EVENTTYPE_FILE: - tpl = TPL_FILE; - fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INFILE : FONT_OUTFILE; - break; + if (top <= height) { + RECT rc2; + SetRect(&rc2, 0, top, width, height); - case EVENTTYPE_STATUSCHANGE: - tpl = TPL_SIGN; - fontid = FONT_STATUS; - break; + HBRUSH hbr; + hbr = CreateSolidBrush(RGB(0xff, 0xff, 0xff)); + FillRect(hdc, &rc2, hbr); + DeleteObject(hbr); + } - case EVENTTYPE_AUTHREQUEST: - tpl = TPL_AUTH; - fontid = FONT_INOTHER; - break; + GetWindowRect(hwnd, &rc); + rc.right -= rc.left; rc.left = 0; + rc.bottom -= rc.top; rc.top = 0; + DrawEdge(hdc, &rc, BDR_SUNKENOUTER, BF_RECT); - case EVENTTYPE_ADDED: - tpl = TPL_ADDED; - fontid = FONT_INOTHER; - break; + BitBlt(hdcWindow, 0, 0, rc.right, rc.bottom, hdc, 0, 0, SRCCOPY); + DeleteObject(SelectObject(hdc, hbmSave)); + DeleteDC(hdc); + } - case EVENTTYPE_JABBER_PRESENCE: - tpl = TPL_PRESENCE; - fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INOTHER : FONT_OUTOTHER; + EndPaint(hwnd, &ps); + } break; - default: - tpl = TPL_OTHER; - fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INOTHER : FONT_OUTOTHER; - break; - } + case WM_SETFOCUS: + return 0; + + case WM_GETDLGCODE: + if (lParam) { + MSG *msg2 = (MSG *)lParam; + if (msg2->message == WM_KEYDOWN) { + if (msg2->wParam == VK_TAB) + return 0; + if (msg2->wParam == VK_ESCAPE && !data->hwndEditBox) + return 0; + } + else if (msg2->message == WM_CHAR) { + if (msg2->wParam == '\t') + return 0; + if (msg2->wParam == 27 && !data->hwndEditBox) + return 0; + } + } + return DLGC_WANTMESSAGE; + + case WM_KEYDOWN: + { + switch (wParam) { + case VK_UP: + SendMessage(hwnd, NSM_SELECTITEMS2, data->caret - 1, data->caret - 1); + SendMessage(hwnd, NSM_SETCARET, data->caret - 1, TRUE); + break; - HFONT hfnt = (HFONT)SelectObject(hdc, fonts[fontid].hfnt); - if (!item->data) - item->data = MTextCreateW(htuLog, ptrW(TplFormatString(tpl, item->hContact, item))); + case VK_DOWN: + SendMessage(hwnd, NSM_SELECTITEMS2, data->caret + 1, data->caret + 1); + SendMessage(hwnd, NSM_SETCARET, data->caret + 1, TRUE); + break; - SIZE sz; - sz.cx = width - 6; - MTextMeasure(hdc, &sz, (HANDLE)item->data); + case VK_PRIOR: + break; - SelectObject(hdc, hfnt); + case VK_NEXT: + break; - ReleaseDC(hwnd, hdc); - return sz.cy + 5; -} + case VK_HOME: + break; -static int PaintItem(HDC hdc, HistoryArray *items, int index, int top, int width) -{ - if (!items) return 0; - HistoryArray::ItemData *item = items->get(index, ELM_DATA); + case VK_END: + break; - // LOGFONT lfText; - COLORREF clText, clBack, clLine; - int tpl; - int fontid; - int colorid; - switch (item->dbe.eventType) { - case EVENTTYPE_MESSAGE: - tpl = TPL_MESSAGE; - fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INMSG : FONT_OUTMSG; - colorid = !(item->dbe.flags & DBEF_SENT) ? COLOR_INMSG : COLOR_OUTMSG; - break; + case VK_F2: + BeginEditItem(hwnd, data, data->caret); + break; + } + break; + } - case EVENTTYPE_FILE: - tpl = TPL_FILE; - fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INFILE : FONT_OUTFILE; - colorid = !(item->dbe.flags & DBEF_SENT) ? COLOR_INFILE : COLOR_OUTFILE; + case WM_SYSCHAR: + case WM_CHAR: + if (wParam == 27) { + if (data->hwndEditBox) + EndEditItem(hwnd, data); + } + else { + char ch = MapVirtualKey((lParam >> 16) & 0xff, 1); + if (((ch == 'C') || (ch == VK_INSERT)) && (GetKeyState(VK_CONTROL) & 0x80)) { + PostMessage(hwnd, NSM_COPY, 0, 0); + } + else if ((ch == 'A') && (GetKeyState(VK_CONTROL) & 0x80)) { + SendMessage(hwnd, NSM_SELECTITEMS, 0, data->items.getCount()); + // } else + // if (ch == VK_ESCAPE) + // { + // PostMessage(GetParent(hwnd), WM_CLOSE, 0, 0); + } + } break; - case EVENTTYPE_STATUSCHANGE: - tpl = TPL_SIGN; - fontid = FONT_STATUS; - colorid = COLOR_STATUS; - break; + case WM_LBUTTONDOWN: + { + int item = SendMessage(hwnd, NSM_GETITEMFROMPIXEL, LOWORD(lParam), HIWORD(lParam)); + if (item >= 0) { + if (data->caret != item) + EndEditItem(hwnd, data); - case EVENTTYPE_AUTHREQUEST: - tpl = TPL_AUTH; - fontid = FONT_INOTHER; - colorid = COLOR_INOTHER; - break; + if (wParam & MK_CONTROL) { + SendMessage(hwnd, NSM_TOGGLEITEMS, item, item); + SendMessage(hwnd, NSM_SETCARET, item, TRUE); + } + else if (wParam & MK_SHIFT) { + SendMessage(hwnd, NSM_SELECTITEMS, data->caret, item); + SendMessage(hwnd, NSM_SETCARET, item, TRUE); + } + else { + if (data->caret == item) { + BeginEditItem(hwnd, data, item); + } + else { + SendMessage(hwnd, NSM_SELECTITEMS2, item, item); + SendMessage(hwnd, NSM_SETCARET, item, TRUE); + } + } + } + } + SetFocus(hwnd); + return 0; - case EVENTTYPE_ADDED: - tpl = TPL_ADDED; - fontid = FONT_INOTHER; - colorid = COLOR_INOTHER; - break; + case WM_MOUSEWHEEL: + { + int s_scrollTopItem = data->scrollTopItem; + int s_scrollTopPixel = data->scrollTopPixel; - case EVENTTYPE_JABBER_PRESENCE: - tpl = TPL_PRESENCE; - fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INOTHER : FONT_OUTOTHER; - colorid = !(item->dbe.flags & DBEF_SENT) ? COLOR_INOTHER : COLOR_OUTOTHER; - break; + UINT scrollLines; + if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, FALSE)) + scrollLines = 3; + ScrollListBy(hwnd, data, 0, (short)HIWORD(wParam) * 10 * (signed)scrollLines / WHEEL_DELTA); - default: - tpl = TPL_OTHER; - fontid = !(item->dbe.flags & DBEF_SENT) ? FONT_INOTHER : FONT_OUTOTHER; - colorid = !(item->dbe.flags & DBEF_SENT) ? COLOR_INOTHER : COLOR_OUTOTHER; - break; - } - clText = fonts[fontid].cl; - if (item->flags & HIF_SELECTED) { - MTextSendMessage(0, item->data, EM_SETSEL, 0, -1); - // clText = colors[COLOR_SELTEXT].cl; - clLine = GetSysColor(COLOR_HIGHLIGHTTEXT); - clBack = GetSysColor(COLOR_HIGHLIGHT); //colors[COLOR_SELECTED].cl; - } - else { - MTextSendMessage(0, item->data, EM_SETSEL, 0, 0); - clLine = colors[COLOR_SELECTED].cl; - clBack = colors[colorid].cl; - } + if ((s_scrollTopItem != data->scrollTopItem) || (s_scrollTopPixel != data->scrollTopPixel)) + InvalidateRect(hwnd, 0, FALSE); + } + return TRUE; - if (!item->data) { - wchar_t *buf = TplFormatString(tpl, item->hContact, item); - item->data = MTextCreateW(htuLog, buf); - mir_free(buf); - if (!item->data) - return 0; - } + case WM_VSCROLL: + { + int s_scrollTopItem = data->scrollTopItem; + int s_scrollTopPixel = data->scrollTopPixel; - SIZE sz; - sz.cx = width - 6; - HFONT hfnt = (HFONT)SelectObject(hdc, fonts[fontid].hfnt); - MTextMeasure(hdc, &sz, (HANDLE)item->data); - SelectObject(hdc, hfnt); - int height = sz.cy + 5; + switch (LOWORD(wParam)) { + case SB_LINEUP: + ScrollListBy(hwnd, data, 0, 10); + break; + case SB_LINEDOWN: + ScrollListBy(hwnd, data, 0, -10); + break; + case SB_PAGEUP: + ScrollListBy(hwnd, data, -10, 0); + break; + case SB_PAGEDOWN: + ScrollListBy(hwnd, data, 10, 0); + break; + case SB_BOTTOM: + data->scrollTopItem = data->items.getCount() - 1; + data->scrollTopPixel = 0; + break; + case SB_TOP: + data->scrollTopItem = 0; + data->scrollTopPixel = 0; + break; + case SB_THUMBTRACK: + { + SCROLLINFO si; + si.cbSize = sizeof(si); + si.fMask = SIF_TRACKPOS | SIF_RANGE; + GetScrollInfo(hwnd, SB_VERT, &si); + int pos = si.nTrackPos; - RECT rc; - SetRect(&rc, 0, top, width, top + height); + if (pos == si.nMax) { + data->scrollTopItem = data->items.getCount(); + data->scrollTopPixel = -1000; + } + else { + data->scrollTopItem = pos / AVERAGE_ITEM_HEIGHT; + int itemHeight = LayoutItem(hwnd, &data->items, data->scrollTopItem); + data->scrollTopPixel = -pos % AVERAGE_ITEM_HEIGHT * itemHeight / AVERAGE_ITEM_HEIGHT; + } + FixScrollPosition(hwnd, data); + } + break; - HBRUSH hbr; - hbr = CreateSolidBrush(clBack); - FillRect(hdc, &rc, hbr); + default: + return 0; + } - SetTextColor(hdc, clText); - SetBkMode(hdc, TRANSPARENT); + if ((s_scrollTopItem != data->scrollTopItem) || (s_scrollTopPixel != data->scrollTopPixel)) + InvalidateRect(hwnd, 0, FALSE); + break; + } - POINT pos; - pos.x = 3; - pos.y = top + 2; - hfnt = (HFONT)SelectObject(hdc, fonts[fontid].hfnt); - MTextDisplay(hdc, pos, sz, (HANDLE)item->data); - SelectObject(hdc, hfnt); + case WM_DESTROY: + delete data; + SetWindowLongPtr(hwnd, 0, 0); + break; + } - DeleteObject(hbr); + return DefWindowProc(hwnd, msg, wParam, lParam); +} - HPEN hpn = (HPEN)SelectObject(hdc, CreatePen(PS_SOLID, 1, clLine)); - MoveToEx(hdc, rc.left, rc.bottom - 1, 0); - LineTo(hdc, rc.right, rc.bottom - 1); - DeleteObject(SelectObject(hdc, hpn)); +void InitNewstoryControl() +{ + htuLog = MTextRegister("Newstory", MTEXT_FANCY_DEFAULT | MTEXT_SYSTEM_HICONS); - return height; + WNDCLASS wndclass = {}; + wndclass.style = /*CS_HREDRAW | CS_VREDRAW | */CS_DBLCLKS | CS_GLOBALCLASS; + wndclass.lpfnWndProc = NewstoryListWndProc; + wndclass.cbWndExtra = sizeof(void *); + wndclass.hInstance = g_plugin.getInst(); + wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclass.lpszClassName = _T(NEWSTORYLIST_CLASS); + RegisterClass(&wndclass); } diff --git a/plugins/NewStory/src/history_control.h b/plugins/NewStory/src/history_control.h index 3b857bac75..f17da39fdb 100644 --- a/plugins/NewStory/src/history_control.h +++ b/plugins/NewStory/src/history_control.h @@ -57,6 +57,9 @@ enum // NSM_GETCOUNT, + // + NSM_SEEKTIME, + NSM_LAST }; -- cgit v1.2.3