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
98 COMBOBOXINFO info{ sizeof info };
\r
99 if (!::GetComboBoxInfo(hCb, &info))
\r
102 ::GetClientRect(info.hwndList, &rc);
\r
103 ::GetWindowRect(hCb, &rcCombo);
\r
104 int const cxCross = ::GetSystemMetrics(SM_CXVSCROLL);
\r
105 ::CreateWindow(WC_BUTTON, NULL,
\r
106 WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
\r
107 rc.right - cxCross, 0, cxCross, cxCross,
\r
108 info.hwndList, reinterpret_cast<HMENU>(IDCANCEL), NULL, NULL);
\r
109 HWND const hTc = ::CreateWindow(WC_TABCONTROL, NULL,
\r
110 WS_CHILD | WS_VISIBLE | WS_DISABLED | TCS_BUTTONS |
\r
111 TCS_FIXEDWIDTH | TCS_FORCELABELLEFT | TCS_MULTILINE,
\r
112 0, 0, rc.right, 10000,
\r
113 info.hwndList, reinterpret_cast<HMENU>(100), NULL, NULL);
\r
114 ::SetWindowLongPtr(hTc, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(hCb));
\r
115 ::SendMessage(hTc, WM_SETFONT, ::SendMessage(hCb, WM_GETFONT, 0, 0), 0);
\r
116 TabCtrl_SetItemSize(hTc, (rc.right - cxCross) / columns - 3, (rcCombo.bottom - rcCombo.top));
\r
117 int const len = ::GetWindowTextLength(hCb) + 1;
\r
118 TCHAR *const patterns = static_cast<TCHAR *>(_alloca(len * sizeof(TCHAR)));
\r
119 ::GetWindowText(hCb, patterns, len);
\r
121 LPCTSTR pch = fixedPatterns;
\r
122 while (size_t const cch = _tcscspn(pch += _tcsspn(pch, _T("; ")), _T("; ")))
\r
125 *std::copy<>(pch, pch + std::min<>(cch, _countof(text) - 1), text) = _T('\0');
\r
127 item.dwStateMask = TCIS_HIGHLIGHTED;
\r
128 item.dwState = (patterns[0] && PathMatchSpec(text, patterns)) ? TCIS_HIGHLIGHTED : 0;
\r
129 item.pszText = text;
\r
130 item.mask = TCIF_TEXT;
\r
131 TabCtrl_InsertItem(hTc, i, &item);
\r
132 item.mask = TCIF_STATE;
\r
133 TabCtrl_SetItem(hTc, i, &item);
\r
137 if (allowUserAddedPatterns)
\r
140 while (size_t const cch = _tcscspn(pch += _tcsspn(pch, _T("; ")), _T("; ")))
\r
143 *std::copy<>(pch, pch + std::min<>(cch, _countof(text) - 1), text) = _T('\0');
\r
144 if (!fixedPatterns[0] || !PathMatchSpec(text, fixedPatterns))
\r
147 item.dwStateMask = TCIS_HIGHLIGHTED;
\r
148 item.dwState = TCIS_HIGHLIGHTED;
\r
149 item.pszText = text;
\r
150 item.mask = TCIF_TEXT;
\r
151 TabCtrl_InsertItem(hTc, i, &item);
\r
152 item.mask = TCIF_STATE;
\r
153 TabCtrl_SetItem(hTc, i, &item);
\r
159 TabCtrl_SetCurSel(hTc, -1);
\r
160 TabCtrl_AdjustRect(hTc, FALSE, &rc);
\r
161 rc.right = static_cast<int>(::SendMessage(hCb, CB_GETDROPPEDWIDTH, 0, 0));
\r
162 ::SetWindowPos(info.hwndList, NULL, 0, 0, rc.right, rc.top, SWP_NOMOVE | SWP_NOZORDER);
\r
163 ::RegisterHotKey(info.hwndList, IDCANCEL, 0, VK_ESCAPE);
\r
164 LONG_PTR pfnSuper = ::SetWindowLongPtr(info.hwndList, GWLP_WNDPROC, (LONG_PTR)LbWndProc);
\r
165 ::SetWindowLongPtr(info.hwndList, GWLP_USERDATA, pfnSuper);
\r
169 * @brief Handles the CBN_CLOSEUP notification.
\r
170 * @param [in] hCb Handle to ComboBox control.
\r
172 bool WildcardDropList::OnCloseUp(HWND hCb)
\r
174 COMBOBOXINFO info{ sizeof info };
\r
175 if (!::GetComboBoxInfo(hCb, &info))
\r
177 ::UnregisterHotKey(info.hwndList, IDCANCEL);
\r
179 if (HWND const hTc = ::GetDlgItem(info.hwndList, 100))
\r
181 if (::IsWindowEnabled(hTc))
\r
184 int const n = TabCtrl_GetItemCount(hTc);
\r
185 TCHAR *const patterns = static_cast<TCHAR *>(
\r
186 _alloca(n * _countof(text) * sizeof(TCHAR)));
\r
187 TCHAR *pch = patterns;
\r
188 for (int i = 0; i < n; ++i)
\r
191 item.pszText = text;
\r
192 item.cchTextMax = _countof(text);
\r
193 item.mask = TCIF_TEXT | TCIF_STATE;
\r
194 item.dwStateMask = TCIS_HIGHLIGHTED;
\r
195 TabCtrl_GetItem(hTc, i, &item);
\r
196 if (item.dwState & TCIS_HIGHLIGHTED)
\r
198 if (pch > patterns)
\r
200 while (TCHAR ch = *item.pszText++)
\r
205 ::SetWindowText(hCb, patterns);
\r
206 ::SendMessage(hCb, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));
\r
209 ::DestroyWindow(hTc);
\r
211 LONG_PTR pfnSuper = ::SetWindowLongPtr(info.hwndList, GWLP_USERDATA, 0);
\r
212 ::SetWindowLongPtr(info.hwndList, GWLP_WNDPROC, pfnSuper);
\r
216 LRESULT WildcardDropList::LvWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
218 WNDPROC pfnSuper = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_USERDATA);
\r
222 switch (HIWORD(wParam))
\r
227 OnCloseUp(reinterpret_cast<HWND>(lParam));
\r
228 ::GetWindowText(reinterpret_cast<HWND>(lParam), text, _countof(text));
\r
229 data = ::GetWindowLongPtr(reinterpret_cast<HWND>(lParam), GWLP_USERDATA);
\r
230 ListView_SetItemText(hwnd, SHORT LOWORD(data), SHORT HIWORD(data), text);
\r
231 ::DestroyWindow(reinterpret_cast<HWND>(lParam));
\r
232 ::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)pfnSuper);
\r
236 ::GetWindowText(reinterpret_cast<HWND>(lParam), text, _countof(text));
\r
237 data = ::GetWindowLongPtr(reinterpret_cast<HWND>(lParam), GWLP_USERDATA);
\r
238 ListView_SetItemText(hwnd, SHORT LOWORD(data), SHORT HIWORD(data), text);
\r
243 return ::CallWindowProc(pfnSuper, hwnd, message, wParam, lParam);
\r
246 void WildcardDropList::OnItemActivate(HWND hLv, int iItem, int iSubItem, int columns, LPCTSTR fixedPatterns, bool allowUserAddedPatterns, int limitTextSize)
\r
249 ListView_EnsureVisible(hLv, iItem, FALSE);
\r
250 ListView_GetSubItemRect(hLv, iItem, iSubItem, LVIR_BOUNDS, &rc);
\r
252 ListView_GetItemText(hLv, iItem, iSubItem, text, _countof(text));
\r
253 HWND hCb = ::CreateWindow(WC_COMBOBOX, NULL, WS_CHILD | WS_VISIBLE |
\r
254 WS_TABSTOP | CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_NOINTEGRALHEIGHT,
\r
255 rc.left, rc.top - 1, rc.right - rc.left, 0,
\r
256 hLv, reinterpret_cast<HMENU>(1), NULL, NULL);
\r
257 ::SetWindowLongPtr(hCb, GWLP_USERDATA, MAKELPARAM(iItem, iSubItem));
\r
258 ::SendMessage(hCb, WM_SETFONT, ::SendMessage(hLv, WM_GETFONT, 0, 0), 0);
\r
260 ::SetWindowText(hCb, text);
\r
262 size_t len = _tcslen(text);
\r
263 LPARAM lp = (len << 16) | len;
\r
264 ::SendMessage(hCb, CB_SETEDITSEL, 0, lp);
\r
266 if (limitTextSize > 0)
\r
267 ::SendMessage(hCb, CB_LIMITTEXT, limitTextSize, 0);
\r
269 LONG_PTR pfnSuper = ::SetWindowLongPtr(hLv, GWLP_WNDPROC, (LONG_PTR)LvWndProc);
\r
270 ::SetWindowLongPtr(hLv, GWLP_USERDATA, pfnSuper);
\r
271 OnDropDown(hCb, columns, fixedPatterns, allowUserAddedPatterns);
\r
272 ::SendMessage(hCb, CB_SHOWDROPDOWN, TRUE, 0);
\r