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 } 422