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