Sync with trunk r63647.
[reactos.git] / base / shell / explorer / explorer.cpp
1 /*
2 * Copyright 2003, 2004, 2005, 2006 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 // explorer.cpp
24 //
25 // Martin Fuchs, 23.07.2003
26 //
27 // Credits: Thanks to Leon Finker for his explorer cabinet window example
28 //
29
30
31 #include <precomp.h> // <precomp.h> instead of "precomp.h" because the ROS build system needs this to find the precompiled header file (*.gch) in the output directory tree
32
33 #include <shlwapi.h>
34 #include <locale.h> // for setlocale()
35
36 #ifndef __WINE__
37 #include <io.h> // for dup2()
38 #include <fcntl.h> // for _O_RDONLY
39 #endif
40
41 //#include "dialogs/settings.h" // for MdiSdiDlg
42
43 #include "services/shellservices.h"
44
45
46 extern "C" int initialize_gdb_stub(); // start up GDB stub
47
48
49 DynamicLoadLibFct<void(__stdcall*)(BOOL)> g_SHDOCVW_ShellDDEInit(TEXT("SHDOCVW"), 118);
50
51
52 ExplorerGlobals g_Globals;
53 boolean SelectOpt=FALSE;
54
55
56 ExplorerGlobals::ExplorerGlobals()
57 {
58 _hInstance = 0;
59 _cfStrFName = 0;
60
61 #ifndef ROSSHELL
62 _hframeClass = 0;
63 _hMainWnd = 0;
64 _desktop_mode = false;
65 _prescan_nodes = false;
66 #endif
67
68 _log = NULL;
69 _SHRestricted = 0;
70 _hwndDesktopBar = 0;
71 _hwndShellView = 0;
72 _hwndDesktop = 0;
73 }
74
75
76 void ExplorerGlobals::init(HINSTANCE hInstance)
77 {
78 _hInstance = hInstance;
79 _SHRestricted = (DWORD(STDAPICALLTYPE*)(RESTRICTIONS)) GetProcAddress(GetModuleHandle(TEXT("SHELL32")), "SHRestricted");
80 _icon_cache.init();
81 }
82
83
84 void ExplorerGlobals::read_persistent()
85 {
86 // read configuration file
87 _cfg_dir.printf(TEXT("%s\\ReactOS"), (LPCTSTR)SpecialFolderFSPath(CSIDL_APPDATA,0));
88 _cfg_path.printf(TEXT("%s\\ros-explorer-cfg.xml"), _cfg_dir.c_str());
89
90 if (!_cfg.read_file(_cfg_path)) {
91 if (!_cfg._errors.empty()) {
92 MessageBox(_hwndDesktop,
93 _cfg._errors.str(),
94 TEXT("ROS Explorer - reading user settings"),
95 MB_OK);
96 }
97 _cfg.read_file(TEXT("explorer-cfg-template.xml"));
98 }
99
100 // read bookmarks
101 _favorites_path.printf(TEXT("%s\\ros-explorer-bookmarks.xml"), _cfg_dir.c_str());
102
103 if (!_favorites.read(_favorites_path)) {
104 _favorites.import_IE_favorites(0);
105 _favorites.write(_favorites_path);
106 }
107 }
108
109 void ExplorerGlobals::write_persistent()
110 {
111 // write configuration file
112 RecursiveCreateDirectory(_cfg_dir);
113
114 _cfg.write_file(_cfg_path);
115 _favorites.write(_favorites_path);
116 }
117
118
119 XMLPos ExplorerGlobals::get_cfg()
120 {
121 XMLPos cfg_pos(&_cfg);
122
123 cfg_pos.smart_create("explorer-cfg");
124
125 return cfg_pos;
126 }
127
128 XMLPos ExplorerGlobals::get_cfg(const char* path)
129 {
130 XMLPos cfg_pos(&_cfg);
131
132 cfg_pos.smart_create("explorer-cfg");
133 cfg_pos.create_relative(path);
134
135 return cfg_pos;
136 }
137
138
139 void _log_(LPCTSTR txt)
140 {
141 FmtString msg(TEXT("%s\n"), txt);
142
143 if (g_Globals._log)
144 _fputts(msg, g_Globals._log);
145
146 OutputDebugString(msg);
147 }
148
149
150 bool FileTypeManager::is_exe_file(LPCTSTR ext)
151 {
152 static const LPCTSTR s_executable_extensions[] = {
153 TEXT("COM"),
154 TEXT("EXE"),
155 TEXT("BAT"),
156 TEXT("CMD"),
157 TEXT("CMM"),
158 TEXT("BTM"),
159 TEXT("AWK"),
160 0
161 };
162
163 TCHAR ext_buffer[_MAX_EXT];
164 const LPCTSTR* p;
165 LPCTSTR s;
166 LPTSTR d;
167
168 for(s=ext+1,d=ext_buffer; (*d=toupper(*s)); s++)
169 ++d;
170
171 for(p=s_executable_extensions; *p; p++)
172 if (!lstrcmp(ext_buffer, *p))
173 return true;
174
175 return false;
176 }
177
178
179 const FileTypeInfo& FileTypeManager::operator[](String ext)
180 {
181 ext.toLower();
182
183 iterator found = find(ext);
184 if (found != end())
185 return found->second;
186
187 FileTypeInfo& ftype = super::operator[](ext);
188
189 ftype._neverShowExt = false;
190
191 HKEY hkey;
192 TCHAR value[MAX_PATH], display_name[MAX_PATH];
193 LONG valuelen = sizeof(value);
194
195 if (!RegQueryValue(HKEY_CLASSES_ROOT, ext, value, &valuelen)) {
196 ftype._classname = value;
197
198 valuelen = sizeof(display_name);
199 if (!RegQueryValue(HKEY_CLASSES_ROOT, ftype._classname, display_name, &valuelen))
200 ftype._displayname = display_name;
201
202 if (!RegOpenKey(HKEY_CLASSES_ROOT, ftype._classname, &hkey)) {
203 if (!RegQueryValueEx(hkey, TEXT("NeverShowExt"), 0, NULL, NULL, NULL))
204 ftype._neverShowExt = true;
205
206 RegCloseKey(hkey);
207 }
208 }
209
210 return ftype;
211 }
212
213 LPCTSTR FileTypeManager::set_type(Entry* entry, bool dont_hide_ext)
214 {
215 LPCTSTR ext = _tcsrchr(entry->_data.cFileName, TEXT('.'));
216
217 if (ext) {
218 const FileTypeInfo& type = (*this)[ext];
219
220 if (!type._displayname.empty())
221 entry->_type_name = _tcsdup(type._displayname);
222
223 // hide some file extensions
224 if (type._neverShowExt && !dont_hide_ext) {
225 int len = ext - entry->_data.cFileName;
226
227 if (entry->_display_name != entry->_data.cFileName)
228 free(entry->_display_name);
229
230 entry->_display_name = (LPTSTR) malloc((len+1)*sizeof(TCHAR));
231 lstrcpyn(entry->_display_name, entry->_data.cFileName, len + 1);
232 }
233
234 if (is_exe_file(ext))
235 entry->_data.dwFileAttributes |= ATTRIBUTE_EXECUTABLE;
236 }
237
238 return ext;
239 }
240
241
242 Icon::Icon()
243 : _id(ICID_UNKNOWN),
244 _itype(IT_STATIC),
245 _hicon(0)
246 {
247 }
248
249 Icon::Icon(ICON_ID id, UINT nid) //, int cx, int cy
250 : _id(id),
251 _itype(IT_STATIC),
252 _hicon(ResIcon(nid)) // ResIconEx(nid, cx, cy)
253 {
254 }
255
256 Icon::Icon(ICON_ID id, UINT nid, int icon_size)
257 : _id(id),
258 _itype(IT_STATIC),
259 _hicon(ResIconEx(nid, icon_size, icon_size))
260 {
261 }
262
263 Icon::Icon(ICON_TYPE itype, int id, HICON hIcon)
264 : _id((ICON_ID)id),
265 _itype(itype),
266 _hicon(hIcon)
267 {
268 }
269
270 Icon::Icon(ICON_TYPE itype, int id, int sys_idx)
271 : _id((ICON_ID)id),
272 _itype(itype),
273 _sys_idx(sys_idx)
274 {
275 }
276
277 void Icon::draw(HDC hdc, int x, int y, int cx, int cy, COLORREF bk_color, HBRUSH bk_brush) const
278 {
279 if (_itype == IT_SYSCACHE)
280 ImageList_DrawEx(g_Globals._icon_cache.get_sys_imagelist(), _sys_idx, hdc, x, y, cx, cy, bk_color, CLR_DEFAULT, ILD_NORMAL);
281 else
282 DrawIconEx(hdc, x, y, _hicon, cx, cy, 0, bk_brush, DI_NORMAL);
283 }
284
285 HBITMAP Icon::create_bitmap(COLORREF bk_color, HBRUSH hbrBkgnd, HDC hdc_wnd) const
286 {
287 if (_itype == IT_SYSCACHE) {
288 HIMAGELIST himl = g_Globals._icon_cache.get_sys_imagelist();
289
290 int cx, cy;
291 ImageList_GetIconSize(himl, &cx, &cy);
292
293 HBITMAP hbmp = CreateCompatibleBitmap(hdc_wnd, cx, cy);
294 HDC hdc = CreateCompatibleDC(hdc_wnd);
295 HBITMAP hbmp_old = SelectBitmap(hdc, hbmp);
296 ImageList_DrawEx(himl, _sys_idx, hdc, 0, 0, cx, cy, bk_color, CLR_DEFAULT, ILD_NORMAL);
297 SelectBitmap(hdc, hbmp_old);
298 DeleteDC(hdc);
299
300 return hbmp;
301 } else
302 return create_bitmap_from_icon(_hicon, hbrBkgnd, hdc_wnd);
303 }
304
305
306 int Icon::add_to_imagelist(HIMAGELIST himl, HDC hdc_wnd, COLORREF bk_color, HBRUSH bk_brush) const
307 {
308 int ret;
309
310 if (_itype == IT_SYSCACHE) {
311 HIMAGELIST himl = g_Globals._icon_cache.get_sys_imagelist();
312
313 int cx, cy;
314 ImageList_GetIconSize(himl, &cx, &cy);
315
316 HBITMAP hbmp = CreateCompatibleBitmap(hdc_wnd, cx, cy);
317 HDC hdc = CreateCompatibleDC(hdc_wnd);
318 HBITMAP hbmp_old = SelectBitmap(hdc, hbmp);
319 ImageList_DrawEx(himl, _sys_idx, hdc, 0, 0, cx, cy, bk_color, CLR_DEFAULT, ILD_NORMAL);
320 SelectBitmap(hdc, hbmp_old);
321 DeleteDC(hdc);
322
323 ret = ImageList_Add(himl, hbmp, 0);
324
325 DeleteObject(hbmp);
326 } else
327 ret = ImageList_AddAlphaIcon(himl, _hicon, bk_brush, hdc_wnd);
328
329 return ret;
330 }
331
332 HBITMAP create_bitmap_from_icon(HICON hIcon, HBRUSH hbrush_bkgnd, HDC hdc_wnd/*, int icon_size*/)
333 {
334 int cx = ICON_SIZE_SMALL;
335 int cy = ICON_SIZE_SMALL;
336 HBITMAP hbmp = CreateCompatibleBitmap(hdc_wnd, cx, cy);
337
338 MemCanvas canvas;
339 BitmapSelection sel(canvas, hbmp);
340
341 RECT rect = {0, 0, cx, cy};
342 FillRect(canvas, &rect, hbrush_bkgnd);
343
344 DrawIconEx(canvas, 0, 0, hIcon, cx, cy, 0, hbrush_bkgnd, DI_NORMAL);
345
346 return hbmp;
347 }
348
349 HBITMAP create_small_bitmap_from_icon(HICON hIcon, HBRUSH hbrush_bkgnd, HDC hdc_wnd)
350 {
351 int cx = GetSystemMetrics(SM_CXSMICON);
352 int cy = GetSystemMetrics(SM_CYSMICON);
353 HBITMAP hbmp = CreateCompatibleBitmap(hdc_wnd, cx, cy);
354
355 MemCanvas canvas;
356 BitmapSelection sel(canvas, hbmp);
357
358 RECT rect = {0, 0, cx, cy};
359 FillRect(canvas, &rect, hbrush_bkgnd);
360
361 DrawIconEx(canvas, 0, 0, hIcon, cx, cy, 0, hbrush_bkgnd, DI_NORMAL);
362
363 return hbmp;
364 }
365
366 int ImageList_AddAlphaIcon(HIMAGELIST himl, HICON hIcon, HBRUSH hbrush_bkgnd, HDC hdc_wnd)
367 {
368 HBITMAP hbmp = create_bitmap_from_icon(hIcon, hbrush_bkgnd, hdc_wnd);
369
370 int ret = ImageList_Add(himl, hbmp, 0);
371
372 DeleteObject(hbmp);
373
374 return ret;
375 }
376
377
378 int IconCache::s_next_id = ICID_DYNAMIC;
379
380
381 void IconCache::init()
382 {
383 int icon_size = STARTMENUROOT_ICON_SIZE;
384
385 _icons[ICID_NONE] = Icon(IT_STATIC, ICID_NONE, (HICON)0);
386
387 _icons[ICID_FOLDER] = Icon(ICID_FOLDER, IDI_FOLDER);
388 //_icons[ICID_DOCUMENT] = Icon(ICID_DOCUMENT, IDI_DOCUMENT);
389 _icons[ICID_EXPLORER] = Icon(ICID_EXPLORER, IDI_EXPLORER);
390 //_icons[ICID_APP] = Icon(ICID_APP, IDI_APPICON);
391
392 _icons[ICID_CONFIG] = Icon(ICID_CONFIG, IDI_CONFIG, icon_size);
393 _icons[ICID_DOCUMENTS] = Icon(ICID_DOCUMENTS, IDI_DOCUMENTS, icon_size);
394 _icons[ICID_FAVORITES] = Icon(ICID_FAVORITES, IDI_FAVORITES, icon_size);
395 _icons[ICID_INFO] = Icon(ICID_INFO, IDI_INFO, icon_size);
396 _icons[ICID_APPS] = Icon(ICID_APPS, IDI_APPS, icon_size);
397 _icons[ICID_SEARCH] = Icon(ICID_SEARCH, IDI_SEARCH, icon_size);
398 _icons[ICID_ACTION] = Icon(ICID_ACTION, IDI_ACTION, icon_size);
399 _icons[ICID_SEARCH_DOC] = Icon(ICID_SEARCH_DOC, IDI_SEARCH_DOC, icon_size);
400 _icons[ICID_PRINTER] = Icon(ICID_PRINTER, IDI_PRINTER, icon_size);
401 _icons[ICID_NETWORK] = Icon(ICID_NETWORK, IDI_NETWORK, icon_size);
402 _icons[ICID_COMPUTER] = Icon(ICID_COMPUTER, IDI_COMPUTER, icon_size);
403 _icons[ICID_LOGOFF] = Icon(ICID_LOGOFF, IDI_LOGOFF, icon_size);
404 _icons[ICID_SHUTDOWN] = Icon(ICID_SHUTDOWN, IDI_SHUTDOWN, icon_size);
405 _icons[ICID_RESTART] = Icon(ICID_RESTART, IDI_RESTART, icon_size);
406 _icons[ICID_BOOKMARK] = Icon(ICID_BOOKMARK, IDI_DOT_TRANS, icon_size);
407 _icons[ICID_MINIMIZE] = Icon(ICID_MINIMIZE, IDI_MINIMIZE, icon_size);
408 _icons[ICID_CONTROLPAN] = Icon(ICID_CONTROLPAN, IDI_CONTROLPAN, icon_size);
409 _icons[ICID_DESKSETTING]= Icon(ICID_DESKSETTING,IDI_DESKSETTING,icon_size);
410 _icons[ICID_NETCONNS] = Icon(ICID_NETCONNS, IDI_NETCONNS, icon_size);
411 _icons[ICID_ADMIN] = Icon(ICID_ADMIN, IDI_ADMIN, icon_size);
412 _icons[ICID_RECENT] = Icon(ICID_RECENT, IDI_RECENT, icon_size);
413 }
414
415
416 const Icon& IconCache::extract(LPCTSTR path, ICONCACHE_FLAGS flags)
417 {
418 // search for matching icon with unchanged flags in the cache
419 CacheKey mapkey(path, flags);
420 PathCacheMap::iterator found = _pathCache.find(mapkey);
421
422 if (found != _pathCache.end())
423 return _icons[found->second];
424
425 // search for matching icon with handle
426 CacheKey mapkey_hicon(path, flags|ICF_HICON);
427 if (flags != mapkey_hicon.second) {
428 found = _pathCache.find(mapkey_hicon);
429
430 if (found != _pathCache.end())
431 return _icons[found->second];
432 }
433
434 // search for matching icon in the system image list cache
435 CacheKey mapkey_syscache(path, flags|ICF_SYSCACHE);
436 if (flags != mapkey_syscache.second) {
437 found = _pathCache.find(mapkey_syscache);
438
439 if (found != _pathCache.end())
440 return _icons[found->second];
441 }
442
443 SHFILEINFO sfi;
444
445 int shgfi_flags = 0;
446
447 if (flags & ICF_OPEN)
448 shgfi_flags |= SHGFI_OPENICON;
449
450 if ((flags&(ICF_LARGE|ICF_MIDDLE|ICF_OVERLAYS|ICF_HICON)) && !(flags&ICF_SYSCACHE)) {
451 shgfi_flags |= SHGFI_ICON;
452
453 if (!(flags & (ICF_LARGE|ICF_MIDDLE)))
454 shgfi_flags |= SHGFI_SMALLICON;
455
456 if (flags & ICF_OVERLAYS)
457 shgfi_flags |= SHGFI_ADDOVERLAYS;
458
459 // get small/big icons with/without overlays
460 if (SHGetFileInfo(path, 0, &sfi, sizeof(sfi), shgfi_flags)) {
461 const Icon& icon = add(sfi.hIcon, IT_CACHED);
462
463 ///@todo limit cache size
464 _pathCache[mapkey_hicon] = icon;
465
466 return icon;
467 }
468 } else {
469 assert(!(flags&ICF_OVERLAYS));
470
471 shgfi_flags |= SHGFI_SYSICONINDEX|SHGFI_SMALLICON;
472
473 // use system image list - the "search program dialog" needs it
474 HIMAGELIST himlSys_small = (HIMAGELIST) SHGetFileInfo(path, 0, &sfi, sizeof(sfi), shgfi_flags);
475
476 if (himlSys_small) {
477 _himlSys_small = himlSys_small;
478
479 const Icon& icon = add(sfi.iIcon/*, IT_SYSCACHE*/);
480
481 ///@todo limit cache size
482 _pathCache[mapkey_syscache] = icon;
483
484 return icon;
485 }
486 }
487
488 return _icons[ICID_NONE];
489 }
490
491 const Icon& IconCache::extract(LPCTSTR path, int icon_idx, ICONCACHE_FLAGS flags)
492 {
493 IdxCacheKey key(path, make_pair(icon_idx, (flags|ICF_HICON)&~ICF_SYSCACHE));
494
495 key.first.toLower();
496
497 IdxCacheMap::iterator found = _idxCache.find(key);
498
499 if (found != _idxCache.end())
500 return _icons[found->second];
501
502 HICON hIcon;
503
504 if ((int)ExtractIconEx(path, icon_idx, NULL, &hIcon, 1) > 0) {
505 const Icon& icon = add(hIcon, IT_CACHED);
506
507 _idxCache[key] = icon;
508
509 return icon;
510 } else {
511
512 ///@todo retreive "http://.../favicon.ico" format icons
513
514 return _icons[ICID_NONE];
515 }
516 }
517
518 const Icon& IconCache::extract(IExtractIcon* pExtract, LPCTSTR path, int icon_idx, ICONCACHE_FLAGS flags)
519 {
520 HICON hIconLarge = 0;
521 HICON hIcon;
522
523 int icon_size = ICON_SIZE_FROM_ICF(flags);
524 HRESULT hr = pExtract->Extract(path, icon_idx, &hIconLarge, &hIcon, MAKELONG(GetSystemMetrics(SM_CXICON), icon_size));
525
526 if (hr == NOERROR) { //@@ oder SUCCEEDED(hr) ?
527 if (icon_size > ICON_SIZE_SMALL) { //@@ OK?
528 if (hIcon)
529 DestroyIcon(hIcon);
530
531 hIcon = hIconLarge;
532 } else {
533 if (hIconLarge)
534 DestroyIcon(hIconLarge);
535 }
536
537 if (hIcon)
538 return add(hIcon); //@@ When do we want not to free this icons?
539 }
540
541 return _icons[ICID_NONE];
542 }
543
544 const Icon& IconCache::extract(LPCITEMIDLIST pidl, ICONCACHE_FLAGS flags)
545 {
546 // search for matching icon with unchanged flags in the cache
547 PidlCacheKey mapkey(pidl, flags);
548 PidlCacheMap::iterator found = _pidlcache.find(mapkey);
549
550 if (found != _pidlcache.end())
551 return _icons[found->second];
552
553 // search for matching icon with handle
554 PidlCacheKey mapkey_hicon(pidl, flags|ICF_HICON);
555 if (flags != mapkey_hicon.second) {
556 found = _pidlcache.find(mapkey_hicon);
557
558 if (found != _pidlcache.end())
559 return _icons[found->second];
560 }
561
562 // search for matching icon in the system image list cache
563 PidlCacheKey mapkey_syscache(pidl, flags|ICF_SYSCACHE);
564 if (flags != mapkey_syscache.second) {
565 found = _pidlcache.find(mapkey_syscache);
566
567 if (found != _pidlcache.end())
568 return _icons[found->second];
569 }
570
571 SHFILEINFO sfi;
572
573 int shgfi_flags = SHGFI_PIDL;
574
575 if (!(flags & (ICF_LARGE|ICF_MIDDLE)))
576 shgfi_flags |= SHGFI_SMALLICON;
577
578 if (flags & ICF_OPEN)
579 shgfi_flags |= SHGFI_OPENICON;
580
581 if (flags & ICF_SYSCACHE) {
582 assert(!(flags&ICF_OVERLAYS));
583
584 HIMAGELIST himlSys = (HIMAGELIST) SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX|shgfi_flags);
585 if (himlSys) {
586 const Icon& icon = add(sfi.iIcon/*, IT_SYSCACHE*/);
587
588 ///@todo limit cache size
589 _pidlcache[mapkey_syscache] = icon;
590
591 return icon;
592 }
593 } else {
594 if (flags & ICF_OVERLAYS)
595 shgfi_flags |= SHGFI_ADDOVERLAYS;
596
597 if (SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_ICON|shgfi_flags)) {
598 const Icon& icon = add(sfi.hIcon, IT_CACHED);
599
600 ///@todo limit cache size
601 _pidlcache[mapkey_hicon] = icon;
602
603 return icon;
604 }
605 }
606
607 return _icons[ICID_NONE];
608 }
609
610
611 const Icon& IconCache::add(HICON hIcon, ICON_TYPE type)
612 {
613 int id = ++s_next_id;
614
615 return _icons[id] = Icon(type, id, hIcon);
616 }
617
618 const Icon& IconCache::add(int sys_idx/*, ICON_TYPE type=IT_SYSCACHE*/)
619 {
620 int id = ++s_next_id;
621
622 return _icons[id] = SysCacheIcon(id, sys_idx);
623 }
624
625 const Icon& IconCache::get_icon(int id)
626 {
627 return _icons[id];
628 }
629
630 IconCache::~IconCache()
631 {
632 /* We don't need to free cached resources - they are automatically freed at process termination
633 for (int index = s_next_id; index >= 0; index--) {
634 IconMap::iterator found = _icons.find(index);
635
636 if (found != _icons.end()) {
637 Icon& icon = found->second;
638
639 if ((icon.get_icontype() == IT_DYNAMIC) ||
640 (icon.get_icontype() == IT_CACHED))
641 {
642 DestroyIcon(icon.get_hicon());
643 _icons.erase(found);
644 }
645 }
646 }
647 */
648 }
649
650 void IconCache::free_icon(int icon_id)
651 {
652 IconMap::iterator found = _icons.find(icon_id);
653
654 if (found != _icons.end()) {
655 Icon& icon = found->second;
656
657 if (icon.destroy())
658 _icons.erase(found);
659 }
660 }
661
662
663 ResString::ResString(UINT nid)
664 {
665 TCHAR buffer[BUFFER_LEN];
666
667 int len = LoadString(g_Globals._hInstance, nid, buffer, sizeof(buffer)/sizeof(TCHAR));
668
669 super::assign(buffer, len);
670 }
671
672
673 ResIcon::ResIcon(UINT nid)
674 {
675 _hicon = LoadIcon(g_Globals._hInstance, MAKEINTRESOURCE(nid));
676 }
677
678 SmallIcon::SmallIcon(UINT nid)
679 {
680 _hicon = (HICON)LoadImage(g_Globals._hInstance, MAKEINTRESOURCE(nid), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
681 }
682
683 ResIconEx::ResIconEx(UINT nid, int w, int h)
684 {
685 _hicon = (HICON)LoadImage(g_Globals._hInstance, MAKEINTRESOURCE(nid), IMAGE_ICON, w, h, LR_SHARED);
686 }
687
688
689 void SetWindowIcon(HWND hwnd, UINT nid)
690 {
691 HICON hIcon = ResIcon(nid);
692 (void)Window_SetIcon(hwnd, ICON_BIG, hIcon);
693
694 HICON hIconSmall = SmallIcon(nid);
695 (void)Window_SetIcon(hwnd, ICON_SMALL, hIconSmall);
696 }
697
698
699 ResBitmap::ResBitmap(UINT nid)
700 {
701 _hBmp = LoadBitmap(g_Globals._hInstance, MAKEINTRESOURCE(nid));
702 }
703
704
705 #ifndef ROSSHELL
706
707 void explorer_show_frame(int cmdShow, LPTSTR lpCmdLine)
708 {
709 ExplorerCmd cmd;
710
711 if (g_Globals._hMainWnd) {
712 if (IsIconic(g_Globals._hMainWnd))
713 ShowWindow(g_Globals._hMainWnd, SW_RESTORE);
714 else
715 SetForegroundWindow(g_Globals._hMainWnd);
716
717 return;
718 }
719
720 g_Globals._prescan_nodes = false;
721
722 cmd._mdi = true;
723 cmd._cmdShow = cmdShow;
724
725 // parse command line options, which may overwrite the MDI flag
726 if (lpCmdLine)
727 cmd.ParseCmdLine(lpCmdLine);
728
729 // create main window
730 MainFrameBase::Create(cmd);
731 }
732
733 bool ExplorerCmd::ParseCmdLine(LPCTSTR lpCmdLine)
734 {
735 bool ok = true;
736
737 LPCTSTR b = lpCmdLine;
738 LPCTSTR p = b;
739
740 while(*b) {
741 // remove leading space
742 while(_istspace((unsigned)*b))
743 ++b;
744
745 p = b;
746
747 bool quote = false;
748
749 // options are separated by ','
750 for(; *p; ++p) {
751 if (*p == '"') // Quote characters may appear at any position in the command line.
752 quote = !quote;
753 else if (*p==',' && !quote)
754 break;
755 }
756
757 if (p > b) {
758 int l = p - b;
759
760 // remove trailing space
761 while(l>0 && _istspace((unsigned)b[l-1]))
762 --l;
763
764 if (!EvaluateOption(String(b, l)))
765 ok = false;
766
767 if (*p)
768 ++p;
769
770 b = p;
771 }
772 }
773
774 return ok;
775 }
776
777 bool ExplorerCmd::EvaluateOption(LPCTSTR option)
778 {
779 String opt_str;
780
781 // Remove quote characters, as they are evaluated at this point.
782 for(; *option; ++option)
783 if (*option != '"')
784 opt_str += *option;
785
786 option = opt_str;
787
788 if (option[0] == '/') {
789 ++option;
790
791 // option /e for windows in explorer mode
792 if (!_tcsicmp(option, TEXT("e")))
793 _flags |= OWM_EXPLORE;
794 // option /root for rooted explorer windows
795 else if (!_tcsicmp(option, TEXT("root")))
796 _flags |= OWM_ROOTED;
797 // non-standard options: /mdi, /sdi
798 else if (!_tcsicmp(option, TEXT("mdi")))
799 _mdi = true;
800 else if (!_tcsicmp(option, TEXT("sdi")))
801 _mdi = false;
802 else if (!_tcsicmp(option, TEXT("n")))
803 {
804 // Do nothing
805 }
806 else if (!_tcsicmp(option, TEXT("select")))
807 {
808 SelectOpt = TRUE;
809 }
810 else
811 return false;
812
813 } else {
814 if (!_path.empty())
815 return false;
816
817 if((SelectOpt == TRUE) && (PathFileExists(option)))
818 {
819 WCHAR szDir[MAX_PATH];
820
821 _wsplitpath(option, szPath, szDir, NULL, NULL);
822 wcscat(szPath, szDir);
823 PathRemoveBackslash(szPath);
824 _path = szPath;
825 SelectOpt = FALSE;
826 }
827 else
828 _path = opt_str;
829 }
830
831 return true;
832 }
833
834 bool ExplorerCmd::IsValidPath() const
835 {
836 if (!_path.empty()) {
837 DWORD attribs = GetFileAttributes(_path);
838
839 if (attribs!=INVALID_FILE_ATTRIBUTES && (attribs&FILE_ATTRIBUTE_DIRECTORY))
840 return true; // file system path
841 else if (*_path==':' && _path.at(1)==':')
842 return true; // text encoded IDL
843 }
844
845 return false;
846 }
847
848 #else
849
850 void explorer_show_frame(int cmdShow, LPTSTR lpCmdLine)
851 {
852 if (!lpCmdLine)
853 lpCmdLine = TEXT("explorer.exe");
854
855 launch_file(GetDesktopWindow(), lpCmdLine, cmdShow);
856 }
857
858 #endif
859
860
861 PopupMenu::PopupMenu(UINT nid)
862 {
863 HMENU hMenu = LoadMenu(g_Globals._hInstance, MAKEINTRESOURCE(nid));
864 _hmenu = GetSubMenu(hMenu, 0);
865 RemoveMenu(hMenu, 0, MF_BYPOSITION);
866 DestroyMenu(hMenu);
867 }
868
869
870 /// "About Explorer" Dialog
871 struct ExplorerAboutDlg : public
872 CtlColorParent<
873 OwnerDrawParent<Dialog>
874 >
875 {
876 typedef CtlColorParent<
877 OwnerDrawParent<Dialog>
878 > super;
879
880 ExplorerAboutDlg(HWND hwnd)
881 : super(hwnd)
882 {
883 SetWindowIcon(hwnd, IDI_REACTOS);
884
885 new FlatButton(hwnd, IDOK);
886
887 _hfont = CreateFont(20, 0, 0, 0, FW_BOLD, TRUE, 0, 0, 0, 0, 0, 0, 0, TEXT("Sans Serif"));
888 new ColorStatic(hwnd, IDC_ROS_EXPLORER, RGB(32,32,128), 0, _hfont);
889
890 new HyperlinkCtrl(hwnd, IDC_WWW);
891
892 FmtString ver_txt(ResString(IDS_EXPLORER_VERSION_STR), (LPCTSTR)ResString(IDS_VERSION_STR));
893 SetWindowText(GetDlgItem(hwnd, IDC_VERSION_TXT), ver_txt);
894
895 HWND hwnd_winver = GetDlgItem(hwnd, IDC_WIN_VERSION);
896 SetWindowText(hwnd_winver, get_windows_version_str());
897 SetWindowFont(hwnd_winver, GetStockFont(DEFAULT_GUI_FONT), FALSE);
898
899 CenterWindow(hwnd);
900 }
901
902 ~ExplorerAboutDlg()
903 {
904 DeleteObject(_hfont);
905 }
906
907 LRESULT WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
908 {
909 switch(nmsg) {
910 case WM_PAINT:
911 Paint();
912 break;
913
914 default:
915 return super::WndProc(nmsg, wparam, lparam);
916 }
917
918 return 0;
919 }
920
921 void Paint()
922 {
923 PaintCanvas canvas(_hwnd);
924
925 HICON hicon = (HICON) LoadImage(g_Globals._hInstance, MAKEINTRESOURCE(IDI_REACTOS_BIG), IMAGE_ICON, 0, 0, LR_SHARED);
926
927 DrawIconEx(canvas, 20, 10, hicon, 0, 0, 0, 0, DI_NORMAL);
928 }
929
930 protected:
931 HFONT _hfont;
932 };
933
934 void explorer_about(HWND hwndParent)
935 {
936 Dialog::DoModal(IDD_ABOUT_EXPLORER, WINDOW_CREATOR(ExplorerAboutDlg), hwndParent);
937 }
938
939
940 static void InitInstance(HINSTANCE hInstance)
941 {
942 CONTEXT("InitInstance");
943
944 setlocale(LC_COLLATE, ""); // set collating rules to local settings for compareName
945
946 #ifndef ROSSHELL
947 // register frame window class
948 g_Globals._hframeClass = IconWindowClass(CLASSNAME_FRAME,IDI_EXPLORER);
949
950 // register child window class
951 WindowClass(CLASSNAME_CHILDWND, CS_CLASSDC|CS_DBLCLKS).Register();
952
953 // register tree window class
954 WindowClass(CLASSNAME_WINEFILETREE, CS_CLASSDC|CS_DBLCLKS).Register();
955 #endif
956
957 g_Globals._cfStrFName = RegisterClipboardFormat(CFSTR_FILENAME);
958 }
959
960
961 int explorer_main(HINSTANCE hInstance, LPTSTR lpCmdLine, int cmdShow)
962 {
963 CONTEXT("explorer_main");
964
965 // initialize Common Controls library
966 CommonControlInit usingCmnCtrl;
967
968 try {
969 InitInstance(hInstance);
970 } catch(COMException& e) {
971 HandleException(e, GetDesktopWindow());
972 return -1;
973 }
974
975 #ifndef ROSSHELL
976 if (cmdShow != SW_HIDE) {
977 /* // don't maximize if being called from the ROS desktop
978 if (cmdShow == SW_SHOWNORMAL)
979 ///@todo read window placement from registry
980 cmdShow = SW_MAXIMIZE;
981 */
982
983 explorer_show_frame(cmdShow, lpCmdLine);
984 }
985 #endif
986
987 Window::MessageLoop();
988
989 return 1;
990 }
991
992
993 static bool SetShellReadyEvent(LPCTSTR evtName)
994 {
995 HANDLE hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, evtName);
996 if (!hEvent)
997 return false;
998
999 SetEvent(hEvent);
1000 CloseHandle(hEvent);
1001
1002 return true;
1003 }
1004
1005
1006 int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd)
1007 {
1008 CONTEXT("WinMain()");
1009
1010 BOOL any_desktop_running = IsAnyDesktopRunning();
1011
1012 BOOL startup_desktop;
1013
1014 // strip extended options from the front of the command line
1015 String ext_options;
1016
1017 while(*lpCmdLine == '-') {
1018 while(*lpCmdLine && !_istspace((unsigned)*lpCmdLine))
1019 ext_options += *lpCmdLine++;
1020
1021 while(_istspace((unsigned)*lpCmdLine))
1022 ++lpCmdLine;
1023 }
1024
1025 // command line option "-install" to replace previous shell application with ROS Explorer
1026 if (_tcsstr(ext_options,TEXT("-install"))) {
1027 // install ROS Explorer into the registry
1028 TCHAR path[MAX_PATH];
1029
1030 int l = GetModuleFileName(0, path, COUNTOF(path));
1031 if (l) {
1032 HKEY hkey;
1033
1034 if (!RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"), &hkey)) {
1035
1036 ///@todo save previous shell application in config file
1037
1038 RegSetValueEx(hkey, TEXT("Shell"), 0, REG_SZ, (LPBYTE)path, l*sizeof(TCHAR));
1039 RegCloseKey(hkey);
1040 }
1041
1042 if (!RegOpenKey(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"), &hkey)) {
1043
1044 ///@todo save previous shell application in config file
1045
1046 RegSetValueEx(hkey, TEXT("Shell"), 0, REG_SZ, (LPBYTE)TEXT(""), l*sizeof(TCHAR));
1047 RegCloseKey(hkey);
1048 }
1049 }
1050
1051 HWND shellWindow = GetShellWindow();
1052
1053 if (shellWindow) {
1054 DWORD pid;
1055
1056 // terminate shell process for NT like systems
1057 GetWindowThreadProcessId(shellWindow, &pid);
1058 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
1059
1060 // On Win 9x it's sufficient to destroy the shell window.
1061 DestroyWindow(shellWindow);
1062
1063 if (TerminateProcess(hProcess, 0))
1064 WaitForSingleObject(hProcess, INFINITE);
1065
1066 CloseHandle(hProcess);
1067 }
1068
1069 startup_desktop = TRUE;
1070 } else {
1071 // create desktop window and task bar only, if there is no other shell and we are
1072 // the first explorer instance
1073 // MS Explorer looks additionally into the registry entry HKCU\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\shell,
1074 // to decide wether it is currently configured as shell application.
1075 startup_desktop = !any_desktop_running;
1076 }
1077
1078
1079 bool autostart = !any_desktop_running;
1080
1081 // disable autostart if the SHIFT key is pressed
1082 if (GetAsyncKeyState(VK_SHIFT) < 0)
1083 autostart = false;
1084
1085 #ifdef _DEBUG //MF: disabled for debugging
1086 autostart = false;
1087 #endif
1088
1089 // If there is given the command line option "-desktop", create desktop window anyways
1090 if (_tcsstr(ext_options,TEXT("-desktop")))
1091 startup_desktop = TRUE;
1092 #ifndef ROSSHELL
1093 else if (_tcsstr(ext_options,TEXT("-nodesktop")))
1094 startup_desktop = FALSE;
1095
1096 // Don't display cabinet window in desktop mode
1097 if (startup_desktop && !_tcsstr(ext_options,TEXT("-explorer")))
1098 nShowCmd = SW_HIDE;
1099 #endif
1100
1101 if (_tcsstr(ext_options,TEXT("-noautostart")))
1102 autostart = false;
1103 else if (_tcsstr(ext_options,TEXT("-autostart")))
1104 autostart = true;
1105
1106 #ifndef __WINE__
1107 if (_tcsstr(ext_options,TEXT("-console"))) {
1108 AllocConsole();
1109
1110 _dup2(_open_osfhandle((long)GetStdHandle(STD_INPUT_HANDLE), _O_RDONLY), 0);
1111 _dup2(_open_osfhandle((long)GetStdHandle(STD_OUTPUT_HANDLE), 0), 1);
1112 _dup2(_open_osfhandle((long)GetStdHandle(STD_ERROR_HANDLE), 0), 2);
1113
1114 g_Globals._log = _fdopen(1, "w");
1115 setvbuf(g_Globals._log, 0, _IONBF, 0);
1116
1117 LOG(TEXT("starting explorer debug log\n"));
1118 }
1119 #endif
1120
1121
1122 if (startup_desktop) {
1123 // hide the XP login screen (Credit to Nicolas Escuder)
1124 // another undocumented event: "Global\\msgina: ReturnToWelcome"
1125 if (!SetShellReadyEvent(TEXT("msgina: ShellReadyEvent")))
1126 SetShellReadyEvent(TEXT("Global\\msgina: ShellReadyEvent"));
1127 }
1128 #ifdef ROSSHELL
1129 else
1130 return 0; // no shell to launch, so exit immediatelly
1131 #endif
1132
1133
1134 if (!any_desktop_running) {
1135 // launch the shell DDE server
1136 if (g_SHDOCVW_ShellDDEInit)
1137 (*g_SHDOCVW_ShellDDEInit)(TRUE);
1138 }
1139
1140
1141 bool use_gdb_stub = false; // !IsDebuggerPresent();
1142
1143 if (_tcsstr(ext_options,TEXT("-debug")))
1144 use_gdb_stub = true;
1145
1146 if (_tcsstr(ext_options,TEXT("-break"))) {
1147 LOG(TEXT("debugger breakpoint"));
1148 __debugbreak();
1149 }
1150
1151 #ifdef _M_IX86
1152 // activate GDB remote debugging stub if no other debugger is running
1153 if (use_gdb_stub) {
1154 LOG(TEXT("waiting for debugger connection...\n"));
1155
1156 initialize_gdb_stub();
1157 }
1158 #endif
1159
1160 g_Globals.init(hInstance);
1161
1162 // initialize COM and OLE before creating the desktop window
1163 OleInit usingCOM;
1164
1165 // init common controls library
1166 CommonControlInit usingCmnCtrl;
1167
1168 g_Globals.read_persistent();
1169
1170 if (startup_desktop) {
1171 WaitCursor wait;
1172
1173 g_Globals._desktops.init();
1174
1175 g_Globals._hwndDesktop = DesktopWindow::Create();
1176 #ifdef _USE_HDESK
1177 g_Globals._desktops.get_current_Desktop()->_hwndDesktop = g_Globals._hwndDesktop;
1178 #endif
1179 }
1180
1181 if (_tcsstr(ext_options,TEXT("-?"))) {
1182 MessageBoxA(g_Globals._hwndDesktop,
1183 "/e open cabinet window in explorer mode\r\n"
1184 "/root open cabinet window in rooted mode\r\n"
1185 "/mdi open cabinet window in MDI mode\r\n"
1186 "/sdi open cabinet window in SDI mode\r\n"
1187 "\r\n"
1188 "-? display command line options\r\n"
1189 "\r\n"
1190 "-desktop start in desktop mode regardless of an already running shell\r\n"
1191 "-nodesktop disable desktop mode\r\n"
1192 "-explorer display cabinet window regardless of enabled desktop mode\r\n"
1193 "\r\n"
1194 "-install replace previous shell application with ROS Explorer\r\n"
1195 "\r\n"
1196 "-noautostart disable autostarts\r\n"
1197 "-autostart enable autostarts regardless of debug build\r\n"
1198 "\r\n"
1199 "-console open debug console\r\n"
1200 "\r\n"
1201 "-debug activate GDB remote debugging stub\r\n"
1202 "-break activate debugger breakpoint\r\n",
1203 "ROS Explorer - command line options", MB_OK);
1204 }
1205
1206 /*
1207 * Set our shutdown parameters: we want to shutdown the very last,
1208 * but before any TaskMgr instance (which has a shutdown level of 1).
1209 */
1210 SetProcessShutdownParameters(2, 0);
1211
1212 Thread* pSSOThread = NULL;
1213
1214 if (startup_desktop) {
1215 // launch SSO thread to allow message processing independent from the explorer main thread
1216 pSSOThread = new SSOThread;
1217 pSSOThread->Start();
1218 }
1219
1220 /**TODO launching autostart programs can be moved into a background thread. */
1221 if (autostart) {
1222 const char* argv[] = {"", "s"}; // call startup routine in SESSION_START mode
1223 startup(2, argv);
1224 }
1225
1226 #ifndef ROSSHELL
1227 if (g_Globals._hwndDesktop)
1228 g_Globals._desktop_mode = true;
1229 #endif
1230
1231
1232 int ret = explorer_main(hInstance, lpCmdLine, nShowCmd);
1233
1234
1235 // write configuration file
1236 g_Globals.write_persistent();
1237
1238 if (pSSOThread) {
1239 pSSOThread->Stop();
1240 delete pSSOThread;
1241 }
1242
1243 if (!any_desktop_running) {
1244 // shutdown the shell DDE server
1245 if (g_SHDOCVW_ShellDDEInit)
1246 (*g_SHDOCVW_ShellDDEInit)(FALSE);
1247 }
1248
1249 return ret;
1250 }