OSDN Git Service

resource.h: Add IDS_PLUGIN_DESCRIPTION*
[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;\r
99         info.cbSize = sizeof info;\r
100         if (!::GetComboBoxInfo(hCb, &info))\r
101                 return;\r
102         RECT rc, rcCombo;\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
121         int i = 0;\r
122         LPCTSTR pch = fixedPatterns;\r
123         while (size_t const cch = _tcscspn(pch += _tcsspn(pch, _T("; ")), _T("; ")))\r
124         {\r
125                 TCHAR text[20];\r
126                 *std::copy<>(pch, pch + std::min<>(cch, _countof(text) - 1), text) = _T('\0');\r
127                 TCITEM item;\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
135                 ++i;\r
136                 pch += cch;\r
137         }\r
138         if (allowUserAddedPatterns)\r
139         {\r
140                 pch = patterns;\r
141                 while (size_t const cch = _tcscspn(pch += _tcsspn(pch, _T("; ")), _T("; ")))\r
142                 {\r
143                         TCHAR text[20];\r
144                         *std::copy<>(pch, pch + std::min<>(cch, _countof(text) - 1), text) = _T('\0');\r
145                         if (!fixedPatterns[0] || !PathMatchSpec(text, fixedPatterns))\r
146                         {\r
147                                 TCITEM item;\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
155                                 ++i;\r
156                         }\r
157                         pch += cch;\r
158                 }\r
159         }\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
167 }\r
168 \r
169 /**\r
170  * @brief Handles the CBN_CLOSEUP notification.\r
171  * @param [in] hCb Handle to ComboBox control.\r
172  */\r
173 bool WildcardDropList::OnCloseUp(HWND hCb)\r
174 {\r
175         COMBOBOXINFO info;\r
176         info.cbSize = sizeof info;\r
177         if (!::GetComboBoxInfo(hCb, &info))\r
178                 return false;\r
179         ::UnregisterHotKey(info.hwndList, IDCANCEL);\r
180         bool ret = false;\r
181         if (HWND const hTc = ::GetDlgItem(info.hwndList, 100))\r
182         {\r
183                 if (::IsWindowEnabled(hTc))\r
184                 {\r
185                         TCHAR text[20];\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
191                         {\r
192                                 TCITEM item;\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
199                                 {\r
200                                         if (pch > patterns)\r
201                                                 *pch++ = _T(';');\r
202                                         while (TCHAR ch = *item.pszText++)\r
203                                                 *pch++ = ch;\r
204                                 }\r
205                         }\r
206                         *pch = _T('\0');\r
207                         ::SetWindowText(hCb, patterns);\r
208                         ::SendMessage(hCb, CB_SETEDITSEL, 0, MAKELPARAM(0, -1));\r
209                         ret = true;\r
210                 }\r
211                 ::DestroyWindow(hTc);\r
212         }\r
213         LONG_PTR pfnSuper = ::SetWindowLongPtr(info.hwndList, GWLP_USERDATA, 0);\r
214         ::SetWindowLongPtr(info.hwndList, GWLP_WNDPROC, pfnSuper);\r
215         return ret;\r
216 }\r
217 \r
218 LRESULT WildcardDropList::LvWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
219 {\r
220         WNDPROC pfnSuper = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_USERDATA);\r
221         switch (message)\r
222         {\r
223         case WM_COMMAND:\r
224                 switch (HIWORD(wParam))\r
225                 {\r
226                         TCHAR text[4096];\r
227                         LONG_PTR data;\r
228                 case CBN_CLOSEUP:\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
235                         ::SetFocus(hwnd);\r
236                         break;\r
237                 case CBN_SELENDOK:\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
241                         break;\r
242                 }\r
243                 return 0;\r
244         }\r
245         return ::CallWindowProc(pfnSuper, hwnd, message, wParam, lParam);\r
246 }\r
247 \r
248 void WildcardDropList::OnItemActivate(HWND hLv, int iItem, int iSubItem, int columns, LPCTSTR fixedPatterns, bool allowUserAddedPatterns, int limitTextSize)\r
249 {\r
250         RECT rc;\r
251         ListView_EnsureVisible(hLv, iItem, FALSE);\r
252         ListView_GetSubItemRect(hLv, iItem, iSubItem, LVIR_BOUNDS, &rc);\r
253         TCHAR text[4096];\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
261         ::SetFocus(hCb);\r
262         ::SetWindowText(hCb, text);\r
263 \r
264         size_t len = _tcslen(text);\r
265         LPARAM lp = (len << 16) | len; \r
266         ::SendMessage(hCb, CB_SETEDITSEL, 0, lp);\r
267 \r
268         if (limitTextSize > 0)\r
269                 ::SendMessage(hCb, CB_LIMITTEXT, limitTextSize, 0);\r
270 \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
275 }\r