[EXPLORER][EXPLORER_NEW]
[reactos.git] / base / shell / explorer / dialogs / searchprogram.cpp
1 /*
2 * Copyright 2003, 2004, 2005 Martin Fuchs
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19
20 //
21 // Explorer clone
22 //
23 // dialogs/searchprogram.cpp
24 //
25 // Explorer dialogs
26 //
27 // Martin Fuchs, 02.10.2003
28 //
29
30
31 #include <precomp.h>
32
33 #include "searchprogram.h"
34
35
36 int CollectProgramsThread::Run()
37 {
38 try {
39 collect_programs(SpecialFolderPath(CSIDL_COMMON_PROGRAMS, _hwnd));
40 } catch(COMException&) {
41 }
42
43 if (_alive)
44 try {
45 collect_programs(SpecialFolderPath(CSIDL_PROGRAMS, _hwnd));
46 } catch(COMException&) {
47 }
48
49 if (_alive)
50 _cache_valid = true;
51
52 return 0;
53 }
54
55 void CollectProgramsThread::collect_programs(const ShellPath& path)
56 {
57 ShellDirectory* dir = new ShellDirectory(GetDesktopFolder(), path, 0);
58 _dirs.push(dir);
59
60 dir->smart_scan(SORT_NONE);
61
62 for(Entry*entry=dir->_down; _alive && entry; entry=entry->_next) {
63 if (entry->_shell_attribs & SFGAO_HIDDEN)
64 continue;
65
66 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
67 collect_programs(entry->create_absolute_pidl());
68 else if (entry->_shell_attribs & SFGAO_LINK)
69 if (_alive)
70 _callback(entry, _para);
71 }
72 }
73
74 void CollectProgramsThread::free_dirs()
75 {
76 while(!_dirs.empty()) {
77 ShellDirectory* dir = _dirs.top();
78 dir->free_subentries();
79 _dirs.pop();
80 }
81 }
82
83
84 #ifdef _MSC_VER
85 #pragma warning(disable: 4355)
86 #endif
87
88 FindProgramDlg::FindProgramDlg(HWND hwnd)
89 : super(hwnd),
90 _list_ctrl(GetDlgItem(hwnd, IDC_PROGRAMS_FOUND)),
91 _thread(collect_programs_callback, hwnd, this),
92 _sort(_list_ctrl, CompareFunc/*, (LPARAM)this*/)
93 {
94 TCHAR szTemp[256];
95 const size_t nChars = sizeof(szTemp)/sizeof(*szTemp);
96 SetWindowIcon(hwnd, IDI_SEARCH);
97
98 _resize_mgr.Add(IDC_FILTER, RESIZE_X);
99 _resize_mgr.Add(IDC_CHECK_ENTRIES, MOVE_X);
100 _resize_mgr.Add(IDC_PROGRAMS_FOUND, RESIZE);
101
102 _resize_mgr.Resize(+520, +300);
103
104 _haccel = LoadAccelerators(g_Globals._hInstance, MAKEINTRESOURCE(IDA_SEARCH_PROGRAM));
105
106 (void)ListView_SetImageList(_list_ctrl, g_Globals._icon_cache.get_sys_imagelist(), LVSIL_SMALL);
107
108 LV_COLUMN column = {LVCF_FMT|LVCF_WIDTH|LVCF_TEXT, LVCFMT_LEFT, 250};
109
110 LoadString(g_Globals._hInstance, IDS_NAMECOLUMN, szTemp, nChars);
111 column.pszText = szTemp;
112 ListView_InsertColumn(_list_ctrl, 0, &column);
113
114 column.cx = 300;
115 LoadString(g_Globals._hInstance, IDS_PATHCOLUMN, szTemp, nChars);
116 column.pszText = szTemp;
117 ListView_InsertColumn(_list_ctrl, 1, &column);
118
119 column.cx = 400;
120 LoadString(g_Globals._hInstance, IDS_MENUCOLUMN, szTemp, nChars);
121 column.pszText = szTemp;
122 ListView_InsertColumn(_list_ctrl, 2, &column);
123
124 ListView_SetExtendedListViewStyleEx(_list_ctrl, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
125
126 _common_programs = SpecialFolderFSPath(CSIDL_COMMON_PROGRAMS, hwnd);
127 if (!_common_programs.empty())
128 _common_programs.append(_T("\\"));
129
130 _user_programs = SpecialFolderFSPath(CSIDL_PROGRAMS, hwnd);
131 if (!_user_programs.empty())
132 _user_programs.append(_T("\\"));
133
134 CenterWindow(hwnd);
135
136 Refresh();
137
138 register_pretranslate(hwnd);
139 }
140
141 FindProgramDlg::~FindProgramDlg()
142 {
143 _thread.Stop();
144
145 unregister_pretranslate(_hwnd);
146 }
147
148
149 void FindProgramDlg::Refresh(bool delete_cache)
150 {
151 WaitCursor wait;
152
153 _thread.Stop();
154
155 TCHAR buffer[1024];
156 GetWindowText(GetDlgItem(_hwnd, IDC_FILTER), buffer, COUNTOF(buffer));
157 CharLower(buffer);
158 _lwr_filter = buffer;
159
160 HiddenWindow hide_listctrl(_list_ctrl);
161
162 ListView_DeleteAllItems(_list_ctrl);
163
164 if (delete_cache || !_thread._cache_valid) {
165 _thread.free_dirs();
166 _thread.Start();
167 } else {
168 for(FPDCache::const_iterator it=_cache.begin(); it!=_cache.end(); ++it)
169 add_entry(*it);
170 }
171 }
172
173 void FindProgramDlg::collect_programs_callback(Entry* entry, void* param)
174 {
175 FindProgramDlg* pThis = (FindProgramDlg*) param;
176
177 IShellLink* pShellLink;
178 HRESULT hr = entry->GetUIObjectOf(pThis->_hwnd, IID_IShellLink, (LPVOID*)&pShellLink);
179
180 if (SUCCEEDED(hr)) {
181 ShellLinkPtr shell_link(pShellLink);
182
183 shell_link->Release();
184
185 /*hr = pShellLink->Resolve(pThis->_hwnd, SLR_NO_UI);
186 if (SUCCEEDED(hr))*/ {
187 WIN32_FIND_DATA wfd;
188 TCHAR path[MAX_PATH];
189
190 hr = pShellLink->GetPath(path, COUNTOF(path)-1, &wfd, SLGP_UNCPRIORITY);
191
192 if (SUCCEEDED(hr)) {
193 TCHAR entry_path[MAX_PATH];
194
195 entry->get_path(entry_path, COUNTOF(entry_path));
196
197 String menu_path;
198
199 int len = pThis->_common_programs.size();
200
201 if (len && !_tcsnicmp(entry_path, pThis->_common_programs, len))
202 menu_path = ResString(IDS_ALL_USERS) + (String(entry_path)+len);
203 else if ((len=pThis->_user_programs.size()) && !_tcsnicmp(entry_path, pThis->_user_programs, len))
204 menu_path = String(entry_path)+len;
205
206 // store info in cache
207 FPDEntry new_entry;
208
209 new_entry._entry = entry;
210 new_entry._menu_path = menu_path;
211 new_entry._path = path;
212 new_entry._idxIcon = I_IMAGECALLBACK;
213
214 pThis->_cache.push_front(new_entry);
215 FPDEntry& cache_entry = pThis->_cache.front();
216
217 Lock lock(pThis->_thread._crit_sect);
218
219 // resolve deadlocks while executing Thread::Stop()
220 if (!pThis->_thread.is_alive())
221 return;
222
223 pThis->add_entry(cache_entry);
224 }
225 }
226 }
227 }
228
229 void FindProgramDlg::add_entry(const FPDEntry& cache_entry)
230 {
231 String lwr_path = cache_entry._path;
232 String lwr_name = cache_entry._entry->_display_name;
233
234 lwr_path.toLower();
235 lwr_name.toLower();
236
237 if (_lwr_filter.empty())
238 if (_tcsstr(lwr_name, _T("uninstal")) || _tcsstr(lwr_name, _T("deinstal"))) // filter out deinstallation links
239 return;
240
241 if (!_tcsstr(lwr_path, _lwr_filter) && !_tcsstr(lwr_name, _lwr_filter))
242 return;
243
244 LV_ITEM item = {LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM, INT_MAX};
245
246 item.pszText = cache_entry._entry->_display_name;
247 item.iImage = cache_entry._idxIcon;
248 item.lParam = (LPARAM) &cache_entry;
249 item.iItem = ListView_InsertItem(_list_ctrl, &item); // We could use the information in _sort to enable manual sorting while populating the list.
250
251 item.mask = LVIF_TEXT;
252
253 item.iSubItem = 1;
254 item.pszText = (LPTSTR)(LPCTSTR)cache_entry._path;
255 ListView_SetItem(_list_ctrl, &item);
256
257 item.iSubItem = 2;
258 item.pszText = (LPTSTR)(LPCTSTR)cache_entry._menu_path;
259 ListView_SetItem(_list_ctrl, &item);
260 }
261
262 LRESULT FindProgramDlg::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
263 {
264 switch(nmsg) {
265 case WM_CLOSE:
266 (void)ListView_SetImageList(_list_ctrl, 0, LVSIL_SMALL); // detach system image list
267 goto def;
268
269 case PM_TRANSLATE_MSG: {
270 MSG* pmsg = (MSG*) lparam;
271
272 if (TranslateAccelerator(_hwnd, _haccel, pmsg))
273 return TRUE;
274
275 return FALSE;}
276
277 default: def:
278 return super::WndProc(nmsg, wparam, lparam);
279 }
280
281 return 0;
282 }
283
284 int FindProgramDlg::Command(int id, int code)
285 {
286 if (code == BN_CLICKED) {
287 switch(id) {
288 case ID_REFRESH:
289 Refresh(true);
290 break;
291
292 case IDOK:
293 LaunchSelected();
294 break;
295
296 case IDC_CHECK_ENTRIES:
297 CheckEntries();
298 break;
299
300 default:
301 return super::Command(id, code);
302 }
303
304 return 0;
305 }
306 else if (code == EN_CHANGE) {
307 switch(id) {
308 case IDC_FILTER:
309 Refresh();
310 break;
311 }
312
313 return 0;
314 }
315
316 return 1;
317 }
318
319 void FindProgramDlg::LaunchSelected()
320 {
321 Lock lock(_thread._crit_sect);
322
323 int count = ListView_GetSelectedCount(_list_ctrl);
324
325 if (count > 1)
326 if (MessageBox(_hwnd, ResString(IDS_LAUNCH_MANY_PROGRAMS), ResString(IDS_TITLE), MB_OKCANCEL) != IDOK)
327 return;
328
329 for(int idx=-1; (idx=ListView_GetNextItem(_list_ctrl, idx, LVNI_SELECTED))!=-1; ) {
330 LPARAM lparam = ListView_GetItemData(_list_ctrl, idx);
331
332 if (lparam) {
333 FPDEntry& cache_entry = *(FPDEntry*)lparam;
334 cache_entry._entry->launch_entry(_hwnd);
335 }
336 }
337 }
338
339 int FindProgramDlg::Notify(int id, NMHDR* pnmh)
340 {
341 switch(pnmh->code) {
342 case LVN_GETDISPINFO: {
343 LV_DISPINFO* pDispInfo = (LV_DISPINFO*) pnmh;
344
345 if (pnmh->hwndFrom == _list_ctrl) {
346 if (pDispInfo->item.mask & LVIF_IMAGE) {
347 FPDEntry& cache_entry = *(FPDEntry*)pDispInfo->item.lParam;
348 Entry* entry = cache_entry._entry;
349
350 if (entry->_icon_id == ICID_UNKNOWN)
351 entry->_icon_id = entry->extract_icon(ICF_SYSCACHE);
352
353 pDispInfo->item.iImage = g_Globals._icon_cache.get_icon(entry->_icon_id).get_sysiml_idx();
354 pDispInfo->item.mask |= LVIF_DI_SETITEM;
355
356 return 1;
357 }
358 }}
359 break;
360
361 case NM_DBLCLK:
362 if (pnmh->hwndFrom == _list_ctrl)
363 LaunchSelected();
364 /*{
365 Lock lock(_thread._crit_sect);
366
367 LPNMLISTVIEW pnmv = (LPNMLISTVIEW) pnmh;
368 LPARAM lparam = ListView_GetItemData(pnmh->hwndFrom, pnmv->iItem);
369
370 if (lparam) {
371 FPDEntry& cache_entry = *(FPDEntry*)lparam;
372 cache_entry._entry->launch_entry(_hwnd);
373 }
374 }*/
375 break;
376
377 case HDN_ITEMCLICK: {
378 WaitCursor wait;
379 NMHEADER* phdr = (NMHEADER*)pnmh;
380
381 if (GetParent(pnmh->hwndFrom) == _list_ctrl) {
382 if (_thread._cache_valid) { // disable manual sorting while populating the list
383 _sort.toggle_sort(phdr->iItem);
384 _sort.sort();
385 }
386 }
387 break;}
388 }
389
390 return 0;
391 }
392
393 int CALLBACK FindProgramDlg::CompareFunc(LPARAM lparam1, LPARAM lparam2, LPARAM lparamSort)
394 {
395 ListSort* sort = (ListSort*)lparamSort;
396
397 FPDEntry& a = *(FPDEntry*)lparam1;
398 FPDEntry& b = *(FPDEntry*)lparam2;
399
400 int cmp = 0;
401
402 switch(sort->_sort_crit) {
403 case 0:
404 cmp = _tcsicoll(a._entry->_display_name, b._entry->_display_name);
405 break;
406
407 case 1:
408 cmp = _tcsicoll(a._path, b._path);
409 break;
410
411 case 2:
412 cmp = _tcsicoll(a._menu_path, b._menu_path);
413 }
414
415 return sort->_direction? -cmp: cmp;
416 }
417
418 void FindProgramDlg::CheckEntries()
419 {
420 ///@todo check all entries for existing targets, display a list of not working entries and ask the user for permission to delete them
421 }