1 // WildcardDropList.cpp
\r
2 // Copyright (c) datadiode
\r
3 // SPDX-License-Identifier: WTFPL
\r
6 #if 0 // Change to 1 if in doubt whether stdafx.h includes them already
\r
9 #include <commctrl.h>
\r
13 #include <algorithm>
\r
16 #include "WildcardDropList.h"
\r
19 * @brief DropList window procedure.
\r
21 LRESULT WildcardDropList::LbWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
26 if (wParam == IDCANCEL)
\r
28 DRAWITEMSTRUCT *const pdis = reinterpret_cast<DRAWITEMSTRUCT *>(lParam);
\r
29 ::DrawFrameControl(pdis->hDC, &pdis->rcItem, DFC_CAPTION, DFCS_CAPTIONCLOSE | DFCS_FLAT | DFCS_MONO);
\r
33 case WM_LBUTTONDOWN:
\r
34 if (HWND hTc = GetDlgItem(hwnd, 100))
\r
37 POINTSTOPOINT(info.pt, lParam);
\r
38 int i = TabCtrl_HitTest(hTc, &info);
\r
42 item.mask = TCIF_STATE;
\r
43 item.dwStateMask = TCIS_HIGHLIGHTED;
\r
44 TabCtrl_GetItem(hTc, i, &item);
\r
45 item.dwState ^= TCIS_HIGHLIGHTED;
\r
46 TabCtrl_SetItem(hTc, i, &item);
\r
47 ::EnableWindow(hTc, TRUE);
\r
49 else if (HWND hwndHit = ::ChildWindowFromPoint(hwnd, info.pt))
\r
51 if (hwndHit != hwnd && ::GetDlgCtrlID(hwndHit) == IDCANCEL)
\r
53 if (HWND hCb = reinterpret_cast<HWND>(::GetWindowLongPtr(hTc, GWLP_USERDATA)))
\r
55 ::EnableWindow(hTc, FALSE);
\r
56 ::SendMessage(hCb, CB_SHOWDROPDOWN, 0, 0);
\r
62 case WM_RBUTTONDOWN:
\r
63 if (HWND hTc = ::GetDlgItem(hwnd, 100))
\r
65 if (HWND hCb = reinterpret_cast<HWND>(::GetWindowLongPtr(hTc, GWLP_USERDATA)))
\r
67 ::SendMessage(hCb, CB_SHOWDROPDOWN, 0, 0);
\r
72 if (wParam == IDCANCEL)
\r
74 if (HWND hTc = ::GetDlgItem(hwnd, 100))
\r
76 if (HWND hCb = reinterpret_cast<HWND>(::GetWindowLongPtr(hTc, GWLP_USERDATA)))
\r
78 ::EnableWindow(hTc, FALSE);
\r
79 ::SendMessage(hCb, CB_SHOWDROPDOWN, 0, 0);
\r
85 WNDPROC pfnSuper = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_USERDATA);
\r
86 return ::CallWindowProc(pfnSuper, hwnd, message, wParam, lParam);
\r
90 * @brief Handles the CBN_DROPDOWN notification.
\r
91 * @param [in] hCb Handle to ComboBox control.
\r
92 * @param [in] columns Number of columns to fit in one line.
\r
93 * @param [in] fixedPatterns Semicolon delimited list of wildcard patterns.
\r
94 * @param [in] allowUserAddedPatterns Whether to allow user-added patterns
\r
96 void WildcardDropList::OnDropDown(HWND hCb, int columns, LPCTSTR fixedPatterns, bool allowUserAddedPatterns)
\r
99 info.cbSize = sizeof info;
\r
100 if (!::GetComboBoxInfo(hCb, &info))
\r
103 ::GetClientRect(info.hwndList, &rc);
\r
104 ::GetWindowRect(hCb, &rcCombo);
\r
105 int const cxCross = ::GetSystemMetrics(SM_CXVSCROLL);
\r
106 ::CreateWindow(WC_BUTTON, NULL,
\r
107 WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
\r
108 rc.right - cxCross, 0, cxCross, cxCross,
\r
109 info.hwndList, reinterpret_cast<HMENU>(IDCANCEL), NULL, NULL);
\r
110 HWND const hTc = ::CreateWindow(WC_TABCONTROL, NULL,
\r
111 WS_CHILD | WS_VISIBLE | WS_DISABLED | TCS_BUTTONS |
\r
112 TCS_FIXEDWIDTH | TCS_FORCELABELLEFT | TCS_MULTILINE,
\r
113 0, 0, rc.right, 10000,
\r
114 info.hwndList, reinterpret_cast<HMENU>(100), NULL, NULL);
\r
115 ::SetWindowLongPtr(hTc, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(hCb));
\r
116 ::SendMessage(hTc, WM_SETFONT, ::SendMessage(hCb, WM_GETFONT, 0, 0), 0);
\r
117 TabCtrl_SetItemSize(hTc, (rc.right - cxCross) / columns - 3, (rcCombo.bottom - rcCombo.top));
\r
118 int const len = ::GetWindowTextLength(hCb) + 1;
\r
119 TCHAR *const patterns = static_cast<TCHAR *>(_alloca(len * sizeof(TCHAR)));
\r
120 ::GetWindowText(hCb, patterns, len);
\r
122 LPCTSTR pch = fixedPatterns;
\r
123 while (size_t const cch = _tcscspn(pch += _tcsspn(pch, _T("; ")), _T("; ")))
\r
126 *std::copy<>(pch, pch + std::min<>(cch, _countof(text) - 1), text) = _T('\0');
\r
128 item.dwStateMask = TCIS_HIGHLIGHTED;
\r
129 item.dwState = (patterns[0] && PathMatchSpec(text, patterns)) ? TCIS_HIGHLIGHTED : 0;
\r
130 item.pszText = text;
\r
131 item.mask = TCIF_TEXT;
\r
132 TabCtrl_InsertItem(hTc, i, &item);
\r
133 item.mask = TCIF_STATE;
\r
134 TabCtrl_SetItem(hTc, i, &item);
\r
138 if (allowUserAddedPatterns)
\r
141 while (size_t const cch = _tcscspn(pch += _tcsspn(pch, _T("; ")), _T("; ")))
\r
144 *std::copy<>(pch, pch + std::min<>(cch, _countof(text) - 1), text) = _T('\0');
\r
145 if (!fixedPatterns[0] || !PathMatchSpec(text, fixedPatterns))
\r
148 item.dwStateMask = TCIS_HIGHLIGHTED;
\r
149 item.dwState = TCIS_HIGHLIGHTED;
\r
150 item.pszText = text;
\r
151 item.mask = TCIF_TEXT;
\r
152 TabCtrl_InsertItem(hTc, i, &item);
\r
153 item.mask = TCIF_STATE;
\r
154 TabCtrl_SetItem(hTc, i, &item);
\r
160 TabCtrl_SetCurSel(hTc, -1);
\r
161 TabCtrl_AdjustRect(hTc, FALSE, &rc);
\r
162 rc.right = static_cast<int>(::SendMessage(hCb, CB_GETDROPPEDWIDTH, 0, 0));
\r
163 ::SetWindowPos(info.hwndList, NULL, 0, 0, rc.right, rc.top, SWP_NOMOVE | SWP_NOZORDER);
\r
164 ::RegisterHotKey(info.hwndList, IDCANCEL, 0, VK_ESCAPE);
\r
165 LONG_PTR pfnSuper = ::SetWindowLongPtr(info.hwndList, GWLP_WNDPROC, (LONG_PTR)LbWndProc);
\r
166 ::SetWindowLongPtr(info.hwndList, GWLP_USERDATA, pfnSuper);
\r
170 * @brief Handles the CBN_CLOSEUP notification.
\r
171 * @param [in] hCb Handle to ComboBox control.
\r
173 bool WildcardDropList::OnCloseUp(HWND hCb)
\r
176 info.cbSize = sizeof info;
\r
177 if (!::GetComboBoxInfo(hCb, &info))
\r
179 ::UnregisterHotKey(info.hwndList, IDCANCEL);
\r
181 if (HWND const hTc = ::GetDlgItem(info.hwndList, 100))
\r
183 if (::IsWindowEnabled(hTc))
\r
186 int const n = TabCtrl_GetItemCount(hTc);
\r
187 TCHAR *const patterns = static_cast<TCHAR *>(
\r
188 _alloca(n * _countof(text) * sizeof(TCHAR)));
\r
189 TCHAR *pch = patterns;
\r
190 for (int i = 0; i < n; ++i)
\r
193 item.pszText = text;
\r
194 item.cchTextMax = _countof(text);
\r
195 item.mask = TCIF_TEXT | TCIF_STATE;
\r
196 item.dwStateMask = TCIS_HIGHLIGHTED;
\r
197 TabCtrl_GetItem(hTc, i, &item);
\r
198 if (item.dwState & TCIS_HIGHLIGHTED)
\r
200 if (pch > patterns)
\r
202 while (TCHAR ch = *item.pszText++)
\r
207 ::SetWindowText(hCb, patterns);
\r
208 ::SendMessage(hCb, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
\r
211 ::DestroyWindow(hTc);
\r
213 LONG_PTR pfnSuper = ::SetWindowLongPtr(info.hwndList, GWLP_USERDATA, 0);
\r
214 ::SetWindowLongPtr(info.hwndList, GWLP_WNDPROC, pfnSuper);
\r
218 LRESULT WildcardDropList::LvWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
220 WNDPROC pfnSuper = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_USERDATA);
\r
224 switch (HIWORD(wParam))
\r
229 OnCloseUp(reinterpret_cast<HWND>(lParam));
\r
230 ::GetWindowText(reinterpret_cast<HWND>(lParam), text, _countof(text));
\r
231 data = ::GetWindowLongPtr(reinterpret_cast<HWND>(lParam), GWLP_USERDATA);
\r
232 ListView_SetItemText(hwnd, SHORT LOWORD(data), SHORT HIWORD(data), text);
\r
233 ::DestroyWindow(reinterpret_cast<HWND>(lParam));
\r
234 ::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)pfnSuper);
\r
238 ::GetWindowText(reinterpret_cast<HWND>(lParam), text, _countof(text));
\r
239 data = ::GetWindowLongPtr(reinterpret_cast<HWND>(lParam), GWLP_USERDATA);
\r
240 ListView_SetItemText(hwnd, SHORT LOWORD(data), SHORT HIWORD(data), text);
\r
245 return ::CallWindowProc(pfnSuper, hwnd, message, wParam, lParam);
\r
248 void WildcardDropList::OnItemActivate(HWND hLv, int iItem, int iSubItem, int columns, LPCTSTR fixedPatterns, bool allowUserAddedPatterns, int limitTextSize)
\r
251 ListView_EnsureVisible(hLv, iItem, FALSE);
\r
252 ListView_GetSubItemRect(hLv, iItem, iSubItem, LVIR_BOUNDS, &rc);
\r
254 ListView_GetItemText(hLv, iItem, iSubItem, text, _countof(text));
\r
255 HWND hCb = ::CreateWindow(WC_COMBOBOX, NULL, WS_CHILD | WS_VISIBLE |
\r
256 WS_TABSTOP | CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_NOINTEGRALHEIGHT,
\r
257 rc.left, rc.top - 1, rc.right - rc.left, 0,
\r
258 hLv, reinterpret_cast<HMENU>(1), NULL, NULL);
\r
259 ::SetWindowLongPtr(hCb, GWLP_USERDATA, MAKELPARAM(iItem, iSubItem));
\r
260 ::SendMessage(hCb, WM_SETFONT, ::SendMessage(hLv, WM_GETFONT, 0, 0), 0);
\r
262 ::SetWindowText(hCb, text);
\r
264 size_t len = _tcslen(text);
\r
265 LPARAM lp = (len << 16) | len;
\r
266 ::SendMessage(hCb, CB_SETEDITSEL, 0, lp);
\r
268 if (limitTextSize > 0)
\r
269 ::SendMessage(hCb, CB_LIMITTEXT, limitTextSize, 0);
\r
271 LONG_PTR pfnSuper = ::SetWindowLongPtr(hLv, GWLP_WNDPROC, (LONG_PTR)LvWndProc);
\r
272 ::SetWindowLongPtr(hLv, GWLP_USERDATA, pfnSuper);
\r
273 OnDropDown(hCb, columns, fixedPatterns, allowUserAddedPatterns);
\r
274 ::SendMessage(hCb, CB_SHOWDROPDOWN, TRUE, 0);
\r