[shell32.dll]
[reactos.git] / dll / win32 / shell32 / brsfolder.cpp
1 /*
2 * Copyright 1999 Juergen Schmied
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 St, Fifth Floor, Boston, MA 02110-1301, USA
17 *
18 * FIXME:
19 * - many memory leaks
20 * - many flags unimplemented
21 * - implement new dialog style "make new folder" button
22 * - implement editbox
23 * - implement new dialog style resizing
24 */
25
26 #include <precomp.h>
27
28
29 WINE_DEFAULT_DEBUG_CHANNEL(shell);
30
31 typedef struct tagbrowse_info
32 {
33 HWND hWnd;
34 HWND hwndTreeView;
35 LPBROWSEINFOW lpBrowseInfo;
36 LPITEMIDLIST pidlRet;
37 } browse_info;
38
39 typedef struct tagTV_ITEMDATA
40 {
41 LPSHELLFOLDER lpsfParent; /* IShellFolder of the parent */
42 LPITEMIDLIST lpi; /* PIDL relative to parent */
43 LPITEMIDLIST lpifq; /* Fully qualified PIDL */
44 IEnumIDList* pEnumIL; /* Children iterator */
45 } TV_ITEMDATA, *LPTV_ITEMDATA;
46
47 #define SUPPORTEDFLAGS (BIF_STATUSTEXT | \
48 BIF_BROWSEFORCOMPUTER | \
49 BIF_RETURNFSANCESTORS | \
50 BIF_RETURNONLYFSDIRS | \
51 BIF_NONEWFOLDERBUTTON | \
52 BIF_NEWDIALOGSTYLE | \
53 BIF_BROWSEINCLUDEFILES)
54
55 static void FillTreeView(browse_info*, LPSHELLFOLDER,
56 LPITEMIDLIST, HTREEITEM, IEnumIDList*);
57 static HTREEITEM InsertTreeViewItem( browse_info*, IShellFolder *,
58 LPCITEMIDLIST, LPCITEMIDLIST, IEnumIDList*, HTREEITEM);
59
60 static const WCHAR szBrowseFolderInfo[] = {
61 '_','_','W','I','N','E','_',
62 'B','R','S','F','O','L','D','E','R','D','L','G','_',
63 'I','N','F','O',0
64 };
65
66 static DWORD __inline BrowseFlagsToSHCONTF(UINT ulFlags)
67 {
68 return SHCONTF_FOLDERS | (ulFlags & BIF_BROWSEINCLUDEFILES ? SHCONTF_NONFOLDERS : 0);
69 }
70
71 static void browsefolder_callback( LPBROWSEINFOW lpBrowseInfo, HWND hWnd,
72 UINT msg, LPARAM param )
73 {
74 if (!lpBrowseInfo->lpfn)
75 return;
76 lpBrowseInfo->lpfn( hWnd, msg, param, lpBrowseInfo->lParam );
77 }
78
79 /******************************************************************************
80 * InitializeTreeView [Internal]
81 *
82 * Called from WM_INITDIALOG handler.
83 *
84 * PARAMS
85 * hwndParent [I] The BrowseForFolder dialog
86 * root [I] ITEMIDLIST of the root shell folder
87 */
88 static void InitializeTreeView( browse_info *info )
89 {
90 LPITEMIDLIST pidlParent, pidlChild;
91 HIMAGELIST hImageList;
92 HRESULT hr;
93 IShellFolder *lpsfParent, *lpsfRoot;
94 IEnumIDList * pEnumChildren = NULL;
95 HTREEITEM item;
96 DWORD flags;
97 LPCITEMIDLIST root = info->lpBrowseInfo->pidlRoot;
98
99 TRACE("%p\n", info );
100
101 Shell_GetImageLists(NULL, &hImageList);
102
103 if (hImageList)
104 SendMessageW( info->hwndTreeView, TVM_SETIMAGELIST, 0, (LPARAM)hImageList );
105
106 /* We want to call InsertTreeViewItem down the code, in order to insert
107 * the root item of the treeview. Due to InsertTreeViewItem's signature,
108 * we need the following to do this:
109 *
110 * + An ITEMIDLIST corresponding to _the parent_ of root.
111 * + An ITEMIDLIST, which is a relative path from root's parent to root
112 * (containing a single SHITEMID).
113 * + An IShellFolder interface pointer of root's parent folder.
114 *
115 * If root is 'Desktop', then root's parent is also 'Desktop'.
116 */
117
118 pidlParent = ILClone(root);
119 ILRemoveLastID(pidlParent);
120 pidlChild = ILClone(ILFindLastID(root));
121
122 if (_ILIsDesktop(pidlParent)) {
123 hr = SHGetDesktopFolder(&lpsfParent);
124 } else {
125 IShellFolder *lpsfDesktop;
126 hr = SHGetDesktopFolder(&lpsfDesktop);
127 if (!SUCCEEDED(hr)) {
128 WARN("SHGetDesktopFolder failed! hr = %08x\n", hr);
129 return;
130 }
131 hr = lpsfDesktop->BindToObject(pidlParent, 0, IID_IShellFolder, (LPVOID *)&lpsfParent);
132 lpsfDesktop->Release();
133 }
134
135 if (!SUCCEEDED(hr)) {
136 WARN("Could not bind to parent shell folder! hr = %08x\n", hr);
137 return;
138 }
139
140 if (pidlChild && pidlChild->mkid.cb) {
141 hr = lpsfParent->BindToObject(pidlChild, 0, IID_IShellFolder, (LPVOID *)&lpsfRoot);
142 } else {
143 lpsfRoot = lpsfParent;
144 hr = lpsfParent->AddRef();
145 }
146
147 if (!SUCCEEDED(hr)) {
148 WARN("Could not bind to root shell folder! hr = %08x\n", hr);
149 lpsfParent->Release();
150 return;
151 }
152
153 flags = BrowseFlagsToSHCONTF( info->lpBrowseInfo->ulFlags );
154 hr = lpsfRoot->EnumObjects(info->hWnd, flags, &pEnumChildren );
155 if (!SUCCEEDED(hr)) {
156 WARN("Could not get child iterator! hr = %08x\n", hr);
157 lpsfParent->Release();
158 lpsfRoot->Release();
159 return;
160 }
161
162 SendMessageW( info->hwndTreeView, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT );
163 item = InsertTreeViewItem( info, lpsfParent, pidlChild,
164 pidlParent, pEnumChildren, TVI_ROOT );
165 SendMessageW( info->hwndTreeView, TVM_EXPAND, TVE_EXPAND, (LPARAM)item );
166
167 lpsfRoot->Release();
168 lpsfParent->Release();
169 }
170
171 static int GetIcon(LPCITEMIDLIST lpi, UINT uFlags)
172 {
173 SHFILEINFOW sfi;
174 SHGetFileInfoW((LPCWSTR)lpi, 0 ,&sfi, sizeof(SHFILEINFOW), uFlags);
175 return sfi.iIcon;
176 }
177
178 static void GetNormalAndSelectedIcons(LPITEMIDLIST lpifq, LPTVITEMW lpTV_ITEM)
179 {
180 LPITEMIDLIST pidlDesktop = NULL;
181 DWORD flags;
182
183 TRACE("%p %p\n",lpifq, lpTV_ITEM);
184
185 if (!lpifq)
186 {
187 pidlDesktop = _ILCreateDesktop();
188 lpifq = pidlDesktop;
189 }
190
191 flags = SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON;
192 lpTV_ITEM->iImage = GetIcon( lpifq, flags );
193
194 flags = SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_OPENICON;
195 lpTV_ITEM->iSelectedImage = GetIcon( lpifq, flags );
196
197 if (pidlDesktop)
198 ILFree( pidlDesktop );
199 }
200
201 /******************************************************************************
202 * GetName [Internal]
203 *
204 * Query a shell folder for the display name of one of it's children
205 *
206 * PARAMS
207 * lpsf [I] IShellFolder interface of the folder to be queried.
208 * lpi [I] ITEMIDLIST of the child, relative to parent
209 * dwFlags [I] as in IShellFolder::GetDisplayNameOf
210 * lpFriendlyName [O] The desired display name in unicode
211 *
212 * RETURNS
213 * Success: TRUE
214 * Failure: FALSE
215 */
216 static BOOL GetName(LPSHELLFOLDER lpsf, LPCITEMIDLIST lpi, DWORD dwFlags, LPWSTR lpFriendlyName)
217 {
218 BOOL bSuccess=TRUE;
219 STRRET str;
220
221 TRACE("%p %p %x %p\n", lpsf, lpi, dwFlags, lpFriendlyName);
222 if (SUCCEEDED(lpsf->GetDisplayNameOf(lpi, dwFlags, &str)))
223 bSuccess = StrRetToStrNW(lpFriendlyName, MAX_PATH, &str, lpi);
224 else
225 bSuccess = FALSE;
226
227 TRACE("-- %s\n", debugstr_w(lpFriendlyName));
228 return bSuccess;
229 }
230
231 /******************************************************************************
232 * InsertTreeViewItem [Internal]
233 *
234 * PARAMS
235 * info [I] data for the dialog
236 * lpsf [I] IShellFolder interface of the item's parent shell folder
237 * pidl [I] ITEMIDLIST of the child to insert, relative to parent
238 * pidlParent [I] ITEMIDLIST of the parent shell folder
239 * pEnumIL [I] Iterator for the children of the item to be inserted
240 * hParent [I] The treeview-item that represents the parent shell folder
241 *
242 * RETURNS
243 * Success: Handle to the created and inserted treeview-item
244 * Failure: NULL
245 */
246 static HTREEITEM InsertTreeViewItem( browse_info *info, IShellFolder * lpsf,
247 LPCITEMIDLIST pidl, LPCITEMIDLIST pidlParent, IEnumIDList* pEnumIL,
248 HTREEITEM hParent)
249 {
250 TVITEMW tvi;
251 TVINSERTSTRUCTW tvins;
252 WCHAR szBuff[MAX_PATH];
253 LPTV_ITEMDATA lptvid=0;
254
255 tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
256
257 tvi.cChildren= pEnumIL ? 1 : 0;
258 tvi.mask |= TVIF_CHILDREN;
259
260 lptvid = (TV_ITEMDATA *)SHAlloc( sizeof(TV_ITEMDATA) );
261 if (!lptvid)
262 return NULL;
263
264 if (!GetName(lpsf, pidl, SHGDN_NORMAL, szBuff))
265 return NULL;
266
267 tvi.pszText = szBuff;
268 tvi.cchTextMax = MAX_PATH;
269 tvi.lParam = (LPARAM)lptvid;
270
271 lpsf->AddRef();
272 lptvid->lpsfParent = lpsf;
273 lptvid->lpi = ILClone(pidl);
274 lptvid->lpifq = pidlParent ? ILCombine(pidlParent, pidl) : ILClone(pidl);
275 lptvid->pEnumIL = pEnumIL;
276 GetNormalAndSelectedIcons(lptvid->lpifq, &tvi);
277
278 tvins.item = tvi;
279 tvins.hInsertAfter = NULL;
280 tvins.hParent = hParent;
281
282 return (HTREEITEM)SendMessageW(info->hwndTreeView, TVM_INSERTITEM, 0, (LPARAM)&tvins );
283 }
284
285 /******************************************************************************
286 * FillTreeView [Internal]
287 *
288 * For each child (given by lpe) of the parent shell folder, which is given by
289 * lpsf and whose PIDL is pidl, insert a treeview-item right under hParent
290 *
291 * PARAMS
292 * info [I] data for the dialog
293 * lpsf [I] IShellFolder interface of the parent shell folder
294 * pidl [I] ITEMIDLIST of the parent shell folder
295 * hParent [I] The treeview item that represents the parent shell folder
296 * lpe [I] An iterator for the children of the parent shell folder
297 */
298 static void FillTreeView( browse_info *info, IShellFolder * lpsf,
299 LPITEMIDLIST pidl, HTREEITEM hParent, IEnumIDList* lpe)
300 {
301 HTREEITEM hPrev = 0;
302 LPITEMIDLIST pidlTemp = 0;
303 ULONG ulFetched;
304 HRESULT hr;
305 HWND hwnd = GetParent( info->hwndTreeView );
306
307 TRACE("%p %p %p %p\n",lpsf, pidl, hParent, lpe);
308
309 /* No IEnumIDList -> No children */
310 if (!lpe) return;
311
312 SetCapture( hwnd );
313 SetCursor( LoadCursorA( 0, (LPSTR)IDC_WAIT ) );
314
315 while (NOERROR == lpe->Next(1,&pidlTemp,&ulFetched))
316 {
317 ULONG ulAttrs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER;
318 IEnumIDList* pEnumIL = NULL;
319 IShellFolder* pSFChild = NULL;
320 lpsf->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlTemp, &ulAttrs);
321 if (ulAttrs & SFGAO_FOLDER)
322 {
323 hr = lpsf->BindToObject(pidlTemp, NULL, IID_IShellFolder, (LPVOID *)&pSFChild);
324 if (SUCCEEDED(hr))
325 {
326 DWORD flags = BrowseFlagsToSHCONTF(info->lpBrowseInfo->ulFlags);
327 hr = pSFChild->EnumObjects(hwnd, flags, &pEnumIL);
328 if (hr == S_OK)
329 {
330 if ((pEnumIL->Skip(1) != S_OK) ||
331 FAILED(pEnumIL->Reset()))
332 {
333 pEnumIL->Release();
334 pEnumIL = NULL;
335 }
336 }
337 pSFChild->Release();
338 }
339 }
340
341 if (!(hPrev = InsertTreeViewItem(info, lpsf, pidlTemp, pidl, pEnumIL, hParent)))
342 goto done;
343 SHFree(pidlTemp); /* Finally, free the pidl that the shell gave us... */
344 pidlTemp=NULL;
345 }
346
347 done:
348 ReleaseCapture();
349 SetCursor(LoadCursorW(0, (LPWSTR)IDC_ARROW));
350 SHFree(pidlTemp);
351 }
352
353 static BOOL __inline PIDLIsType(LPCITEMIDLIST pidl, PIDLTYPE type)
354 {
355 LPPIDLDATA data = _ILGetDataPointer(pidl);
356 if (!data)
357 return FALSE;
358 return (data->type == type);
359 }
360
361 static void BrsFolder_CheckValidSelection( browse_info *info, LPTV_ITEMDATA lptvid )
362 {
363 LPBROWSEINFOW lpBrowseInfo = info->lpBrowseInfo;
364 LPCITEMIDLIST pidl = lptvid->lpi;
365 BOOL bEnabled = TRUE;
366 DWORD dwAttributes;
367 HRESULT r;
368
369 if ((lpBrowseInfo->ulFlags & BIF_BROWSEFORCOMPUTER) &&
370 !PIDLIsType(pidl, PT_COMP))
371 bEnabled = FALSE;
372 if (lpBrowseInfo->ulFlags & BIF_RETURNFSANCESTORS)
373 {
374 dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
375 r = lptvid->lpsfParent->GetAttributesOf(1,
376 (LPCITEMIDLIST*)&lptvid->lpi, &dwAttributes);
377 if (FAILED(r) || !(dwAttributes & (SFGAO_FILESYSANCESTOR|SFGAO_FILESYSTEM)))
378 bEnabled = FALSE;
379 }
380 if (lpBrowseInfo->ulFlags & BIF_RETURNONLYFSDIRS)
381 {
382 dwAttributes = SFGAO_FOLDER | SFGAO_FILESYSTEM;
383 r = lptvid->lpsfParent->GetAttributesOf(1,
384 (LPCITEMIDLIST*)&lptvid->lpi, &dwAttributes);
385 if (FAILED(r) ||
386 ((dwAttributes & (SFGAO_FOLDER|SFGAO_FILESYSTEM)) != (SFGAO_FOLDER|SFGAO_FILESYSTEM)))
387 {
388 bEnabled = FALSE;
389 }
390 }
391 SendMessageW(info->hWnd, BFFM_ENABLEOK, 0, (LPARAM)bEnabled);
392 }
393
394 static LRESULT BrsFolder_Treeview_Delete( browse_info *info, NMTREEVIEWW *pnmtv )
395 {
396 LPTV_ITEMDATA lptvid = (LPTV_ITEMDATA)pnmtv->itemOld.lParam;
397
398 TRACE("TVN_DELETEITEMA/W %p\n", lptvid);
399
400 lptvid->lpsfParent->Release();
401 if (lptvid->pEnumIL)
402 lptvid->pEnumIL->Release();
403 SHFree(lptvid->lpi);
404 SHFree(lptvid->lpifq);
405 SHFree(lptvid);
406 return 0;
407 }
408
409 static LRESULT BrsFolder_Treeview_Expand( browse_info *info, NMTREEVIEWW *pnmtv )
410 {
411 IShellFolder *lpsf2 = NULL;
412 LPTV_ITEMDATA lptvid = (LPTV_ITEMDATA) pnmtv->itemNew.lParam;
413 HRESULT r;
414
415 TRACE("TVN_ITEMEXPANDINGA/W\n");
416
417 if ((pnmtv->itemNew.state & TVIS_EXPANDEDONCE))
418 return 0;
419
420 if (lptvid->lpi && lptvid->lpi->mkid.cb) {
421 r = lptvid->lpsfParent->BindToObject(lptvid->lpi, 0,
422 IID_IShellFolder, (LPVOID *)&lpsf2 );
423 } else {
424 lpsf2 = lptvid->lpsfParent;
425 r = lpsf2->AddRef();
426 }
427
428 if (SUCCEEDED(r))
429 FillTreeView( info, lpsf2, lptvid->lpifq, pnmtv->itemNew.hItem, lptvid->pEnumIL);
430
431 /* My Computer is already sorted and trying to do a simple text
432 * sort will only mess things up */
433 if (!_ILIsMyComputer(lptvid->lpi))
434 SendMessageW( info->hwndTreeView, TVM_SORTCHILDREN,
435 FALSE, (LPARAM)pnmtv->itemNew.hItem );
436
437 return 0;
438 }
439
440 static HRESULT BrsFolder_Treeview_Changed( browse_info *info, NMTREEVIEWW *pnmtv )
441 {
442 LPTV_ITEMDATA lptvid = (LPTV_ITEMDATA) pnmtv->itemNew.lParam;
443
444 lptvid = (LPTV_ITEMDATA) pnmtv->itemNew.lParam;
445 info->pidlRet = lptvid->lpifq;
446 browsefolder_callback( info->lpBrowseInfo, info->hWnd, BFFM_SELCHANGED,
447 (LPARAM)info->pidlRet );
448 BrsFolder_CheckValidSelection( info, lptvid );
449 return 0;
450 }
451
452 static LRESULT BrsFolder_OnNotify( browse_info *info, UINT CtlID, LPNMHDR lpnmh )
453 {
454 NMTREEVIEWW *pnmtv = (NMTREEVIEWW *)lpnmh;
455
456 TRACE("%p %x %p msg=%x\n", info, CtlID, lpnmh, pnmtv->hdr.code);
457
458 if (pnmtv->hdr.idFrom != IDD_TREEVIEW)
459 return 0;
460
461 switch (pnmtv->hdr.code)
462 {
463 case TVN_DELETEITEMA:
464 case TVN_DELETEITEMW:
465 return BrsFolder_Treeview_Delete( info, pnmtv );
466
467 case TVN_ITEMEXPANDINGA:
468 case TVN_ITEMEXPANDINGW:
469 return BrsFolder_Treeview_Expand( info, pnmtv );
470
471 case TVN_SELCHANGEDA:
472 case TVN_SELCHANGEDW:
473 return BrsFolder_Treeview_Changed( info, pnmtv );
474
475 default:
476 WARN("unhandled (%d)\n", pnmtv->hdr.code);
477 break;
478 }
479
480 return 0;
481 }
482
483
484 static BOOL BrsFolder_OnCreate( HWND hWnd, browse_info *info )
485 {
486 LPBROWSEINFOW lpBrowseInfo = info->lpBrowseInfo;
487
488 info->hWnd = hWnd;
489 SetPropW( hWnd, szBrowseFolderInfo, info );
490
491 if (lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE)
492 FIXME("flags BIF_NEWDIALOGSTYLE partially implemented\n");
493 if (lpBrowseInfo->ulFlags & ~SUPPORTEDFLAGS)
494 FIXME("flags %x not implemented\n", lpBrowseInfo->ulFlags & ~SUPPORTEDFLAGS);
495
496 if (lpBrowseInfo->lpszTitle)
497 SetWindowTextW( GetDlgItem(hWnd, IDD_TITLE), lpBrowseInfo->lpszTitle );
498 else
499 ShowWindow( GetDlgItem(hWnd, IDD_TITLE), SW_HIDE );
500
501 if (!(lpBrowseInfo->ulFlags & BIF_STATUSTEXT)
502 || (lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE))
503 ShowWindow( GetDlgItem(hWnd, IDD_STATUS), SW_HIDE );
504
505 /* Hide "Make New Folder" Button? */
506 if ((lpBrowseInfo->ulFlags & BIF_NONEWFOLDERBUTTON)
507 || !(lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE))
508 ShowWindow( GetDlgItem(hWnd, IDD_MAKENEWFOLDER), SW_HIDE );
509
510 /* Hide the editbox? */
511 if (!(lpBrowseInfo->ulFlags & BIF_EDITBOX))
512 {
513 ShowWindow( GetDlgItem(hWnd, IDD_FOLDER), SW_HIDE );
514 ShowWindow( GetDlgItem(hWnd, IDD_FOLDERTEXT), SW_HIDE );
515 }
516
517 info->hwndTreeView = GetDlgItem( hWnd, IDD_TREEVIEW );
518 if (info->hwndTreeView)
519 {
520 InitializeTreeView( info );
521
522 /* Resize the treeview if there's not editbox */
523 if ((lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE)
524 && !(lpBrowseInfo->ulFlags & BIF_EDITBOX))
525 {
526 RECT rc;
527 GetClientRect(info->hwndTreeView, &rc);
528 SetWindowPos(info->hwndTreeView, HWND_TOP, 0, 0,
529 rc.right, rc.bottom + 40, SWP_NOMOVE);
530 }
531 }
532 else
533 ERR("treeview control missing!\n");
534
535 browsefolder_callback( info->lpBrowseInfo, hWnd, BFFM_INITIALIZED, 0 );
536
537 return TRUE;
538 }
539
540 static BOOL BrsFolder_OnCommand( browse_info *info, UINT id )
541 {
542 LPBROWSEINFOW lpBrowseInfo = info->lpBrowseInfo;
543
544 switch (id)
545 {
546 case IDOK:
547 /* The original pidl is owned by the treeview and will be free'd. */
548 info->pidlRet = ILClone(info->pidlRet);
549 if (info->pidlRet == NULL) /* A null pidl would mean a cancel */
550 info->pidlRet = _ILCreateDesktop();
551 pdump( info->pidlRet );
552 if (lpBrowseInfo->pszDisplayName)
553 SHGetPathFromIDListW( info->pidlRet, lpBrowseInfo->pszDisplayName );
554 EndDialog( info->hWnd, 1 );
555 return TRUE;
556
557 case IDCANCEL:
558 EndDialog( info->hWnd, 0 );
559 return TRUE;
560
561 case IDD_MAKENEWFOLDER:
562 FIXME("make new folder not implemented\n");
563 return TRUE;
564 }
565 return FALSE;
566 }
567
568 static BOOL BrsFolder_OnSetExpanded(browse_info *info, LPVOID selection,
569 BOOL is_str, HTREEITEM *pItem)
570 {
571 LPITEMIDLIST pidlSelection = (LPITEMIDLIST)selection;
572 LPCITEMIDLIST pidlCurrent, pidlRoot;
573 TVITEMEXW item;
574 BOOL bResult = FALSE;
575
576 /* If 'selection' is a string, convert to a Shell ID List. */
577 if (is_str) {
578 IShellFolder *psfDesktop;
579 HRESULT hr;
580
581 hr = SHGetDesktopFolder(&psfDesktop);
582 if (FAILED(hr))
583 goto done;
584
585 hr = psfDesktop->ParseDisplayName(NULL, NULL,
586 (LPOLESTR)selection, NULL, &pidlSelection, NULL);
587 psfDesktop->Release();
588 if (FAILED(hr))
589 goto done;
590 }
591
592 /* Move pidlCurrent behind the SHITEMIDs in pidlSelection, which are the root of
593 * the sub-tree currently displayed. */
594 pidlRoot = info->lpBrowseInfo->pidlRoot;
595 pidlCurrent = pidlSelection;
596 while (!_ILIsEmpty(pidlRoot) && _ILIsEqualSimple(pidlRoot, pidlCurrent)) {
597 pidlRoot = ILGetNext(pidlRoot);
598 pidlCurrent = ILGetNext(pidlCurrent);
599 }
600
601 /* The given ID List is not part of the SHBrowseForFolder's current sub-tree. */
602 if (!_ILIsEmpty(pidlRoot))
603 goto done;
604
605 /* Initialize item to point to the first child of the root folder. */
606 memset(&item, 0, sizeof(item));
607 item.mask = TVIF_PARAM;
608 item.hItem = TreeView_GetRoot(info->hwndTreeView);
609 if (item.hItem)
610 item.hItem = TreeView_GetChild(info->hwndTreeView, item.hItem);
611
612 /* Walk the tree along the nodes corresponding to the remaining ITEMIDLIST */
613 while (item.hItem && !_ILIsEmpty(pidlCurrent)) {
614 LPTV_ITEMDATA pItemData;
615
616 SendMessageW(info->hwndTreeView, TVM_GETITEMW, 0, (LPARAM)&item);
617 pItemData = (LPTV_ITEMDATA)item.lParam;
618
619 if (_ILIsEqualSimple(pItemData->lpi, pidlCurrent)) {
620 pidlCurrent = ILGetNext(pidlCurrent);
621 if (!_ILIsEmpty(pidlCurrent)) {
622 /* Only expand current node and move on to it's first child,
623 * if we didn't already reach the last SHITEMID */
624 SendMessageW(info->hwndTreeView, TVM_EXPAND, TVE_EXPAND, (LPARAM)item.hItem);
625 item.hItem = TreeView_GetChild(info->hwndTreeView, item.hItem);
626 }
627 } else {
628 item.hItem = TreeView_GetNextSibling(info->hwndTreeView, item.hItem);
629 }
630 }
631
632 if (_ILIsEmpty(pidlCurrent) && item.hItem)
633 bResult = TRUE;
634
635 done:
636 if (pidlSelection && pidlSelection != (LPITEMIDLIST)selection)
637 ILFree(pidlSelection);
638
639 if (pItem)
640 *pItem = item.hItem;
641
642 return bResult;
643 }
644
645 static BOOL BrsFolder_OnSetSelectionW(browse_info *info, LPVOID selection, BOOL is_str) {
646 HTREEITEM hItem;
647 BOOL bResult;
648
649 bResult = BrsFolder_OnSetExpanded(info, selection, is_str, &hItem);
650 if (bResult)
651 SendMessageW(info->hwndTreeView, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hItem );
652 return bResult;
653 }
654
655 static BOOL BrsFolder_OnSetSelectionA(browse_info *info, LPVOID selection, BOOL is_str) {
656 LPWSTR selectionW = NULL;
657 BOOL result = FALSE;
658 int length;
659
660 if (!is_str)
661 return BrsFolder_OnSetSelectionW(info, selection, is_str);
662
663 if ((length = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)selection, -1, NULL, 0)) &&
664 (selectionW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, length * sizeof(WCHAR))) &&
665 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)selection, -1, selectionW, length))
666 {
667 result = BrsFolder_OnSetSelectionW(info, selectionW, is_str);
668 }
669
670 HeapFree(GetProcessHeap(), 0, selectionW);
671 return result;
672 }
673
674 /*************************************************************************
675 * BrsFolderDlgProc32 (not an exported API function)
676 */
677 static INT_PTR CALLBACK BrsFolderDlgProc( HWND hWnd, UINT msg, WPARAM wParam,
678 LPARAM lParam )
679 {
680 browse_info *info;
681
682 TRACE("hwnd=%p msg=%04x 0x%08lx 0x%08lx\n", hWnd, msg, wParam, lParam );
683
684 if (msg == WM_INITDIALOG)
685 return BrsFolder_OnCreate( hWnd, (browse_info*) lParam );
686
687 info = (browse_info*) GetPropW( hWnd, szBrowseFolderInfo );
688
689 switch (msg)
690 {
691 case WM_NOTIFY:
692 return BrsFolder_OnNotify( info, (UINT)wParam, (LPNMHDR)lParam);
693
694 case WM_COMMAND:
695 return BrsFolder_OnCommand( info, wParam );
696
697 case BFFM_SETSTATUSTEXTA:
698 TRACE("Set status %s\n", debugstr_a((LPSTR)lParam));
699 SetWindowTextA(GetDlgItem(hWnd, IDD_STATUS), (LPSTR)lParam);
700 break;
701
702 case BFFM_SETSTATUSTEXTW:
703 TRACE("Set status %s\n", debugstr_w((LPWSTR)lParam));
704 SetWindowTextW(GetDlgItem(hWnd, IDD_STATUS), (LPWSTR)lParam);
705 break;
706
707 case BFFM_ENABLEOK:
708 TRACE("Enable %ld\n", lParam);
709 EnableWindow(GetDlgItem(hWnd, 1), (lParam)?TRUE:FALSE);
710 break;
711
712 case BFFM_SETOKTEXT: /* unicode only */
713 TRACE("Set OK text %s\n", debugstr_w((LPWSTR)wParam));
714 SetWindowTextW(GetDlgItem(hWnd, 1), (LPWSTR)wParam);
715 break;
716
717 case BFFM_SETSELECTIONA:
718 return BrsFolder_OnSetSelectionA(info, (LPVOID)lParam, (BOOL)wParam);
719
720 case BFFM_SETSELECTIONW:
721 return BrsFolder_OnSetSelectionW(info, (LPVOID)lParam, (BOOL)wParam);
722
723 case BFFM_SETEXPANDED: /* unicode only */
724 return BrsFolder_OnSetExpanded(info, (LPVOID)lParam, (BOOL)wParam, NULL);
725 }
726 return FALSE;
727 }
728
729 static const WCHAR swBrowseTemplateName[] = {
730 'S','H','B','R','S','F','O','R','F','O','L','D','E','R','_','M','S','G','B','O','X',0};
731 static const WCHAR swNewBrowseTemplateName[] = {
732 'S','H','N','E','W','B','R','S','F','O','R','F','O','L','D','E','R','_','M','S','G','B','O','X',0};
733
734 /*************************************************************************
735 * SHBrowseForFolderA [SHELL32.@]
736 * SHBrowseForFolder [SHELL32.@]
737 */
738 LPITEMIDLIST WINAPI SHBrowseForFolderA (LPBROWSEINFOA lpbi)
739 {
740 BROWSEINFOW bi;
741 LPITEMIDLIST lpid;
742 INT len;
743 LPWSTR title;
744
745 TRACE("%p\n", lpbi);
746
747 bi.hwndOwner = lpbi->hwndOwner;
748 bi.pidlRoot = lpbi->pidlRoot;
749 if (lpbi->pszDisplayName)
750 {
751 bi.pszDisplayName = (WCHAR *)HeapAlloc( GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR) );
752 MultiByteToWideChar( CP_ACP, 0, lpbi->pszDisplayName, -1, bi.pszDisplayName, MAX_PATH );
753 }
754 else
755 bi.pszDisplayName = NULL;
756
757 if (lpbi->lpszTitle)
758 {
759 len = MultiByteToWideChar( CP_ACP, 0, lpbi->lpszTitle, -1, NULL, 0 );
760 title = (WCHAR *)HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
761 MultiByteToWideChar( CP_ACP, 0, lpbi->lpszTitle, -1, title, len );
762 }
763 else
764 title = NULL;
765
766 bi.lpszTitle = title;
767 bi.ulFlags = lpbi->ulFlags;
768 bi.lpfn = lpbi->lpfn;
769 bi.lParam = lpbi->lParam;
770 bi.iImage = lpbi->iImage;
771 lpid = SHBrowseForFolderW( &bi );
772 if (bi.pszDisplayName)
773 {
774 WideCharToMultiByte( CP_ACP, 0, bi.pszDisplayName, -1,
775 lpbi->pszDisplayName, MAX_PATH, 0, NULL);
776 HeapFree( GetProcessHeap(), 0, bi.pszDisplayName );
777 }
778 HeapFree(GetProcessHeap(), 0, title);
779 lpbi->iImage = bi.iImage;
780 return lpid;
781 }
782
783
784 /*************************************************************************
785 * SHBrowseForFolderW [SHELL32.@]
786 *
787 * NOTES
788 * crashes when passed a null pointer
789 */
790 LPITEMIDLIST WINAPI SHBrowseForFolderW (LPBROWSEINFOW lpbi)
791 {
792 browse_info info;
793 DWORD r;
794 HRESULT hr;
795 const WCHAR * templateName;
796
797 info.hWnd = 0;
798 info.pidlRet = NULL;
799 info.lpBrowseInfo = lpbi;
800 info.hwndTreeView = NULL;
801
802 hr = OleInitialize(NULL);
803
804 if (lpbi->ulFlags & BIF_NEWDIALOGSTYLE)
805 templateName = swNewBrowseTemplateName;
806 else
807 templateName = swBrowseTemplateName;
808 r = DialogBoxParamW( shell32_hInstance, templateName, lpbi->hwndOwner,
809 BrsFolderDlgProc, (LPARAM)&info );
810 if (SUCCEEDED(hr))
811 OleUninitialize();
812 if (!r)
813 return NULL;
814
815 return info.pidlRet;
816 }