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 
Run()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 
collect_programs(const ShellPath & path)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 
free_dirs()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 
FindProgramDlg(HWND hwnd)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 
~FindProgramDlg()141 FindProgramDlg::~FindProgramDlg()
142 {
143 	_thread.Stop();
144 
145 	unregister_pretranslate(_hwnd);
146 }
147 
148 
Refresh(bool delete_cache)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 
collect_programs_callback(Entry * entry,void * param)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 
add_entry(const FPDEntry & cache_entry)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 
WndProc(UINT nmsg,WPARAM wparam,LPARAM lparam)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 
Command(int id,int code)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 
LaunchSelected()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 
Notify(int id,NMHDR * pnmh)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 
CompareFunc(LPARAM lparam1,LPARAM lparam2,LPARAM lparamSort)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 
CheckEntries()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 }
422