Release 1.5.29.
[wine] / programs / explorer / explorer.c
1 /*
2  * explorer.exe
3  *
4  * Copyright 2004 CodeWeavers, Mike Hearn
5  * Copyright 2005,2006 CodeWeavers, Aric Stewart
6  * Copyright 2011 Jay Yang
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #define COBJMACROS
24 #define NONAMELESSUNION
25
26 #include "wine/unicode.h"
27 #include "wine/debug.h"
28 #include "explorer_private.h"
29 #include "resource.h"
30
31 #include <initguid.h>
32 #include <windows.h>
33 #include <shellapi.h>
34 #include <shobjidl.h>
35 #include <shlobj.h>
36 #include <shlwapi.h>
37 #include <commoncontrols.h>
38 #include <commctrl.h>
39
40 WINE_DEFAULT_DEBUG_CHANNEL(explorer);
41
42 #define EXPLORER_INFO_INDEX 0
43
44 #define NAV_TOOLBAR_HEIGHT 30
45 #define PATHBOX_HEIGHT 24
46
47 #define DEFAULT_WIDTH 640
48 #define DEFAULT_HEIGHT 480
49
50
51 static const WCHAR EXPLORER_CLASS[] = {'W','I','N','E','_','E','X','P','L','O','R','E','R','\0'};
52 static const WCHAR PATH_BOX_NAME[] = {'\0'};
53
54 HINSTANCE explorer_hInstance;
55
56 typedef struct parametersTAG {
57     BOOL    explorer_mode;
58     WCHAR   root[MAX_PATH];
59     WCHAR   selection[MAX_PATH];
60 } parameters_struct;
61
62 typedef struct
63 {
64     IExplorerBrowser *browser;
65     HWND main_window,path_box;
66     INT rebar_height;
67     LPITEMIDLIST pidl;
68     IImageList *icon_list;
69     DWORD advise_cookie;
70 } explorer_info;
71
72 enum
73 {
74     BACK_BUTTON,FORWARD_BUTTON,UP_BUTTON
75 };
76
77 typedef struct
78 {
79     IExplorerBrowserEvents IExplorerBrowserEvents_iface;
80     explorer_info* info;
81     LONG ref;
82 } IExplorerBrowserEventsImpl;
83
84 static IExplorerBrowserEventsImpl *impl_from_IExplorerBrowserEvents(IExplorerBrowserEvents *iface)
85 {
86     return CONTAINING_RECORD(iface, IExplorerBrowserEventsImpl, IExplorerBrowserEvents_iface);
87 }
88
89 static HRESULT WINAPI IExplorerBrowserEventsImpl_fnQueryInterface(IExplorerBrowserEvents *iface, REFIID riid, void **ppvObject)
90 {
91     return E_NOINTERFACE;
92 }
93
94 static ULONG WINAPI IExplorerBrowserEventsImpl_fnAddRef(IExplorerBrowserEvents *iface)
95 {
96     IExplorerBrowserEventsImpl *This = impl_from_IExplorerBrowserEvents(iface);
97     return InterlockedIncrement(&This->ref);
98 }
99
100 static ULONG WINAPI IExplorerBrowserEventsImpl_fnRelease(IExplorerBrowserEvents *iface)
101 {
102     IExplorerBrowserEventsImpl *This = impl_from_IExplorerBrowserEvents(iface);
103     ULONG ref = InterlockedDecrement(&This->ref);
104     if(!ref)
105         HeapFree(GetProcessHeap(),0,This);
106     return ref;
107 }
108
109 static BOOL create_combobox_item(IShellFolder *folder, LPCITEMIDLIST pidl, IImageList *icon_list, COMBOBOXEXITEMW *item)
110 {
111     STRRET strret;
112     HRESULT hres;
113     IExtractIconW *extract_icon;
114     UINT reserved;
115     WCHAR icon_file[MAX_PATH];
116     INT icon_index;
117     UINT icon_flags;
118     HICON icon;
119     strret.uType=STRRET_WSTR;
120     hres = IShellFolder_GetDisplayNameOf(folder,pidl,SHGDN_FORADDRESSBAR,&strret);
121     if(FAILED(hres))
122     {
123         WINE_WARN("Could not get name for pidl\n");
124         return FALSE;
125     }
126     switch(strret.uType)
127     {
128     case STRRET_WSTR:
129         item->pszText = strret.u.pOleStr;
130         break;
131     default:
132         WINE_FIXME("Unimplemented STRRET type:%u\n",strret.uType);
133         break;
134     }
135     hres = IShellFolder_GetUIObjectOf(folder,NULL,1,&pidl,&IID_IExtractIconW,
136                                       &reserved,(void**)&extract_icon);
137     if(SUCCEEDED(hres))
138     {
139         item->mask |= CBEIF_IMAGE;
140         IExtractIconW_GetIconLocation(extract_icon,GIL_FORSHELL,icon_file,
141                                       sizeof(icon_file)/sizeof(WCHAR),
142                                       &icon_index,&icon_flags);
143         IExtractIconW_Extract(extract_icon,icon_file,icon_index,NULL,&icon,20);
144         item->iImage = ImageList_AddIcon((HIMAGELIST)icon_list,icon);
145         IExtractIconW_Release(extract_icon);
146     }
147     else
148     {
149         item->mask &= ~CBEIF_IMAGE;
150         WINE_WARN("Could not get an icon for %s\n",wine_dbgstr_w(item->pszText));
151     }
152     return TRUE;
153 }
154
155 static void update_path_box(explorer_info *info)
156 {
157     COMBOBOXEXITEMW item;
158     COMBOBOXEXITEMW main_item;
159     IShellFolder *desktop;
160     IPersistFolder2 *persist;
161     LPITEMIDLIST desktop_pidl;
162     IEnumIDList *ids;
163
164     ImageList_Remove((HIMAGELIST)info->icon_list,-1);
165     SendMessageW(info->path_box,CB_RESETCONTENT,0,0);
166     SHGetDesktopFolder(&desktop);
167     IShellFolder_QueryInterface(desktop,&IID_IPersistFolder2,(void**)&persist);
168     IPersistFolder2_GetCurFolder(persist,&desktop_pidl);
169     IPersistFolder2_Release(persist);
170     persist = NULL;
171     /*Add Desktop*/
172     item.iItem = -1;
173     item.mask = CBEIF_TEXT | CBEIF_INDENT | CBEIF_LPARAM;
174     item.iIndent = 0;
175     create_combobox_item(desktop,desktop_pidl,info->icon_list,&item);
176     item.lParam = (LPARAM)desktop_pidl;
177     SendMessageW(info->path_box,CBEM_INSERTITEMW,0,(LPARAM)&item);
178     if(ILIsEqual(info->pidl,desktop_pidl))
179         main_item = item;
180     else
181         CoTaskMemFree(item.pszText);
182     /*Add all direct subfolders of Desktop*/
183     if(SUCCEEDED(IShellFolder_EnumObjects(desktop,NULL,SHCONTF_FOLDERS,&ids))
184        && ids!=NULL)
185     {
186         LPITEMIDLIST curr_pidl=NULL;
187         HRESULT hres;
188
189         item.iIndent = 1;
190         while(1)
191         {
192             ILFree(curr_pidl);
193             curr_pidl=NULL;
194             hres = IEnumIDList_Next(ids,1,&curr_pidl,NULL);
195             if(FAILED(hres) || hres == S_FALSE)
196                 break;
197             if(!create_combobox_item(desktop,curr_pidl,info->icon_list,&item))
198                 WINE_WARN("Could not create a combobox item\n");
199             else
200             {
201                 LPITEMIDLIST full_pidl = ILCombine(desktop_pidl,curr_pidl);
202                 item.lParam = (LPARAM)full_pidl;
203                 SendMessageW(info->path_box,CBEM_INSERTITEMW,0,(LPARAM)&item);
204                 if(ILIsEqual(full_pidl,info->pidl))
205                     main_item = item;
206                 else if(ILIsParent(full_pidl,info->pidl,FALSE))
207                 {
208                     /*add all parents of the pidl passed in*/
209                     LPITEMIDLIST next_pidl = ILFindChild(full_pidl,info->pidl);
210                     IShellFolder *curr_folder = NULL, *temp;
211                     hres = IShellFolder_BindToObject(desktop,curr_pidl,NULL,
212                                                      &IID_IShellFolder,
213                                                      (void**)&curr_folder);
214                     if(FAILED(hres))
215                         WINE_WARN("Could not get an IShellFolder\n");
216                     while(!ILIsEmpty(next_pidl))
217                     {
218                         LPITEMIDLIST first = ILCloneFirst(next_pidl);
219                         CoTaskMemFree(item.pszText);
220                         if(!create_combobox_item(curr_folder,first,
221                                                  info->icon_list,&item))
222                         {
223                             WINE_WARN("Could not create a combobox item\n");
224                             break;
225                         }
226                         ++item.iIndent;
227                         full_pidl = ILCombine(full_pidl,first);
228                         item.lParam = (LPARAM)full_pidl;
229                         SendMessageW(info->path_box,CBEM_INSERTITEMW,0,(LPARAM)&item);
230                         temp=NULL;
231                         hres = IShellFolder_BindToObject(curr_folder,first,NULL,
232                                                          &IID_IShellFolder,
233                                                          (void**)&temp);
234                         if(FAILED(hres))
235                         {
236                             WINE_WARN("Could not get an IShellFolder\n");
237                             break;
238                         }
239                         IShellFolder_Release(curr_folder);
240                         curr_folder = temp;
241
242                         ILFree(first);
243                         next_pidl = ILGetNext(next_pidl);
244                     }
245                     memcpy(&main_item,&item,sizeof(item));
246                     if(curr_folder)
247                         IShellFolder_Release(curr_folder);
248                     item.iIndent = 1;
249                 }
250                 else
251                     CoTaskMemFree(item.pszText);
252             }
253         }
254         ILFree(curr_pidl);
255         IEnumIDList_Release(ids);
256     }
257     else
258         WINE_WARN("Could not enumerate the desktop\n");
259     SendMessageW(info->path_box,CBEM_SETITEMW,0,(LPARAM)&main_item);
260     CoTaskMemFree(main_item.pszText);
261 }
262
263 static HRESULT WINAPI IExplorerBrowserEventsImpl_fnOnNavigationComplete(IExplorerBrowserEvents *iface, PCIDLIST_ABSOLUTE pidl)
264 {
265     IExplorerBrowserEventsImpl *This = impl_from_IExplorerBrowserEvents(iface);
266     ILFree(This->info->pidl);
267     This->info->pidl = ILClone(pidl);
268     update_path_box(This->info);
269     return S_OK;
270 }
271
272 static HRESULT WINAPI IExplorerBrowserEventsImpl_fnOnNavigationFailed(IExplorerBrowserEvents *iface, PCIDLIST_ABSOLUTE pidl)
273 {
274     return S_OK;
275 }
276
277 static HRESULT WINAPI IExplorerBrowserEventsImpl_fnOnNavigationPending(IExplorerBrowserEvents *iface, PCIDLIST_ABSOLUTE pidl)
278 {
279     return S_OK;
280 }
281
282 static HRESULT WINAPI IExplorerBrowserEventsImpl_fnOnViewCreated(IExplorerBrowserEvents *iface, IShellView *psv)
283 {
284     return S_OK;
285 }
286
287 static IExplorerBrowserEventsVtbl vt_IExplorerBrowserEvents =
288 {
289     IExplorerBrowserEventsImpl_fnQueryInterface,
290     IExplorerBrowserEventsImpl_fnAddRef,
291     IExplorerBrowserEventsImpl_fnRelease,
292     IExplorerBrowserEventsImpl_fnOnNavigationPending,
293     IExplorerBrowserEventsImpl_fnOnViewCreated,
294     IExplorerBrowserEventsImpl_fnOnNavigationComplete,
295     IExplorerBrowserEventsImpl_fnOnNavigationFailed
296 };
297
298 static IExplorerBrowserEvents *make_explorer_events(explorer_info *info)
299 {
300     IExplorerBrowserEventsImpl *ret
301         = HeapAlloc(GetProcessHeap(), 0, sizeof(IExplorerBrowserEventsImpl));
302     ret->IExplorerBrowserEvents_iface.lpVtbl = &vt_IExplorerBrowserEvents;
303     ret->info = info;
304     ret->ref = 1;
305     SHGetImageList(SHIL_SMALL,&IID_IImageList,(void**)&(ret->info->icon_list));
306     SendMessageW(info->path_box,CBEM_SETIMAGELIST,0,(LPARAM)ret->info->icon_list);
307     return &ret->IExplorerBrowserEvents_iface;
308 }
309
310 static void make_explorer_window(IShellFolder* startFolder)
311 {
312     RECT explorerRect;
313     HWND rebar,nav_toolbar;
314     FOLDERSETTINGS fs;
315     IExplorerBrowserEvents *events;
316     explorer_info *info;
317     HRESULT hres;
318     WCHAR explorer_title[100];
319     WCHAR pathbox_label[50];
320     TBADDBITMAP bitmap_info;
321     TBBUTTON nav_buttons[3];
322     int hist_offset,view_offset;
323     REBARBANDINFOW band_info;
324     memset(nav_buttons,0,sizeof(nav_buttons));
325     LoadStringW(explorer_hInstance,IDS_EXPLORER_TITLE,explorer_title,
326                 sizeof(explorer_title)/sizeof(WCHAR));
327     LoadStringW(explorer_hInstance,IDS_PATHBOX_LABEL,pathbox_label,
328                 sizeof(pathbox_label)/sizeof(WCHAR));
329     info = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(explorer_info));
330     if(!info)
331     {
332         WINE_ERR("Could not allocate a explorer_info struct\n");
333         return;
334     }
335     hres = CoCreateInstance(&CLSID_ExplorerBrowser,NULL,CLSCTX_INPROC_SERVER,
336                             &IID_IExplorerBrowser,(LPVOID*)&info->browser);
337     if(FAILED(hres))
338     {
339         WINE_ERR("Could not obtain an instance of IExplorerBrowser\n");
340         HeapFree(GetProcessHeap(),0,info);
341         return;
342     }
343     info->rebar_height=0;
344     info->main_window
345         = CreateWindowW(EXPLORER_CLASS,explorer_title,WS_OVERLAPPEDWINDOW,
346                         CW_USEDEFAULT,CW_USEDEFAULT,DEFAULT_WIDTH,
347                         DEFAULT_HEIGHT,NULL,NULL,explorer_hInstance,NULL);
348
349     fs.ViewMode = FVM_DETAILS;
350     fs.fFlags = FWF_AUTOARRANGE;
351     explorerRect.left = 0;
352     explorerRect.top = 0;
353     explorerRect.right = DEFAULT_WIDTH;
354     explorerRect.bottom = DEFAULT_HEIGHT;
355
356     IExplorerBrowser_Initialize(info->browser,info->main_window,&explorerRect,&fs);
357     IExplorerBrowser_SetOptions(info->browser,EBO_SHOWFRAMES);
358     SetWindowLongPtrW(info->main_window,EXPLORER_INFO_INDEX,(LONG_PTR)info);
359
360     /*setup navbar*/
361     rebar = CreateWindowExW(WS_EX_TOOLWINDOW,REBARCLASSNAMEW,NULL,
362                             WS_CHILD|WS_VISIBLE|RBS_VARHEIGHT|CCS_TOP|CCS_NODIVIDER,
363                             0,0,0,0,info->main_window,NULL,explorer_hInstance,NULL);
364     nav_toolbar
365         = CreateWindowExW(TBSTYLE_EX_MIXEDBUTTONS,TOOLBARCLASSNAMEW,NULL,
366                           WS_CHILD|WS_VISIBLE|TBSTYLE_FLAT,0,0,0,0,rebar,NULL,
367                           explorer_hInstance,NULL);
368
369     bitmap_info.hInst = HINST_COMMCTRL;
370     bitmap_info.nID = IDB_HIST_LARGE_COLOR;
371     hist_offset= SendMessageW(nav_toolbar,TB_ADDBITMAP,0,(LPARAM)&bitmap_info);
372     bitmap_info.nID = IDB_VIEW_LARGE_COLOR;
373     view_offset= SendMessageW(nav_toolbar,TB_ADDBITMAP,0,(LPARAM)&bitmap_info);
374
375     nav_buttons[0].iBitmap=hist_offset+HIST_BACK;
376     nav_buttons[0].idCommand=BACK_BUTTON;
377     nav_buttons[0].fsState=TBSTATE_ENABLED;
378     nav_buttons[0].fsStyle=BTNS_BUTTON|BTNS_AUTOSIZE;
379     nav_buttons[1].iBitmap=hist_offset+HIST_FORWARD;
380     nav_buttons[1].idCommand=FORWARD_BUTTON;
381     nav_buttons[1].fsState=TBSTATE_ENABLED;
382     nav_buttons[1].fsStyle=BTNS_BUTTON|BTNS_AUTOSIZE;
383     nav_buttons[2].iBitmap=view_offset+VIEW_PARENTFOLDER;
384     nav_buttons[2].idCommand=UP_BUTTON;
385     nav_buttons[2].fsState=TBSTATE_ENABLED;
386     nav_buttons[2].fsStyle=BTNS_BUTTON|BTNS_AUTOSIZE;
387     SendMessageW(nav_toolbar,TB_BUTTONSTRUCTSIZE,sizeof(TBBUTTON),0);
388     SendMessageW(nav_toolbar,TB_ADDBUTTONSW,sizeof(nav_buttons)/sizeof(TBBUTTON),(LPARAM)nav_buttons);
389
390     band_info.cbSize = sizeof(band_info);
391     band_info.fMask = RBBIM_STYLE|RBBIM_CHILD|RBBIM_CHILDSIZE|RBBIM_SIZE;
392     band_info.hwndChild = nav_toolbar;
393     band_info.fStyle=RBBS_GRIPPERALWAYS|RBBS_CHILDEDGE;
394     band_info.cyChild=NAV_TOOLBAR_HEIGHT;
395     band_info.cx=0;
396     band_info.cyMinChild=NAV_TOOLBAR_HEIGHT;
397     band_info.cxMinChild=0;
398     SendMessageW(rebar,RB_INSERTBANDW,-1,(LPARAM)&band_info);
399     info->path_box = CreateWindowW(WC_COMBOBOXEXW,PATH_BOX_NAME,
400                                    WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
401                                    0,0,DEFAULT_WIDTH,PATHBOX_HEIGHT,rebar,NULL,
402                                    explorer_hInstance,NULL);
403     band_info.cyChild=PATHBOX_HEIGHT;
404     band_info.cx=0;
405     band_info.cyMinChild=PATHBOX_HEIGHT;
406     band_info.cxMinChild=0;
407     band_info.fMask|=RBBIM_TEXT;
408     band_info.lpText=pathbox_label;
409     band_info.fStyle|=RBBS_BREAK;
410     band_info.hwndChild=info->path_box;
411     SendMessageW(rebar,RB_INSERTBANDW,-1,(LPARAM)&band_info);
412     events = make_explorer_events(info);
413     IExplorerBrowser_Advise(info->browser,events,&info->advise_cookie);
414     IExplorerBrowser_BrowseToObject(info->browser,(IUnknown*)startFolder,
415                                     SBSP_ABSOLUTE);
416     ShowWindow(info->main_window,SW_SHOWDEFAULT);
417     UpdateWindow(info->main_window);
418     IExplorerBrowserEvents_Release(events);
419 }
420
421 static void update_window_size(explorer_info *info, int height, int width)
422 {
423     RECT new_rect;
424     new_rect.left = 0;
425     new_rect.top = info->rebar_height;
426     new_rect.right = width;
427     new_rect.bottom = height;
428     IExplorerBrowser_SetRect(info->browser,NULL,new_rect);
429 }
430
431 static void do_exit(int code)
432 {
433     OleUninitialize();
434     ExitProcess(code);
435 }
436
437 static LRESULT explorer_on_end_edit(explorer_info *info,NMCBEENDEDITW *edit_info)
438 {
439     LPITEMIDLIST pidl = NULL;
440
441     WINE_TRACE("iWhy=%x\n",edit_info->iWhy);
442     switch(edit_info->iWhy)
443     {
444     case CBENF_DROPDOWN:
445         if(edit_info->iNewSelection!=CB_ERR)
446             pidl = (LPITEMIDLIST)SendMessageW(edit_info->hdr.hwndFrom,
447                                               CB_GETITEMDATA,
448                                               edit_info->iNewSelection,0);
449         break;
450     case CBENF_RETURN:
451         {
452             WCHAR path[MAX_PATH];
453             HWND edit_ctrl = (HWND)SendMessageW(edit_info->hdr.hwndFrom,
454                                                 CBEM_GETEDITCONTROL,0,0);
455             *((WORD*)path)=MAX_PATH;
456             SendMessageW(edit_ctrl,EM_GETLINE,0,(LPARAM)path);
457             pidl = ILCreateFromPathW(path);
458             break;
459         }
460     case CBENF_ESCAPE:
461         /*make sure the that the path box resets*/
462         update_path_box(info);
463         return 0;
464     default:
465         return 0;
466     }
467     if(pidl)
468         IExplorerBrowser_BrowseToIDList(info->browser,pidl,SBSP_ABSOLUTE);
469     if(edit_info->iWhy==CBENF_RETURN)
470         ILFree(pidl);
471     return 0;
472 }
473
474 static LRESULT update_rebar_size(explorer_info* info,NMRBAUTOSIZE *size_info)
475 {
476     RECT new_rect;
477     RECT window_rect;
478     info->rebar_height = size_info->rcTarget.bottom-size_info->rcTarget.top;
479     GetWindowRect(info->main_window,&window_rect);
480     new_rect.left = 0;
481     new_rect.top = info->rebar_height;
482     new_rect.right = window_rect.right-window_rect.left;
483     new_rect.bottom = window_rect.bottom-window_rect.top;
484     IExplorerBrowser_SetRect(info->browser,NULL,new_rect);
485     return 0;
486 }
487
488 static LRESULT explorer_on_notify(explorer_info* info,NMHDR* notification)
489 {
490     WINE_TRACE("code=%i\n",notification->code);
491     switch(notification->code)
492     {
493     case CBEN_BEGINEDIT:
494         {
495             WCHAR path[MAX_PATH];
496             HWND edit_ctrl = (HWND)SendMessageW(notification->hwndFrom,
497                                                 CBEM_GETEDITCONTROL,0,0);
498             SHGetPathFromIDListW(info->pidl,path);
499             SetWindowTextW(edit_ctrl,path);
500             break;
501         }
502     case CBEN_ENDEDITA:
503         {
504             NMCBEENDEDITA *edit_info_a = (NMCBEENDEDITA*)notification;
505             NMCBEENDEDITW edit_info_w;
506             edit_info_w.hdr = edit_info_a->hdr;
507             edit_info_w.fChanged = edit_info_a->fChanged;
508             edit_info_w.iNewSelection = edit_info_a->iNewSelection;
509             MultiByteToWideChar(CP_ACP,0,edit_info_a->szText,-1,
510                                 edit_info_w.szText,CBEMAXSTRLEN);
511             edit_info_w.iWhy = edit_info_a->iWhy;
512             return explorer_on_end_edit(info,&edit_info_w);
513         }
514     case CBEN_ENDEDITW:
515         return explorer_on_end_edit(info,(NMCBEENDEDITW*)notification);
516     case CBEN_DELETEITEM:
517         {
518             NMCOMBOBOXEXW *entry = (NMCOMBOBOXEXW*)notification;
519             if(entry->ceItem.lParam)
520                 ILFree((LPITEMIDLIST)entry->ceItem.lParam);
521             break;
522         }
523     case RBN_AUTOSIZE:
524         return update_rebar_size(info,(NMRBAUTOSIZE*)notification);
525     default:
526         break;
527     }
528     return 0;
529 }
530
531 static LRESULT CALLBACK explorer_wnd_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
532 {
533     explorer_info *info
534         = (explorer_info*)GetWindowLongPtrW(hwnd,EXPLORER_INFO_INDEX);
535     IExplorerBrowser *browser = NULL;
536
537     WINE_TRACE("(hwnd=%p,uMsg=%u,wParam=%lx,lParam=%lx)\n",hwnd,uMsg,wParam,lParam);
538     if(info)
539         browser = info->browser;
540     switch(uMsg)
541     {
542     case WM_DESTROY:
543         IExplorerBrowser_Unadvise(browser,info->advise_cookie);
544         IExplorerBrowser_Destroy(browser);
545         IExplorerBrowser_Release(browser);
546         ILFree(info->pidl);
547         IImageList_Release(info->icon_list);
548         HeapFree(GetProcessHeap(),0,info);
549         SetWindowLongPtrW(hwnd,EXPLORER_INFO_INDEX,0);
550         PostQuitMessage(0);
551         break;
552     case WM_QUIT:
553         do_exit(wParam);
554     case WM_NOTIFY:
555         return explorer_on_notify(info,(NMHDR*)lParam);
556     case WM_COMMAND:
557         if(HIWORD(wParam)==BN_CLICKED)
558         {
559             switch(LOWORD(wParam))
560             {
561             case BACK_BUTTON:
562                 IExplorerBrowser_BrowseToObject(browser,NULL,SBSP_NAVIGATEBACK);
563                 break;
564             case FORWARD_BUTTON:
565                 IExplorerBrowser_BrowseToObject(browser,NULL,SBSP_NAVIGATEFORWARD);
566                 break;
567             case UP_BUTTON:
568                 IExplorerBrowser_BrowseToObject(browser,NULL,SBSP_PARENT);
569                 break;
570             }
571         }
572         break;
573     case WM_SIZE:
574         update_window_size(info,HIWORD(lParam),LOWORD(lParam));
575         break;
576     default:
577         return DefWindowProcW(hwnd,uMsg,wParam,lParam);
578     }
579     return 0;
580 }
581
582 static void register_explorer_window_class(void)
583 {
584     WNDCLASSEXW window_class;
585     window_class.cbSize = sizeof(WNDCLASSEXW);
586     window_class.style = 0;
587     window_class.cbClsExtra = 0;
588     window_class.cbWndExtra = sizeof(LONG_PTR);
589     window_class.lpfnWndProc = explorer_wnd_proc;
590     window_class.hInstance = explorer_hInstance;
591     window_class.hIcon = NULL;
592     window_class.hCursor = NULL;
593     window_class.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
594     window_class.lpszMenuName = NULL;
595     window_class.lpszClassName = EXPLORER_CLASS;
596     window_class.hIconSm = NULL;
597     RegisterClassExW(&window_class);
598 }
599
600 static IShellFolder* get_starting_shell_folder(parameters_struct* params)
601 {
602     IShellFolder* desktop,*folder;
603     LPITEMIDLIST root_pidl;
604     HRESULT hres;
605
606     SHGetDesktopFolder(&desktop);
607     if (!params->root[0])
608     {
609         return desktop;
610     }
611     hres = IShellFolder_ParseDisplayName(desktop,NULL,NULL,
612                                          params->root,NULL,
613                                          &root_pidl,NULL);
614
615     if(FAILED(hres))
616     {
617         return desktop;
618     }
619     hres = IShellFolder_BindToObject(desktop,root_pidl,NULL,
620                                      &IID_IShellFolder,
621                                      (void**)&folder);
622     if(FAILED(hres))
623     {
624         return desktop;
625     }
626     IShellFolder_Release(desktop);
627     return folder;
628 }
629
630 static int copy_path_string(LPWSTR target, LPWSTR source)
631 {
632     INT i = 0;
633
634     while (isspaceW(*source)) source++;
635
636     if (*source == '\"')
637     {
638         source ++;
639         while (*source != '\"') target[i++] = *source++;
640         target[i] = 0;
641         source ++;
642         i+=2;
643     }
644     else
645     {
646         while (*source && !isspaceW(*source)) target[i++] = *source++;
647         target[i] = 0;
648     }
649     return i;
650 }
651
652
653 static void copy_path_root(LPWSTR root, LPWSTR path)
654 {
655     LPWSTR p,p2;
656     INT i = 0;
657
658     p = path;
659     while (*p!=0)
660         p++;
661
662     while (*p!='\\' && p > path)
663         p--;
664
665     if (p == path)
666         return;
667
668     p2 = path;
669     while (p2 != p)
670     {
671         root[i] = *p2;
672         i++;
673         p2++;
674     }
675     root[i] = 0;
676 }
677
678 /*
679  * Command Line parameters are:
680  * [/n]  Opens in single-paned view for each selected items. This is default
681  * [/e,] Uses Windows Explorer View
682  * [/root,object] Specifies the root level of the view
683  * [/select,object] parent folder is opened and specified object is selected
684  */
685 static void parse_command_line(LPWSTR commandline,parameters_struct *parameters)
686 {
687     static const WCHAR arg_n[] = {'/','n'};
688     static const WCHAR arg_e[] = {'/','e',','};
689     static const WCHAR arg_root[] = {'/','r','o','o','t',','};
690     static const WCHAR arg_select[] = {'/','s','e','l','e','c','t',','};
691     static const WCHAR arg_desktop[] = {'/','d','e','s','k','t','o','p'};
692
693     LPWSTR p, p2;
694
695     p2 = commandline;
696     p = strchrW(commandline,'/');
697     while(p)
698     {
699         if (strncmpW(p, arg_n, sizeof(arg_n)/sizeof(WCHAR))==0)
700         {
701             parameters->explorer_mode = FALSE;
702             p += sizeof(arg_n)/sizeof(WCHAR);
703         }
704         else if (strncmpW(p, arg_e, sizeof(arg_e)/sizeof(WCHAR))==0)
705         {
706             parameters->explorer_mode = TRUE;
707             p += sizeof(arg_e)/sizeof(WCHAR);
708         }
709         else if (strncmpW(p, arg_root, sizeof(arg_root)/sizeof(WCHAR))==0)
710         {
711             p += sizeof(arg_root)/sizeof(WCHAR);
712             p+=copy_path_string(parameters->root,p);
713         }
714         else if (strncmpW(p, arg_select, sizeof(arg_select)/sizeof(WCHAR))==0)
715         {
716             p += sizeof(arg_select)/sizeof(WCHAR);
717             p+=copy_path_string(parameters->selection,p);
718             if (!parameters->root[0])
719                 copy_path_root(parameters->root,
720                                parameters->selection);
721         }
722         else if (strncmpW(p, arg_desktop, sizeof(arg_desktop)/sizeof(WCHAR))==0)
723         {
724             p += sizeof(arg_desktop)/sizeof(WCHAR);
725             manage_desktop( p );  /* the rest of the command line is handled by desktop mode */
726         }
727         else p++;
728
729         p2 = p;
730         p = strchrW(p,'/');
731     }
732     if (p2 && *p2)
733     {
734         /* left over command line is generally the path to be opened */
735         copy_path_string(parameters->root,p2);
736     }
737 }
738
739 int WINAPI wWinMain(HINSTANCE hinstance,
740                     HINSTANCE previnstance,
741                     LPWSTR cmdline,
742                     int cmdshow)
743 {
744
745     parameters_struct   parameters;
746     HRESULT hres;
747     MSG msg;
748     IShellFolder *folder;
749     INITCOMMONCONTROLSEX init_info;
750
751     memset(&parameters,0,sizeof(parameters));
752     explorer_hInstance = hinstance;
753     parse_command_line(cmdline,&parameters);
754     hres = OleInitialize(NULL);
755     if(FAILED(hres))
756     {
757         WINE_ERR("Could not initialize COM\n");
758         ExitProcess(EXIT_FAILURE);
759     }
760     if(parameters.root[0] && !PathIsDirectoryW(parameters.root))
761         if(ShellExecuteW(NULL,NULL,parameters.root,NULL,NULL,SW_SHOWDEFAULT) > (HINSTANCE)32)
762             ExitProcess(EXIT_SUCCESS);
763     init_info.dwSize = sizeof(INITCOMMONCONTROLSEX);
764     init_info.dwICC = ICC_USEREX_CLASSES | ICC_BAR_CLASSES | ICC_COOL_CLASSES;
765     if(!InitCommonControlsEx(&init_info))
766     {
767         WINE_ERR("Could not initialize Comctl\n");
768         ExitProcess(EXIT_FAILURE);
769     }
770     register_explorer_window_class();
771     folder = get_starting_shell_folder(&parameters);
772     make_explorer_window(folder);
773     IShellFolder_Release(folder);
774     while(GetMessageW( &msg, NULL, 0, 0 ) != 0)
775     {
776             TranslateMessage(&msg);
777             DispatchMessageW(&msg);
778     }
779     return 0;
780 }