2 * Copyright 1999 Juergen Schmied
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.
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.
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
20 * - many flags unimplemented
21 * - implement new dialog style "make new folder" button
23 * - implement new dialog style resizing
30 #define NONAMELESSUNION
31 #define NONAMELESSSTRUCT
33 #include "wine/debug.h"
34 #include "undocshell.h"
36 #include "shell32_main.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
42 typedef struct tagbrowse_info
46 LPBROWSEINFOW lpBrowseInfo
;
50 typedef struct tagTV_ITEMDATA
52 LPSHELLFOLDER lpsfParent
; /* IShellFolder of the parent */
53 LPITEMIDLIST lpi
; /* PIDL relative to parent */
54 LPITEMIDLIST lpifq
; /* Fully qualified PIDL */
55 IEnumIDList
* pEnumIL
; /* Children iterator */
56 } TV_ITEMDATA
, *LPTV_ITEMDATA
;
58 #define SUPPORTEDFLAGS (BIF_STATUSTEXT | \
59 BIF_BROWSEFORCOMPUTER | \
60 BIF_RETURNFSANCESTORS | \
61 BIF_RETURNONLYFSDIRS | \
62 BIF_NONEWFOLDERBUTTON | \
63 BIF_NEWDIALOGSTYLE | \
64 BIF_BROWSEINCLUDEFILES)
66 static void FillTreeView(browse_info
*, LPSHELLFOLDER
,
67 LPITEMIDLIST
, HTREEITEM
, IEnumIDList
*);
68 static HTREEITEM
InsertTreeViewItem( browse_info
*, IShellFolder
*,
69 LPCITEMIDLIST
, LPCITEMIDLIST
, IEnumIDList
*, HTREEITEM
);
71 static const WCHAR szBrowseFolderInfo
[] = {
72 '_','_','W','I','N','E','_',
73 'B','R','S','F','O','L','D','E','R','D','L','G','_',
77 static inline DWORD
BrowseFlagsToSHCONTF(UINT ulFlags
)
79 return SHCONTF_FOLDERS
| (ulFlags
& BIF_BROWSEINCLUDEFILES
? SHCONTF_NONFOLDERS
: 0);
82 static void browsefolder_callback( LPBROWSEINFOW lpBrowseInfo
, HWND hWnd
,
83 UINT msg
, LPARAM param
)
85 if (!lpBrowseInfo
->lpfn
)
87 lpBrowseInfo
->lpfn( hWnd
, msg
, param
, lpBrowseInfo
->lParam
);
90 /******************************************************************************
91 * InitializeTreeView [Internal]
93 * Called from WM_INITDIALOG handler.
96 * hwndParent [I] The BrowseForFolder dialog
97 * root [I] ITEMIDLIST of the root shell folder
99 static void InitializeTreeView( browse_info
*info
)
101 LPITEMIDLIST pidlParent
, pidlChild
;
102 HIMAGELIST hImageList
;
104 IShellFolder
*lpsfParent
, *lpsfRoot
;
105 IEnumIDList
* pEnumChildren
= NULL
;
108 LPCITEMIDLIST root
= info
->lpBrowseInfo
->pidlRoot
;
110 TRACE("%p\n", info
);
112 Shell_GetImageList(NULL
, &hImageList
);
115 SendMessageW( info
->hwndTreeView
, TVM_SETIMAGELIST
, 0, (LPARAM
)hImageList
);
117 /* We want to call InsertTreeViewItem down the code, in order to insert
118 * the root item of the treeview. Due to InsertTreeViewItem's signature,
119 * we need the following to do this:
121 * + An ITEMIDLIST corresponding to _the parent_ of root.
122 * + An ITEMIDLIST, which is a relative path from root's parent to root
123 * (containing a single SHITEMID).
124 * + An IShellFolder interface pointer of root's parent folder.
126 * If root is 'Desktop', then root's parent is also 'Desktop'.
129 pidlParent
= ILClone(root
);
130 ILRemoveLastID(pidlParent
);
131 pidlChild
= ILClone(ILFindLastID(root
));
133 if (_ILIsDesktop(pidlParent
)) {
134 hr
= SHGetDesktopFolder(&lpsfParent
);
136 IShellFolder
*lpsfDesktop
;
137 hr
= SHGetDesktopFolder(&lpsfDesktop
);
138 if (!SUCCEEDED(hr
)) {
139 WARN("SHGetDesktopFolder failed! hr = %08x\n", hr
);
142 hr
= IShellFolder_BindToObject(lpsfDesktop
, pidlParent
, 0, &IID_IShellFolder
, (LPVOID
*)&lpsfParent
);
143 IShellFolder_Release(lpsfDesktop
);
146 if (!SUCCEEDED(hr
)) {
147 WARN("Could not bind to parent shell folder! hr = %08x\n", hr
);
151 if (pidlChild
&& pidlChild
->mkid
.cb
) {
152 hr
= IShellFolder_BindToObject(lpsfParent
, pidlChild
, 0, &IID_IShellFolder
, (LPVOID
*)&lpsfRoot
);
154 lpsfRoot
= lpsfParent
;
155 hr
= IShellFolder_AddRef(lpsfParent
);
158 if (!SUCCEEDED(hr
)) {
159 WARN("Could not bind to root shell folder! hr = %08x\n", hr
);
160 IShellFolder_Release(lpsfParent
);
164 flags
= BrowseFlagsToSHCONTF( info
->lpBrowseInfo
->ulFlags
);
165 hr
= IShellFolder_EnumObjects( lpsfRoot
, info
->hWnd
, flags
, &pEnumChildren
);
166 if (!SUCCEEDED(hr
)) {
167 WARN("Could not get child iterator! hr = %08x\n", hr
);
168 IShellFolder_Release(lpsfParent
);
169 IShellFolder_Release(lpsfRoot
);
173 SendMessageW( info
->hwndTreeView
, TVM_DELETEITEM
, 0, (LPARAM
)TVI_ROOT
);
174 item
= InsertTreeViewItem( info
, lpsfParent
, pidlChild
,
175 pidlParent
, pEnumChildren
, TVI_ROOT
);
176 SendMessageW( info
->hwndTreeView
, TVM_EXPAND
, TVE_EXPAND
, (LPARAM
)item
);
178 IShellFolder_Release(lpsfRoot
);
179 IShellFolder_Release(lpsfParent
);
182 static int GetIcon(LPCITEMIDLIST lpi
, UINT uFlags
)
185 SHGetFileInfoW((LPCWSTR
)lpi
, 0 ,&sfi
, sizeof(SHFILEINFOW
), uFlags
);
189 static void GetNormalAndSelectedIcons(LPITEMIDLIST lpifq
, LPTVITEMW lpTV_ITEM
)
191 LPITEMIDLIST pidlDesktop
= NULL
;
194 TRACE("%p %p\n",lpifq
, lpTV_ITEM
);
198 pidlDesktop
= _ILCreateDesktop();
202 flags
= SHGFI_PIDL
| SHGFI_SYSICONINDEX
| SHGFI_SMALLICON
;
203 lpTV_ITEM
->iImage
= GetIcon( lpifq
, flags
);
205 flags
= SHGFI_PIDL
| SHGFI_SYSICONINDEX
| SHGFI_SMALLICON
| SHGFI_OPENICON
;
206 lpTV_ITEM
->iSelectedImage
= GetIcon( lpifq
, flags
);
209 ILFree( pidlDesktop
);
212 /******************************************************************************
215 * Query a shell folder for the display name of one of it's children
218 * lpsf [I] IShellFolder interface of the folder to be queried.
219 * lpi [I] ITEMIDLIST of the child, relative to parent
220 * dwFlags [I] as in IShellFolder::GetDisplayNameOf
221 * lpFriendlyName [O] The desired display name in unicode
227 static BOOL
GetName(LPSHELLFOLDER lpsf
, LPCITEMIDLIST lpi
, DWORD dwFlags
, LPWSTR lpFriendlyName
)
232 TRACE("%p %p %x %p\n", lpsf
, lpi
, dwFlags
, lpFriendlyName
);
233 if (SUCCEEDED(IShellFolder_GetDisplayNameOf(lpsf
, lpi
, dwFlags
, &str
)))
234 bSuccess
= StrRetToStrNW(lpFriendlyName
, MAX_PATH
, &str
, lpi
);
238 TRACE("-- %s\n", debugstr_w(lpFriendlyName
));
242 /******************************************************************************
243 * InsertTreeViewItem [Internal]
246 * info [I] data for the dialog
247 * lpsf [I] IShellFolder interface of the item's parent shell folder
248 * pidl [I] ITEMIDLIST of the child to insert, relative to parent
249 * pidlParent [I] ITEMIDLIST of the parent shell folder
250 * pEnumIL [I] Iterator for the children of the item to be inserted
251 * hParent [I] The treeview-item that represents the parent shell folder
254 * Success: Handle to the created and inserted treeview-item
257 static HTREEITEM
InsertTreeViewItem( browse_info
*info
, IShellFolder
* lpsf
,
258 LPCITEMIDLIST pidl
, LPCITEMIDLIST pidlParent
, IEnumIDList
* pEnumIL
,
262 TVINSERTSTRUCTW tvins
;
263 WCHAR szBuff
[MAX_PATH
];
264 LPTV_ITEMDATA lptvid
=0;
266 tvi
.mask
= TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_PARAM
;
268 tvi
.cChildren
= pEnumIL
? 1 : 0;
269 tvi
.mask
|= TVIF_CHILDREN
;
271 lptvid
= SHAlloc( sizeof(TV_ITEMDATA
) );
275 if (!GetName(lpsf
, pidl
, SHGDN_NORMAL
, szBuff
))
278 tvi
.pszText
= szBuff
;
279 tvi
.cchTextMax
= MAX_PATH
;
280 tvi
.lParam
= (LPARAM
)lptvid
;
282 IShellFolder_AddRef(lpsf
);
283 lptvid
->lpsfParent
= lpsf
;
284 lptvid
->lpi
= ILClone(pidl
);
285 lptvid
->lpifq
= pidlParent
? ILCombine(pidlParent
, pidl
) : ILClone(pidl
);
286 lptvid
->pEnumIL
= pEnumIL
;
287 GetNormalAndSelectedIcons(lptvid
->lpifq
, &tvi
);
290 tvins
.hInsertAfter
= NULL
;
291 tvins
.hParent
= hParent
;
293 return TreeView_InsertItemW( info
->hwndTreeView
, &tvins
);
296 /******************************************************************************
297 * FillTreeView [Internal]
299 * For each child (given by lpe) of the parent shell folder, which is given by
300 * lpsf and whose PIDL is pidl, insert a treeview-item right under hParent
303 * info [I] data for the dialog
304 * lpsf [I] IShellFolder interface of the parent shell folder
305 * pidl [I] ITEMIDLIST of the parent shell folder
306 * hParent [I] The treeview item that represents the parent shell folder
307 * lpe [I] An iterator for the children of the parent shell folder
309 static void FillTreeView( browse_info
*info
, IShellFolder
* lpsf
,
310 LPITEMIDLIST pidl
, HTREEITEM hParent
, IEnumIDList
* lpe
)
313 LPITEMIDLIST pidlTemp
= 0;
316 HWND hwnd
= GetParent( info
->hwndTreeView
);
318 TRACE("%p %p %p %p\n",lpsf
, pidl
, hParent
, lpe
);
320 /* No IEnumIDList -> No children */
324 SetCursor( LoadCursorA( 0, (LPSTR
)IDC_WAIT
) );
326 while (NOERROR
== IEnumIDList_Next(lpe
,1,&pidlTemp
,&ulFetched
))
328 ULONG ulAttrs
= SFGAO_HASSUBFOLDER
| SFGAO_FOLDER
;
329 IEnumIDList
* pEnumIL
= NULL
;
330 IShellFolder
* pSFChild
= NULL
;
331 IShellFolder_GetAttributesOf(lpsf
, 1, (LPCITEMIDLIST
*)&pidlTemp
, &ulAttrs
);
332 if (ulAttrs
& SFGAO_FOLDER
)
334 hr
= IShellFolder_BindToObject(lpsf
,pidlTemp
,NULL
,&IID_IShellFolder
,(LPVOID
*)&pSFChild
);
337 DWORD flags
= BrowseFlagsToSHCONTF(info
->lpBrowseInfo
->ulFlags
);
338 hr
= IShellFolder_EnumObjects(pSFChild
, hwnd
, flags
, &pEnumIL
);
341 if ((IEnumIDList_Skip(pEnumIL
, 1) != S_OK
) ||
342 FAILED(IEnumIDList_Reset(pEnumIL
)))
344 IEnumIDList_Release(pEnumIL
);
348 IShellFolder_Release(pSFChild
);
352 if (!(hPrev
= InsertTreeViewItem(info
, lpsf
, pidlTemp
, pidl
, pEnumIL
, hParent
)))
354 SHFree(pidlTemp
); /* Finally, free the pidl that the shell gave us... */
360 SetCursor(LoadCursorW(0, (LPWSTR
)IDC_ARROW
));
364 static inline BOOL
PIDLIsType(LPCITEMIDLIST pidl
, PIDLTYPE type
)
366 LPPIDLDATA data
= _ILGetDataPointer(pidl
);
369 return (data
->type
== type
);
372 static void BrsFolder_CheckValidSelection( browse_info
*info
, LPTV_ITEMDATA lptvid
)
374 LPBROWSEINFOW lpBrowseInfo
= info
->lpBrowseInfo
;
375 LPCITEMIDLIST pidl
= lptvid
->lpi
;
376 BOOL bEnabled
= TRUE
;
380 if ((lpBrowseInfo
->ulFlags
& BIF_BROWSEFORCOMPUTER
) &&
381 !PIDLIsType(pidl
, PT_COMP
))
383 if (lpBrowseInfo
->ulFlags
& BIF_RETURNFSANCESTORS
)
385 dwAttributes
= SFGAO_FILESYSANCESTOR
| SFGAO_FILESYSTEM
;
386 r
= IShellFolder_GetAttributesOf(lptvid
->lpsfParent
, 1,
387 (LPCITEMIDLIST
*)&lptvid
->lpi
, &dwAttributes
);
388 if (FAILED(r
) || !(dwAttributes
& (SFGAO_FILESYSANCESTOR
|SFGAO_FILESYSTEM
)))
391 if (lpBrowseInfo
->ulFlags
& BIF_RETURNONLYFSDIRS
)
393 dwAttributes
= SFGAO_FOLDER
| SFGAO_FILESYSTEM
;
394 r
= IShellFolder_GetAttributesOf(lptvid
->lpsfParent
, 1,
395 (LPCITEMIDLIST
*)&lptvid
->lpi
, &dwAttributes
);
397 ((dwAttributes
& (SFGAO_FOLDER
|SFGAO_FILESYSTEM
)) != (SFGAO_FOLDER
|SFGAO_FILESYSTEM
)))
402 SendMessageW(info
->hWnd
, BFFM_ENABLEOK
, 0, (LPARAM
)bEnabled
);
405 static LRESULT
BrsFolder_Treeview_Delete( browse_info
*info
, NMTREEVIEWW
*pnmtv
)
407 LPTV_ITEMDATA lptvid
= (LPTV_ITEMDATA
)pnmtv
->itemOld
.lParam
;
409 TRACE("TVN_DELETEITEMA/W %p\n", lptvid
);
411 IShellFolder_Release(lptvid
->lpsfParent
);
413 IEnumIDList_Release(lptvid
->pEnumIL
);
415 SHFree(lptvid
->lpifq
);
420 static LRESULT
BrsFolder_Treeview_Expand( browse_info
*info
, NMTREEVIEWW
*pnmtv
)
422 IShellFolder
*lpsf2
= NULL
;
423 LPTV_ITEMDATA lptvid
= (LPTV_ITEMDATA
) pnmtv
->itemNew
.lParam
;
426 TRACE("TVN_ITEMEXPANDINGA/W\n");
428 if ((pnmtv
->itemNew
.state
& TVIS_EXPANDEDONCE
))
431 if (lptvid
->lpi
&& lptvid
->lpi
->mkid
.cb
) {
432 r
= IShellFolder_BindToObject( lptvid
->lpsfParent
, lptvid
->lpi
, 0,
433 (REFIID
)&IID_IShellFolder
, (LPVOID
*)&lpsf2
);
435 lpsf2
= lptvid
->lpsfParent
;
436 r
= IShellFolder_AddRef(lpsf2
);
440 FillTreeView( info
, lpsf2
, lptvid
->lpifq
, pnmtv
->itemNew
.hItem
, lptvid
->pEnumIL
);
442 /* My Computer is already sorted and trying to do a simple text
443 * sort will only mess things up */
444 if (!_ILIsMyComputer(lptvid
->lpi
))
445 SendMessageW( info
->hwndTreeView
, TVM_SORTCHILDREN
,
446 FALSE
, (LPARAM
)pnmtv
->itemNew
.hItem
);
451 static HRESULT
BrsFolder_Treeview_Changed( browse_info
*info
, NMTREEVIEWW
*pnmtv
)
453 LPTV_ITEMDATA lptvid
= (LPTV_ITEMDATA
) pnmtv
->itemNew
.lParam
;
455 lptvid
= (LPTV_ITEMDATA
) pnmtv
->itemNew
.lParam
;
456 info
->pidlRet
= lptvid
->lpifq
;
457 browsefolder_callback( info
->lpBrowseInfo
, info
->hWnd
, BFFM_SELCHANGED
,
458 (LPARAM
)info
->pidlRet
);
459 BrsFolder_CheckValidSelection( info
, lptvid
);
463 static LRESULT
BrsFolder_OnNotify( browse_info
*info
, UINT CtlID
, LPNMHDR lpnmh
)
465 NMTREEVIEWW
*pnmtv
= (NMTREEVIEWW
*)lpnmh
;
467 TRACE("%p %x %p msg=%x\n", info
, CtlID
, lpnmh
, pnmtv
->hdr
.code
);
469 if (pnmtv
->hdr
.idFrom
!= IDD_TREEVIEW
)
472 switch (pnmtv
->hdr
.code
)
474 case TVN_DELETEITEMA
:
475 case TVN_DELETEITEMW
:
476 return BrsFolder_Treeview_Delete( info
, pnmtv
);
478 case TVN_ITEMEXPANDINGA
:
479 case TVN_ITEMEXPANDINGW
:
480 return BrsFolder_Treeview_Expand( info
, pnmtv
);
482 case TVN_SELCHANGEDA
:
483 case TVN_SELCHANGEDW
:
484 return BrsFolder_Treeview_Changed( info
, pnmtv
);
487 WARN("unhandled (%d)\n", pnmtv
->hdr
.code
);
495 static BOOL
BrsFolder_OnCreate( HWND hWnd
, browse_info
*info
)
497 LPBROWSEINFOW lpBrowseInfo
= info
->lpBrowseInfo
;
500 SetPropW( hWnd
, szBrowseFolderInfo
, info
);
502 if (lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
)
503 FIXME("flags BIF_NEWDIALOGSTYLE partially implemented\n");
504 if (lpBrowseInfo
->ulFlags
& ~SUPPORTEDFLAGS
)
505 FIXME("flags %x not implemented\n", lpBrowseInfo
->ulFlags
& ~SUPPORTEDFLAGS
);
507 if (lpBrowseInfo
->lpszTitle
)
508 SetWindowTextW( GetDlgItem(hWnd
, IDD_TITLE
), lpBrowseInfo
->lpszTitle
);
510 ShowWindow( GetDlgItem(hWnd
, IDD_TITLE
), SW_HIDE
);
512 if (!(lpBrowseInfo
->ulFlags
& BIF_STATUSTEXT
)
513 || (lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
))
514 ShowWindow( GetDlgItem(hWnd
, IDD_STATUS
), SW_HIDE
);
516 /* Hide "Make New Folder" Button? */
517 if ((lpBrowseInfo
->ulFlags
& BIF_NONEWFOLDERBUTTON
)
518 || !(lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
))
519 ShowWindow( GetDlgItem(hWnd
, IDD_MAKENEWFOLDER
), SW_HIDE
);
521 /* Hide the editbox? */
522 if (!(lpBrowseInfo
->ulFlags
& BIF_EDITBOX
))
524 ShowWindow( GetDlgItem(hWnd
, IDD_FOLDER
), SW_HIDE
);
525 ShowWindow( GetDlgItem(hWnd
, IDD_FOLDERTEXT
), SW_HIDE
);
528 info
->hwndTreeView
= GetDlgItem( hWnd
, IDD_TREEVIEW
);
529 if (info
->hwndTreeView
)
531 InitializeTreeView( info
);
533 /* Resize the treeview if there's not editbox */
534 if ((lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
)
535 && !(lpBrowseInfo
->ulFlags
& BIF_EDITBOX
))
538 GetClientRect(info
->hwndTreeView
, &rc
);
539 SetWindowPos(info
->hwndTreeView
, HWND_TOP
, 0, 0,
540 rc
.right
, rc
.bottom
+ 40, SWP_NOMOVE
);
544 ERR("treeview control missing!\n");
546 browsefolder_callback( info
->lpBrowseInfo
, hWnd
, BFFM_INITIALIZED
, 0 );
551 static BOOL
BrsFolder_OnCommand( browse_info
*info
, UINT id
)
553 LPBROWSEINFOW lpBrowseInfo
= info
->lpBrowseInfo
;
558 /* The original pidl is owned by the treeview and will be free'd. */
559 info
->pidlRet
= ILClone(info
->pidlRet
);
560 if (info
->pidlRet
== NULL
) /* A null pidl would mean a cancel */
561 info
->pidlRet
= _ILCreateDesktop();
562 pdump( info
->pidlRet
);
563 if (lpBrowseInfo
->pszDisplayName
)
564 SHGetPathFromIDListW( info
->pidlRet
, lpBrowseInfo
->pszDisplayName
);
565 EndDialog( info
->hWnd
, 1 );
569 EndDialog( info
->hWnd
, 0 );
572 case IDD_MAKENEWFOLDER
:
573 FIXME("make new folder not implemented\n");
579 static BOOL
BrsFolder_OnSetExpanded(browse_info
*info
, LPVOID selection
,
580 BOOL is_str
, HTREEITEM
*pItem
)
582 LPITEMIDLIST pidlSelection
= (LPITEMIDLIST
)selection
;
583 LPCITEMIDLIST pidlCurrent
, pidlRoot
;
585 BOOL bResult
= FALSE
;
587 /* If 'selection' is a string, convert to a Shell ID List. */
589 IShellFolder
*psfDesktop
;
592 hr
= SHGetDesktopFolder(&psfDesktop
);
596 hr
= IShellFolder_ParseDisplayName(psfDesktop
, NULL
, NULL
,
597 (LPOLESTR
)selection
, NULL
, &pidlSelection
, NULL
);
598 IShellFolder_Release(psfDesktop
);
603 /* Move pidlCurrent behind the SHITEMIDs in pidlSelection, which are the root of
604 * the sub-tree currently displayed. */
605 pidlRoot
= info
->lpBrowseInfo
->pidlRoot
;
606 pidlCurrent
= pidlSelection
;
607 while (!_ILIsEmpty(pidlRoot
) && _ILIsEqualSimple(pidlRoot
, pidlCurrent
)) {
608 pidlRoot
= ILGetNext(pidlRoot
);
609 pidlCurrent
= ILGetNext(pidlCurrent
);
612 /* The given ID List is not part of the SHBrowseForFolder's current sub-tree. */
613 if (!_ILIsEmpty(pidlRoot
))
616 /* Initialize item to point to the first child of the root folder. */
617 memset(&item
, 0, sizeof(item
));
618 item
.mask
= TVIF_PARAM
;
619 item
.hItem
= TreeView_GetRoot(info
->hwndTreeView
);
621 item
.hItem
= TreeView_GetChild(info
->hwndTreeView
, item
.hItem
);
623 /* Walk the tree along the nodes corresponding to the remaining ITEMIDLIST */
624 while (item
.hItem
&& !_ILIsEmpty(pidlCurrent
)) {
625 LPTV_ITEMDATA pItemData
;
627 SendMessageW(info
->hwndTreeView
, TVM_GETITEMW
, 0, (LPARAM
)&item
);
628 pItemData
= (LPTV_ITEMDATA
)item
.lParam
;
630 if (_ILIsEqualSimple(pItemData
->lpi
, pidlCurrent
)) {
631 pidlCurrent
= ILGetNext(pidlCurrent
);
632 if (!_ILIsEmpty(pidlCurrent
)) {
633 /* Only expand current node and move on to it's first child,
634 * if we didn't already reach the last SHITEMID */
635 SendMessageW(info
->hwndTreeView
, TVM_EXPAND
, TVE_EXPAND
, (LPARAM
)item
.hItem
);
636 item
.hItem
= TreeView_GetChild(info
->hwndTreeView
, item
.hItem
);
639 item
.hItem
= TreeView_GetNextSibling(info
->hwndTreeView
, item
.hItem
);
643 if (_ILIsEmpty(pidlCurrent
) && item
.hItem
)
647 if (pidlSelection
&& pidlSelection
!= (LPITEMIDLIST
)selection
)
648 ILFree(pidlSelection
);
656 static BOOL
BrsFolder_OnSetSelectionW(browse_info
*info
, LPVOID selection
, BOOL is_str
) {
660 bResult
= BrsFolder_OnSetExpanded(info
, selection
, is_str
, &hItem
);
662 SendMessageW(info
->hwndTreeView
, TVM_SELECTITEM
, TVGN_CARET
, (LPARAM
)hItem
);
666 static BOOL
BrsFolder_OnSetSelectionA(browse_info
*info
, LPVOID selection
, BOOL is_str
) {
667 LPWSTR selectionW
= NULL
;
672 return BrsFolder_OnSetSelectionW(info
, selection
, is_str
);
674 if ((length
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)selection
, -1, NULL
, 0)) &&
675 (selectionW
= HeapAlloc(GetProcessHeap(), 0, length
* sizeof(WCHAR
))) &&
676 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)selection
, -1, selectionW
, length
))
678 result
= BrsFolder_OnSetSelectionW(info
, selectionW
, is_str
);
681 HeapFree(GetProcessHeap(), 0, selectionW
);
685 /*************************************************************************
686 * BrsFolderDlgProc32 (not an exported API function)
688 static INT_PTR CALLBACK
BrsFolderDlgProc( HWND hWnd
, UINT msg
, WPARAM wParam
,
693 TRACE("hwnd=%p msg=%04x 0x%08lx 0x%08lx\n", hWnd
, msg
, wParam
, lParam
);
695 if (msg
== WM_INITDIALOG
)
696 return BrsFolder_OnCreate( hWnd
, (browse_info
*) lParam
);
698 info
= (browse_info
*) GetPropW( hWnd
, szBrowseFolderInfo
);
703 return BrsFolder_OnNotify( info
, (UINT
)wParam
, (LPNMHDR
)lParam
);
706 return BrsFolder_OnCommand( info
, wParam
);
708 case BFFM_SETSTATUSTEXTA
:
709 TRACE("Set status %s\n", debugstr_a((LPSTR
)lParam
));
710 SetWindowTextA(GetDlgItem(hWnd
, IDD_STATUS
), (LPSTR
)lParam
);
713 case BFFM_SETSTATUSTEXTW
:
714 TRACE("Set status %s\n", debugstr_w((LPWSTR
)lParam
));
715 SetWindowTextW(GetDlgItem(hWnd
, IDD_STATUS
), (LPWSTR
)lParam
);
719 TRACE("Enable %ld\n", lParam
);
720 EnableWindow(GetDlgItem(hWnd
, 1), (lParam
)?TRUE
:FALSE
);
723 case BFFM_SETOKTEXT
: /* unicode only */
724 TRACE("Set OK text %s\n", debugstr_w((LPWSTR
)wParam
));
725 SetWindowTextW(GetDlgItem(hWnd
, 1), (LPWSTR
)wParam
);
728 case BFFM_SETSELECTIONA
:
729 return BrsFolder_OnSetSelectionA(info
, (LPVOID
)lParam
, (BOOL
)wParam
);
731 case BFFM_SETSELECTIONW
:
732 return BrsFolder_OnSetSelectionW(info
, (LPVOID
)lParam
, (BOOL
)wParam
);
734 case BFFM_SETEXPANDED
: /* unicode only */
735 return BrsFolder_OnSetExpanded(info
, (LPVOID
)lParam
, (BOOL
)wParam
, NULL
);
740 static const WCHAR swBrowseTemplateName
[] = {
741 'S','H','B','R','S','F','O','R','F','O','L','D','E','R','_','M','S','G','B','O','X',0};
742 static const WCHAR swNewBrowseTemplateName
[] = {
743 'S','H','N','E','W','B','R','S','F','O','R','F','O','L','D','E','R','_','M','S','G','B','O','X',0};
745 /*************************************************************************
746 * SHBrowseForFolderA [SHELL32.@]
747 * SHBrowseForFolder [SHELL32.@]
749 LPITEMIDLIST WINAPI
SHBrowseForFolderA (LPBROWSEINFOA lpbi
)
758 bi
.hwndOwner
= lpbi
->hwndOwner
;
759 bi
.pidlRoot
= lpbi
->pidlRoot
;
760 if (lpbi
->pszDisplayName
)
762 bi
.pszDisplayName
= HeapAlloc( GetProcessHeap(), 0, MAX_PATH
* sizeof(WCHAR
) );
763 MultiByteToWideChar( CP_ACP
, 0, lpbi
->pszDisplayName
, -1, bi
.pszDisplayName
, MAX_PATH
);
766 bi
.pszDisplayName
= NULL
;
770 len
= MultiByteToWideChar( CP_ACP
, 0, lpbi
->lpszTitle
, -1, NULL
, 0 );
771 title
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
772 MultiByteToWideChar( CP_ACP
, 0, lpbi
->lpszTitle
, -1, title
, len
);
777 bi
.lpszTitle
= title
;
778 bi
.ulFlags
= lpbi
->ulFlags
;
779 bi
.lpfn
= lpbi
->lpfn
;
780 bi
.lParam
= lpbi
->lParam
;
781 bi
.iImage
= lpbi
->iImage
;
782 lpid
= SHBrowseForFolderW( &bi
);
783 if (bi
.pszDisplayName
)
785 WideCharToMultiByte( CP_ACP
, 0, bi
.pszDisplayName
, -1,
786 lpbi
->pszDisplayName
, MAX_PATH
, 0, NULL
);
787 HeapFree( GetProcessHeap(), 0, bi
.pszDisplayName
);
789 HeapFree(GetProcessHeap(), 0, title
);
790 lpbi
->iImage
= bi
.iImage
;
795 /*************************************************************************
796 * SHBrowseForFolderW [SHELL32.@]
799 * crashes when passed a null pointer
801 LPITEMIDLIST WINAPI
SHBrowseForFolderW (LPBROWSEINFOW lpbi
)
806 const WCHAR
* templateName
;
810 info
.lpBrowseInfo
= lpbi
;
811 info
.hwndTreeView
= NULL
;
813 hr
= OleInitialize(NULL
);
815 if (lpbi
->ulFlags
& BIF_NEWDIALOGSTYLE
)
816 templateName
= swNewBrowseTemplateName
;
818 templateName
= swBrowseTemplateName
;
819 r
= DialogBoxParamW( shell32_hInstance
, templateName
, lpbi
->hwndOwner
,
820 BrsFolderDlgProc
, (LPARAM
)&info
);