OSDN Git Service

refactor
[winmerge-jp/winmerge-jp.git] / Src / WildcardDropList.cpp
1 // WildcardDropList.cpp\r
2 // Copyright (c) datadiode\r
3 // SPDX-License-Identifier: WTFPL\r
4 \r
5 #include "stdafx.h"\r
6 #if 0 // Change to 1 if in doubt whether stdafx.h includes them already\r
7 #include <windows.h>\r
8 #include <shlwapi.h>\r
9 #include <commctrl.h>\r
10 #include <stdlib.h>\r
11 #include <malloc.h>\r
12 #include <tchar.h>\r
13 #include <algorithm>\r
14 #endif\r
15 \r
16 #include "WildcardDropList.h"\r
17 \r
18 /**\r
19  * @brief DropList window procedure.\r
20  */\r
21 LRESULT WildcardDropList::LbWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
22 {\r
23         switch (message)\r
24         {\r
25         case WM_DRAWITEM:\r
26                 if (wParam == IDCANCEL)\r
27                 {\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
30                         return 1;\r
31                 }\r
32                 break;\r
33         case WM_LBUTTONDOWN:\r
34                 if (HWND hTc = GetDlgItem(hwnd, 100))\r
35                 {\r
36                         TCHITTESTINFO info;\r
37                         POINTSTOPOINT(info.pt, lParam);\r
38                         int i = TabCtrl_HitTest(hTc, &info);\r
39                         if (i != -1)\r
40                         {\r
41                                 TCITEM item;\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
48                         }\r
49                         else if (HWND hwndHit = ::ChildWindowFromPoint(hwnd, info.pt))\r
50                         {\r
51                                 if (hwndHit != hwnd && ::GetDlgCtrlID(hwndHit) == IDCANCEL)\r
52                                 {\r
53                                         if (HWND hCb = reinterpret_cast<HWND>(::GetWindowLongPtr(hTc, GWLP_USERDATA)))\r
54                                         {\r
55                                                 ::EnableWindow(hTc, FALSE);\r
56                                                 ::SendMessage(hCb, CB_SHOWDROPDOWN, 0, 0);\r
57                                         }\r
58                                 }\r
59                         }\r
60                 }\r
61                 break;\r
62         case WM_RBUTTONDOWN:\r
63                 if (HWND hTc = ::GetDlgItem(hwnd, 100))\r
64                 {\r
65                         if (HWND hCb = reinterpret_cast<HWND>(::GetWindowLongPtr(hTc, GWLP_USERDATA)))\r
66                         {\r
67                                 ::SendMessage(hCb, CB_SHOWDROPDOWN, 0, 0);\r
68                         }\r
69                 }\r
70                 break;\r
71         case WM_HOTKEY:\r
72                 if (wParam == IDCANCEL)\r
73                 {\r
74                         if (HWND hTc = ::GetDlgItem(hwnd, 100))\r
75                         {\r
76                                 if (HWND hCb = reinterpret_cast<HWND>(::GetWindowLongPtr(hTc, GWLP_USERDATA)))\r
77                                 {\r
78                                         ::EnableWindow(hTc, FALSE);\r
79                                         ::SendMessage(hCb, CB_SHOWDROPDOWN, 0, 0);\r
80                                 }\r
81                         }\r
82                 }\r
83                 break;\r
84         }\r
85         WNDPROC pfnSuper = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_USERDATA);\r
86         return ::CallWindowProc(pfnSuper, hwnd, message, wParam, lParam);\r
87 }\r
88 \r
89 /**\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
95  */\r
96 void WildcardDropList::OnDropDown(HWND hCb, int columns, LPCTSTR fixedPatterns, bool allowUserAddedPatterns)\r
97 {\r
98         COMBOBOXINFO info{ sizeof info };\r
99         if (!::GetComboBoxInfo(hCb, &info))\r
100                 return;\r
101         RECT rc, rcCombo;\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
120         int i = 0;\r
121         LPCTSTR pch = fixedPatterns;\r
122         while (size_t const cch = _tcscspn(pch += _tcsspn(pch, _T("; ")), _T("; ")))\r
123         {\r
124                 TCHAR text[20];\r
125                 *std::copy<>(pch, pch + std::min<>(cch, _countof(text) - 1), text) = _T('\0');\r
126                 TCITEM item;\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
134                 ++i;\r
135                 pch += cch;\r
136         }\r
137         if (allowUserAddedPatterns)\r
138         {\r
139                 pch = patterns;\r
140                 while (size_t const cch = _tcscspn(pch += _tcsspn(pch, _T("; ")), _T("; ")))\r
141                 {\r
142                         TCHAR text[20];\r
143                         *std::copy<>(pch, pch + std::min<>(cch, _countof(text) - 1), text) = _T('\0');\r
144                         if (!fixedPatterns[0] || !PathMatchSpec(text, fixedPatterns))\r
145                         {\r
146                                 TCITEM item;\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
154                                 ++i;\r
155                         }\r
156                         pch += cch;\r
157                 }\r
158         }\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
166 }\r
167 \r
168 /**\r
169  * @brief Handles the CBN_CLOSEUP notification.\r
170  * @param [in] hCb Handle to ComboBox control.\r
171  */\r
172 bool WildcardDropList::OnCloseUp(HWND hCb)\r
173 {\r
174         COMBOBOXINFO info{ sizeof info };\r
175         if (!::GetComboBoxInfo(hCb, &info))\r
176                 return false;\r
177         ::UnregisterHotKey(info.hwndList, IDCANCEL);\r
178         bool ret = false;\r
179         if (HWND const hTc = ::GetDlgItem(info.hwndList, 100))\r
180         {\r
181                 if (::IsWindowEnabled(hTc))\r
182                 {\r
183                         TCHAR text[20];\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
189                         {\r
190                                 TCITEM item;\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
197                                 {\r
198                                         if (pch > patterns)\r
199                                                 *pch++ = _T(';');\r
200                                         while (TCHAR ch = *item.pszText++)\r
201                                                 *pch++ = ch;\r
202                                 }\r
203                         }\r
204                         *pch = _T('\0');\r
205                         ::SetWindowText(hCb, patterns);\r
206                         ::SendMessage(hCb, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));\r
207                         ret = true;\r
208                 }\r
209                 ::DestroyWindow(hTc);\r
210         }\r
211         LONG_PTR pfnSuper = ::SetWindowLongPtr(info.hwndList, GWLP_USERDATA, 0);\r
212         ::SetWindowLongPtr(info.hwndList, GWLP_WNDPROC, pfnSuper);\r
213         return ret;\r
214 }\r
215 \r
216 LRESULT WildcardDropList::LvWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
217 {\r
218         WNDPROC pfnSuper = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_USERDATA);\r
219         switch (message)\r
220         {\r
221         case WM_COMMAND:\r
222                 switch (HIWORD(wParam))\r
223                 {\r
224                         TCHAR text[4096];\r
225                         LONG_PTR data;\r
226                 case CBN_CLOSEUP:\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
233                         ::SetFocus(hwnd);\r
234                         break;\r
235                 case CBN_SELENDOK:\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
239                         break;\r
240                 }\r
241                 return 0;\r
242         }\r
243         return ::CallWindowProc(pfnSuper, hwnd, message, wParam, lParam);\r
244 }\r
245 \r
246 void WildcardDropList::OnItemActivate(HWND hLv, int iItem, int iSubItem, int columns, LPCTSTR fixedPatterns, bool allowUserAddedPatterns, int limitTextSize)\r
247 {\r
248         RECT rc;\r
249         ListView_EnsureVisible(hLv, iItem, FALSE);\r
250         ListView_GetSubItemRect(hLv, iItem, iSubItem, LVIR_BOUNDS, &rc);\r
251         TCHAR text[4096];\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
259         ::SetFocus(hCb);\r
260         ::SetWindowText(hCb, text);\r
261 \r
262         size_t len = _tcslen(text);\r
263         LPARAM lp = (len << 16) | len; \r
264         ::SendMessage(hCb, CB_SETEDITSEL, 0, lp);\r
265 \r
266         if (limitTextSize > 0)\r
267                 ::SendMessage(hCb, CB_LIMITTEXT, limitTextSize, 0);\r
268 \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
273 }\r