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
29 #define NONAMELESSUNION
30 #define NONAMELESSSTRUCT
32 #include "wine/debug.h"
33 #include "undocshell.h"
35 #include "shell32_main.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
41 /* original margins and control size */
42 typedef struct tagLAYOUT_DATA
44 LONG left
, width
, right
;
45 LONG top
, height
, bottom
;
48 typedef struct tagbrowse_info
52 LPBROWSEINFOW lpBrowseInfo
;
54 LAYOUT_DATA
*layout
; /* filled by LayoutInit, used by LayoutUpdate */
58 typedef struct tagTV_ITEMDATA
60 LPSHELLFOLDER lpsfParent
; /* IShellFolder of the parent */
61 LPITEMIDLIST lpi
; /* PIDL relative to parent */
62 LPITEMIDLIST lpifq
; /* Fully qualified PIDL */
63 IEnumIDList
* pEnumIL
; /* Children iterator */
64 } TV_ITEMDATA
, *LPTV_ITEMDATA
;
66 typedef struct tagLAYOUT_INFO
68 int iItemId
; /* control id */
69 DWORD dwAnchor
; /* BF_* flags specifying which margins should remain constant */
72 static const LAYOUT_INFO g_layout_info
[] =
74 {IDD_TITLE
, BF_TOP
|BF_LEFT
|BF_RIGHT
},
75 {IDD_STATUS
, BF_TOP
|BF_LEFT
|BF_RIGHT
},
76 {IDD_FOLDER
, BF_TOP
|BF_LEFT
|BF_RIGHT
},
77 {IDD_TREEVIEW
, BF_TOP
|BF_BOTTOM
|BF_LEFT
|BF_RIGHT
},
78 {IDD_FOLDERTEXT
, BF_BOTTOM
|BF_LEFT
|BF_RIGHT
},
79 {IDD_MAKENEWFOLDER
, BF_BOTTOM
|BF_LEFT
},
80 {IDOK
, BF_BOTTOM
|BF_RIGHT
},
81 {IDCANCEL
, BF_BOTTOM
|BF_RIGHT
}
84 #define LAYOUT_INFO_COUNT (sizeof(g_layout_info)/sizeof(g_layout_info[0]))
86 #define SUPPORTEDFLAGS (BIF_STATUSTEXT | \
87 BIF_BROWSEFORCOMPUTER | \
88 BIF_RETURNFSANCESTORS | \
89 BIF_RETURNONLYFSDIRS | \
90 BIF_NONEWFOLDERBUTTON | \
91 BIF_NEWDIALOGSTYLE | \
92 BIF_BROWSEINCLUDEFILES)
94 static void FillTreeView(browse_info
*, LPSHELLFOLDER
,
95 LPITEMIDLIST
, HTREEITEM
, IEnumIDList
*);
96 static HTREEITEM
InsertTreeViewItem( browse_info
*, IShellFolder
*,
97 LPCITEMIDLIST
, LPCITEMIDLIST
, IEnumIDList
*, HTREEITEM
);
99 static const WCHAR szBrowseFolderInfo
[] = {
100 '_','_','W','I','N','E','_',
101 'B','R','S','F','O','L','D','E','R','D','L','G','_',
105 static inline DWORD
BrowseFlagsToSHCONTF(UINT ulFlags
)
107 return SHCONTF_FOLDERS
| (ulFlags
& BIF_BROWSEINCLUDEFILES
? SHCONTF_NONFOLDERS
: 0);
110 static void browsefolder_callback( LPBROWSEINFOW lpBrowseInfo
, HWND hWnd
,
111 UINT msg
, LPARAM param
)
113 if (!lpBrowseInfo
->lpfn
)
115 lpBrowseInfo
->lpfn( hWnd
, msg
, param
, lpBrowseInfo
->lParam
);
118 static LAYOUT_DATA
*LayoutInit(HWND hwnd
, const LAYOUT_INFO
*layout_info
, int layout_count
)
124 GetClientRect(hwnd
, &rcWnd
);
125 data
= SHAlloc(sizeof(LAYOUT_DATA
)*layout_count
);
126 for (i
= 0; i
< layout_count
; i
++)
129 HWND hItem
= GetDlgItem(hwnd
, layout_info
[i
].iItemId
);
132 ERR("Item %d not found\n", i
);
133 GetWindowRect(hItem
, &r
);
134 MapWindowPoints(HWND_DESKTOP
, hwnd
, (LPPOINT
)&r
, 2);
136 data
[i
].left
= r
.left
;
137 data
[i
].right
= rcWnd
.right
- r
.right
;
138 data
[i
].width
= r
.right
- r
.left
;
141 data
[i
].bottom
= rcWnd
.bottom
- r
.bottom
;
142 data
[i
].height
= r
.bottom
- r
.top
;
147 static void LayoutUpdate(HWND hwnd
, LAYOUT_DATA
*data
, const LAYOUT_INFO
*layout_info
, int layout_count
)
152 GetClientRect(hwnd
, &rcWnd
);
153 for (i
= 0; i
< layout_count
; i
++)
156 HWND hItem
= GetDlgItem(hwnd
, layout_info
[i
].iItemId
);
158 GetWindowRect(hItem
, &r
);
159 MapWindowPoints(HWND_DESKTOP
, hwnd
, (LPPOINT
)&r
, 2);
161 if (layout_info
[i
].dwAnchor
& BF_RIGHT
)
163 r
.right
= rcWnd
.right
- data
[i
].right
;
164 if (!(layout_info
[i
].dwAnchor
& BF_LEFT
))
165 r
.left
= r
.right
- data
[i
].width
;
168 if (layout_info
[i
].dwAnchor
& BF_BOTTOM
)
170 r
.bottom
= rcWnd
.bottom
- data
[i
].bottom
;
171 if (!(layout_info
[i
].dwAnchor
& BF_TOP
))
172 r
.top
= r
.bottom
- data
[i
].height
;
175 SetWindowPos(hItem
, NULL
, r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
, SWP_NOZORDER
);
180 /******************************************************************************
181 * InitializeTreeView [Internal]
183 * Called from WM_INITDIALOG handler.
186 * hwndParent [I] The BrowseForFolder dialog
187 * root [I] ITEMIDLIST of the root shell folder
189 static void InitializeTreeView( browse_info
*info
)
191 LPITEMIDLIST pidlParent
, pidlChild
;
192 HIMAGELIST hImageList
;
194 IShellFolder
*lpsfParent
, *lpsfRoot
;
195 IEnumIDList
* pEnumChildren
= NULL
;
198 LPCITEMIDLIST root
= info
->lpBrowseInfo
->pidlRoot
;
200 TRACE("%p\n", info
);
202 Shell_GetImageList(NULL
, &hImageList
);
205 SendMessageW( info
->hwndTreeView
, TVM_SETIMAGELIST
, 0, (LPARAM
)hImageList
);
207 /* We want to call InsertTreeViewItem down the code, in order to insert
208 * the root item of the treeview. Due to InsertTreeViewItem's signature,
209 * we need the following to do this:
211 * + An ITEMIDLIST corresponding to _the parent_ of root.
212 * + An ITEMIDLIST, which is a relative path from root's parent to root
213 * (containing a single SHITEMID).
214 * + An IShellFolder interface pointer of root's parent folder.
216 * If root is 'Desktop', then root's parent is also 'Desktop'.
219 pidlParent
= ILClone(root
);
220 ILRemoveLastID(pidlParent
);
221 pidlChild
= ILClone(ILFindLastID(root
));
223 if (_ILIsDesktop(pidlParent
)) {
224 hr
= SHGetDesktopFolder(&lpsfParent
);
226 IShellFolder
*lpsfDesktop
;
227 hr
= SHGetDesktopFolder(&lpsfDesktop
);
229 WARN("SHGetDesktopFolder failed! hr = %08x\n", hr
);
232 hr
= IShellFolder_BindToObject(lpsfDesktop
, pidlParent
, 0, &IID_IShellFolder
, (LPVOID
*)&lpsfParent
);
233 IShellFolder_Release(lpsfDesktop
);
237 WARN("Could not bind to parent shell folder! hr = %08x\n", hr
);
241 if (pidlChild
&& pidlChild
->mkid
.cb
) {
242 hr
= IShellFolder_BindToObject(lpsfParent
, pidlChild
, 0, &IID_IShellFolder
, (LPVOID
*)&lpsfRoot
);
244 lpsfRoot
= lpsfParent
;
245 hr
= IShellFolder_AddRef(lpsfParent
);
249 WARN("Could not bind to root shell folder! hr = %08x\n", hr
);
250 IShellFolder_Release(lpsfParent
);
254 flags
= BrowseFlagsToSHCONTF( info
->lpBrowseInfo
->ulFlags
);
255 hr
= IShellFolder_EnumObjects( lpsfRoot
, info
->hWnd
, flags
, &pEnumChildren
);
257 WARN("Could not get child iterator! hr = %08x\n", hr
);
258 IShellFolder_Release(lpsfParent
);
259 IShellFolder_Release(lpsfRoot
);
263 SendMessageW( info
->hwndTreeView
, TVM_DELETEITEM
, 0, (LPARAM
)TVI_ROOT
);
264 item
= InsertTreeViewItem( info
, lpsfParent
, pidlChild
,
265 pidlParent
, pEnumChildren
, TVI_ROOT
);
266 SendMessageW( info
->hwndTreeView
, TVM_EXPAND
, TVE_EXPAND
, (LPARAM
)item
);
268 IShellFolder_Release(lpsfRoot
);
269 IShellFolder_Release(lpsfParent
);
272 static int GetIcon(LPCITEMIDLIST lpi
, UINT uFlags
)
275 SHGetFileInfoW((LPCWSTR
)lpi
, 0 ,&sfi
, sizeof(SHFILEINFOW
), uFlags
);
279 static void GetNormalAndSelectedIcons(LPITEMIDLIST lpifq
, LPTVITEMW lpTV_ITEM
)
281 LPITEMIDLIST pidlDesktop
= NULL
;
284 TRACE("%p %p\n",lpifq
, lpTV_ITEM
);
288 pidlDesktop
= _ILCreateDesktop();
292 flags
= SHGFI_PIDL
| SHGFI_SYSICONINDEX
| SHGFI_SMALLICON
;
293 lpTV_ITEM
->iImage
= GetIcon( lpifq
, flags
);
295 flags
= SHGFI_PIDL
| SHGFI_SYSICONINDEX
| SHGFI_SMALLICON
| SHGFI_OPENICON
;
296 lpTV_ITEM
->iSelectedImage
= GetIcon( lpifq
, flags
);
299 ILFree( pidlDesktop
);
302 /******************************************************************************
305 * Query a shell folder for the display name of one of it's children
308 * lpsf [I] IShellFolder interface of the folder to be queried.
309 * lpi [I] ITEMIDLIST of the child, relative to parent
310 * dwFlags [I] as in IShellFolder::GetDisplayNameOf
311 * lpFriendlyName [O] The desired display name in unicode
317 static BOOL
GetName(LPSHELLFOLDER lpsf
, LPCITEMIDLIST lpi
, DWORD dwFlags
, LPWSTR lpFriendlyName
)
322 TRACE("%p %p %x %p\n", lpsf
, lpi
, dwFlags
, lpFriendlyName
);
323 if (SUCCEEDED(IShellFolder_GetDisplayNameOf(lpsf
, lpi
, dwFlags
, &str
)))
324 bSuccess
= StrRetToStrNW(lpFriendlyName
, MAX_PATH
, &str
, lpi
);
328 TRACE("-- %s\n", debugstr_w(lpFriendlyName
));
332 /******************************************************************************
333 * InsertTreeViewItem [Internal]
336 * info [I] data for the dialog
337 * lpsf [I] IShellFolder interface of the item's parent shell folder
338 * pidl [I] ITEMIDLIST of the child to insert, relative to parent
339 * pidlParent [I] ITEMIDLIST of the parent shell folder
340 * pEnumIL [I] Iterator for the children of the item to be inserted
341 * hParent [I] The treeview-item that represents the parent shell folder
344 * Success: Handle to the created and inserted treeview-item
347 static HTREEITEM
InsertTreeViewItem( browse_info
*info
, IShellFolder
* lpsf
,
348 LPCITEMIDLIST pidl
, LPCITEMIDLIST pidlParent
, IEnumIDList
* pEnumIL
,
352 TVINSERTSTRUCTW tvins
;
353 WCHAR szBuff
[MAX_PATH
];
354 LPTV_ITEMDATA lptvid
=0;
356 tvi
.mask
= TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_PARAM
;
358 tvi
.cChildren
= pEnumIL
? 1 : 0;
359 tvi
.mask
|= TVIF_CHILDREN
;
361 lptvid
= SHAlloc( sizeof(TV_ITEMDATA
) );
365 if (!GetName(lpsf
, pidl
, SHGDN_NORMAL
, szBuff
))
368 tvi
.pszText
= szBuff
;
369 tvi
.cchTextMax
= MAX_PATH
;
370 tvi
.lParam
= (LPARAM
)lptvid
;
372 IShellFolder_AddRef(lpsf
);
373 lptvid
->lpsfParent
= lpsf
;
374 lptvid
->lpi
= ILClone(pidl
);
375 lptvid
->lpifq
= pidlParent
? ILCombine(pidlParent
, pidl
) : ILClone(pidl
);
376 lptvid
->pEnumIL
= pEnumIL
;
377 GetNormalAndSelectedIcons(lptvid
->lpifq
, &tvi
);
380 tvins
.hInsertAfter
= NULL
;
381 tvins
.hParent
= hParent
;
383 return TreeView_InsertItemW( info
->hwndTreeView
, &tvins
);
386 /******************************************************************************
387 * FillTreeView [Internal]
389 * For each child (given by lpe) of the parent shell folder, which is given by
390 * lpsf and whose PIDL is pidl, insert a treeview-item right under hParent
393 * info [I] data for the dialog
394 * lpsf [I] IShellFolder interface of the parent shell folder
395 * pidl [I] ITEMIDLIST of the parent shell folder
396 * hParent [I] The treeview item that represents the parent shell folder
397 * lpe [I] An iterator for the children of the parent shell folder
399 static void FillTreeView( browse_info
*info
, IShellFolder
* lpsf
,
400 LPITEMIDLIST pidl
, HTREEITEM hParent
, IEnumIDList
* lpe
)
403 LPITEMIDLIST pidlTemp
= 0;
406 HWND hwnd
= GetParent( info
->hwndTreeView
);
408 TRACE("%p %p %p %p\n",lpsf
, pidl
, hParent
, lpe
);
410 /* No IEnumIDList -> No children */
414 SetCursor( LoadCursorA( 0, (LPSTR
)IDC_WAIT
) );
416 while (NOERROR
== IEnumIDList_Next(lpe
,1,&pidlTemp
,&ulFetched
))
418 ULONG ulAttrs
= SFGAO_HASSUBFOLDER
| SFGAO_FOLDER
;
419 IEnumIDList
* pEnumIL
= NULL
;
420 IShellFolder
* pSFChild
= NULL
;
421 IShellFolder_GetAttributesOf(lpsf
, 1, (LPCITEMIDLIST
*)&pidlTemp
, &ulAttrs
);
422 if (ulAttrs
& SFGAO_FOLDER
)
424 hr
= IShellFolder_BindToObject(lpsf
,pidlTemp
,NULL
,&IID_IShellFolder
,(LPVOID
*)&pSFChild
);
427 DWORD flags
= BrowseFlagsToSHCONTF(info
->lpBrowseInfo
->ulFlags
);
428 hr
= IShellFolder_EnumObjects(pSFChild
, hwnd
, flags
, &pEnumIL
);
431 if ((IEnumIDList_Skip(pEnumIL
, 1) != S_OK
) ||
432 FAILED(IEnumIDList_Reset(pEnumIL
)))
434 IEnumIDList_Release(pEnumIL
);
438 IShellFolder_Release(pSFChild
);
442 if (!(hPrev
= InsertTreeViewItem(info
, lpsf
, pidlTemp
, pidl
, pEnumIL
, hParent
)))
444 SHFree(pidlTemp
); /* Finally, free the pidl that the shell gave us... */
450 SetCursor(LoadCursorW(0, (LPWSTR
)IDC_ARROW
));
454 static inline BOOL
PIDLIsType(LPCITEMIDLIST pidl
, PIDLTYPE type
)
456 LPPIDLDATA data
= _ILGetDataPointer(pidl
);
459 return (data
->type
== type
);
462 static void BrsFolder_CheckValidSelection( browse_info
*info
, LPTV_ITEMDATA lptvid
)
464 LPBROWSEINFOW lpBrowseInfo
= info
->lpBrowseInfo
;
465 LPCITEMIDLIST pidl
= lptvid
->lpi
;
466 BOOL bEnabled
= TRUE
;
470 if ((lpBrowseInfo
->ulFlags
& BIF_BROWSEFORCOMPUTER
) &&
471 !PIDLIsType(pidl
, PT_COMP
))
473 if (lpBrowseInfo
->ulFlags
& BIF_RETURNFSANCESTORS
)
475 dwAttributes
= SFGAO_FILESYSANCESTOR
| SFGAO_FILESYSTEM
;
476 r
= IShellFolder_GetAttributesOf(lptvid
->lpsfParent
, 1,
477 (LPCITEMIDLIST
*)&lptvid
->lpi
, &dwAttributes
);
478 if (FAILED(r
) || !(dwAttributes
& (SFGAO_FILESYSANCESTOR
|SFGAO_FILESYSTEM
)))
481 if (lpBrowseInfo
->ulFlags
& BIF_RETURNONLYFSDIRS
)
483 dwAttributes
= SFGAO_FOLDER
| SFGAO_FILESYSTEM
;
484 r
= IShellFolder_GetAttributesOf(lptvid
->lpsfParent
, 1,
485 (LPCITEMIDLIST
*)&lptvid
->lpi
, &dwAttributes
);
487 ((dwAttributes
& (SFGAO_FOLDER
|SFGAO_FILESYSTEM
)) != (SFGAO_FOLDER
|SFGAO_FILESYSTEM
)))
492 SendMessageW(info
->hWnd
, BFFM_ENABLEOK
, 0, (LPARAM
)bEnabled
);
495 static LRESULT
BrsFolder_Treeview_Delete( browse_info
*info
, NMTREEVIEWW
*pnmtv
)
497 LPTV_ITEMDATA lptvid
= (LPTV_ITEMDATA
)pnmtv
->itemOld
.lParam
;
499 TRACE("TVN_DELETEITEMA/W %p\n", lptvid
);
501 IShellFolder_Release(lptvid
->lpsfParent
);
503 IEnumIDList_Release(lptvid
->pEnumIL
);
505 SHFree(lptvid
->lpifq
);
510 static LRESULT
BrsFolder_Treeview_Expand( browse_info
*info
, NMTREEVIEWW
*pnmtv
)
512 IShellFolder
*lpsf2
= NULL
;
513 LPTV_ITEMDATA lptvid
= (LPTV_ITEMDATA
) pnmtv
->itemNew
.lParam
;
516 TRACE("TVN_ITEMEXPANDINGA/W\n");
518 if ((pnmtv
->itemNew
.state
& TVIS_EXPANDEDONCE
))
521 if (lptvid
->lpi
&& lptvid
->lpi
->mkid
.cb
) {
522 r
= IShellFolder_BindToObject( lptvid
->lpsfParent
, lptvid
->lpi
, 0,
523 (REFIID
)&IID_IShellFolder
, (LPVOID
*)&lpsf2
);
525 lpsf2
= lptvid
->lpsfParent
;
526 r
= IShellFolder_AddRef(lpsf2
);
530 FillTreeView( info
, lpsf2
, lptvid
->lpifq
, pnmtv
->itemNew
.hItem
, lptvid
->pEnumIL
);
532 /* My Computer is already sorted and trying to do a simple text
533 * sort will only mess things up */
534 if (!_ILIsMyComputer(lptvid
->lpi
))
535 SendMessageW( info
->hwndTreeView
, TVM_SORTCHILDREN
,
536 FALSE
, (LPARAM
)pnmtv
->itemNew
.hItem
);
541 static HRESULT
BrsFolder_Treeview_Changed( browse_info
*info
, NMTREEVIEWW
*pnmtv
)
543 LPTV_ITEMDATA lptvid
= (LPTV_ITEMDATA
) pnmtv
->itemNew
.lParam
;
545 lptvid
= (LPTV_ITEMDATA
) pnmtv
->itemNew
.lParam
;
546 info
->pidlRet
= lptvid
->lpifq
;
547 browsefolder_callback( info
->lpBrowseInfo
, info
->hWnd
, BFFM_SELCHANGED
,
548 (LPARAM
)info
->pidlRet
);
549 BrsFolder_CheckValidSelection( info
, lptvid
);
553 static LRESULT
BrsFolder_OnNotify( browse_info
*info
, UINT CtlID
, LPNMHDR lpnmh
)
555 NMTREEVIEWW
*pnmtv
= (NMTREEVIEWW
*)lpnmh
;
557 TRACE("%p %x %p msg=%x\n", info
, CtlID
, lpnmh
, pnmtv
->hdr
.code
);
559 if (pnmtv
->hdr
.idFrom
!= IDD_TREEVIEW
)
562 switch (pnmtv
->hdr
.code
)
564 case TVN_DELETEITEMA
:
565 case TVN_DELETEITEMW
:
566 return BrsFolder_Treeview_Delete( info
, pnmtv
);
568 case TVN_ITEMEXPANDINGA
:
569 case TVN_ITEMEXPANDINGW
:
570 return BrsFolder_Treeview_Expand( info
, pnmtv
);
572 case TVN_SELCHANGEDA
:
573 case TVN_SELCHANGEDW
:
574 return BrsFolder_Treeview_Changed( info
, pnmtv
);
577 WARN("unhandled (%d)\n", pnmtv
->hdr
.code
);
585 static BOOL
BrsFolder_OnCreate( HWND hWnd
, browse_info
*info
)
587 LPBROWSEINFOW lpBrowseInfo
= info
->lpBrowseInfo
;
590 SetPropW( hWnd
, szBrowseFolderInfo
, info
);
592 if (lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
)
593 FIXME("flags BIF_NEWDIALOGSTYLE partially implemented\n");
594 if (lpBrowseInfo
->ulFlags
& ~SUPPORTEDFLAGS
)
595 FIXME("flags %x not implemented\n", lpBrowseInfo
->ulFlags
& ~SUPPORTEDFLAGS
);
597 if (lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
)
601 info
->layout
= LayoutInit(hWnd
, g_layout_info
, LAYOUT_INFO_COUNT
);
603 /* TODO: Windows allows shrinking the windows a bit */
604 GetWindowRect(hWnd
, &rcWnd
);
605 info
->szMin
.cx
= rcWnd
.right
- rcWnd
.left
;
606 info
->szMin
.cy
= rcWnd
.bottom
- rcWnd
.top
;
613 if (lpBrowseInfo
->lpszTitle
)
614 SetWindowTextW( GetDlgItem(hWnd
, IDD_TITLE
), lpBrowseInfo
->lpszTitle
);
616 ShowWindow( GetDlgItem(hWnd
, IDD_TITLE
), SW_HIDE
);
618 if (!(lpBrowseInfo
->ulFlags
& BIF_STATUSTEXT
)
619 || (lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
))
620 ShowWindow( GetDlgItem(hWnd
, IDD_STATUS
), SW_HIDE
);
622 /* Hide "Make New Folder" Button? */
623 if ((lpBrowseInfo
->ulFlags
& BIF_NONEWFOLDERBUTTON
)
624 || !(lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
))
625 ShowWindow( GetDlgItem(hWnd
, IDD_MAKENEWFOLDER
), SW_HIDE
);
627 /* Hide the editbox? */
628 if (!(lpBrowseInfo
->ulFlags
& BIF_EDITBOX
))
630 ShowWindow( GetDlgItem(hWnd
, IDD_FOLDER
), SW_HIDE
);
631 ShowWindow( GetDlgItem(hWnd
, IDD_FOLDERTEXT
), SW_HIDE
);
634 info
->hwndTreeView
= GetDlgItem( hWnd
, IDD_TREEVIEW
);
635 if (info
->hwndTreeView
)
637 InitializeTreeView( info
);
639 /* Resize the treeview if there's not editbox */
640 if ((lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
)
641 && !(lpBrowseInfo
->ulFlags
& BIF_EDITBOX
))
644 GetClientRect(info
->hwndTreeView
, &rc
);
645 SetWindowPos(info
->hwndTreeView
, HWND_TOP
, 0, 0,
646 rc
.right
, rc
.bottom
+ 40, SWP_NOMOVE
);
650 ERR("treeview control missing!\n");
652 browsefolder_callback( info
->lpBrowseInfo
, hWnd
, BFFM_INITIALIZED
, 0 );
657 static BOOL
BrsFolder_OnCommand( browse_info
*info
, UINT id
)
659 LPBROWSEINFOW lpBrowseInfo
= info
->lpBrowseInfo
;
664 /* The original pidl is owned by the treeview and will be free'd. */
665 info
->pidlRet
= ILClone(info
->pidlRet
);
666 if (info
->pidlRet
== NULL
) /* A null pidl would mean a cancel */
667 info
->pidlRet
= _ILCreateDesktop();
668 pdump( info
->pidlRet
);
669 if (lpBrowseInfo
->pszDisplayName
)
670 SHGetPathFromIDListW( info
->pidlRet
, lpBrowseInfo
->pszDisplayName
);
671 EndDialog( info
->hWnd
, 1 );
675 EndDialog( info
->hWnd
, 0 );
678 case IDD_MAKENEWFOLDER
:
679 FIXME("make new folder not implemented\n");
685 static BOOL
BrsFolder_OnSetExpanded(browse_info
*info
, LPVOID selection
,
686 BOOL is_str
, HTREEITEM
*pItem
)
688 LPITEMIDLIST pidlSelection
= (LPITEMIDLIST
)selection
;
689 LPCITEMIDLIST pidlCurrent
, pidlRoot
;
691 BOOL bResult
= FALSE
;
693 /* If 'selection' is a string, convert to a Shell ID List. */
695 IShellFolder
*psfDesktop
;
698 hr
= SHGetDesktopFolder(&psfDesktop
);
702 hr
= IShellFolder_ParseDisplayName(psfDesktop
, NULL
, NULL
,
703 (LPOLESTR
)selection
, NULL
, &pidlSelection
, NULL
);
704 IShellFolder_Release(psfDesktop
);
709 /* Move pidlCurrent behind the SHITEMIDs in pidlSelection, which are the root of
710 * the sub-tree currently displayed. */
711 pidlRoot
= info
->lpBrowseInfo
->pidlRoot
;
712 pidlCurrent
= pidlSelection
;
713 while (!_ILIsEmpty(pidlRoot
) && _ILIsEqualSimple(pidlRoot
, pidlCurrent
)) {
714 pidlRoot
= ILGetNext(pidlRoot
);
715 pidlCurrent
= ILGetNext(pidlCurrent
);
718 /* The given ID List is not part of the SHBrowseForFolder's current sub-tree. */
719 if (!_ILIsEmpty(pidlRoot
))
722 /* Initialize item to point to the first child of the root folder. */
723 memset(&item
, 0, sizeof(item
));
724 item
.mask
= TVIF_PARAM
;
725 item
.hItem
= TreeView_GetRoot(info
->hwndTreeView
);
727 item
.hItem
= TreeView_GetChild(info
->hwndTreeView
, item
.hItem
);
729 /* Walk the tree along the nodes corresponding to the remaining ITEMIDLIST */
730 while (item
.hItem
&& !_ILIsEmpty(pidlCurrent
)) {
731 LPTV_ITEMDATA pItemData
;
733 SendMessageW(info
->hwndTreeView
, TVM_GETITEMW
, 0, (LPARAM
)&item
);
734 pItemData
= (LPTV_ITEMDATA
)item
.lParam
;
736 if (_ILIsEqualSimple(pItemData
->lpi
, pidlCurrent
)) {
737 pidlCurrent
= ILGetNext(pidlCurrent
);
738 if (!_ILIsEmpty(pidlCurrent
)) {
739 /* Only expand current node and move on to it's first child,
740 * if we didn't already reach the last SHITEMID */
741 SendMessageW(info
->hwndTreeView
, TVM_EXPAND
, TVE_EXPAND
, (LPARAM
)item
.hItem
);
742 item
.hItem
= TreeView_GetChild(info
->hwndTreeView
, item
.hItem
);
745 item
.hItem
= TreeView_GetNextSibling(info
->hwndTreeView
, item
.hItem
);
749 if (_ILIsEmpty(pidlCurrent
) && item
.hItem
)
753 if (pidlSelection
&& pidlSelection
!= (LPITEMIDLIST
)selection
)
754 ILFree(pidlSelection
);
762 static BOOL
BrsFolder_OnSetSelectionW(browse_info
*info
, LPVOID selection
, BOOL is_str
) {
766 bResult
= BrsFolder_OnSetExpanded(info
, selection
, is_str
, &hItem
);
768 SendMessageW(info
->hwndTreeView
, TVM_SELECTITEM
, TVGN_CARET
, (LPARAM
)hItem
);
772 static BOOL
BrsFolder_OnSetSelectionA(browse_info
*info
, LPVOID selection
, BOOL is_str
) {
773 LPWSTR selectionW
= NULL
;
778 return BrsFolder_OnSetSelectionW(info
, selection
, is_str
);
780 if ((length
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)selection
, -1, NULL
, 0)) &&
781 (selectionW
= HeapAlloc(GetProcessHeap(), 0, length
* sizeof(WCHAR
))) &&
782 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)selection
, -1, selectionW
, length
))
784 result
= BrsFolder_OnSetSelectionW(info
, selectionW
, is_str
);
787 HeapFree(GetProcessHeap(), 0, selectionW
);
791 static BOOL
BrsFolder_OnWindowPosChanging(browse_info
*info
, WINDOWPOS
*pos
)
793 if ((info
->lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
) && !(pos
->flags
& SWP_NOSIZE
))
795 if (pos
->cx
< info
->szMin
.cx
)
796 pos
->cx
= info
->szMin
.cx
;
797 if (pos
->cy
< info
->szMin
.cy
)
798 pos
->cy
= info
->szMin
.cy
;
803 /*************************************************************************
804 * BrsFolderDlgProc32 (not an exported API function)
806 static INT_PTR CALLBACK
BrsFolderDlgProc( HWND hWnd
, UINT msg
, WPARAM wParam
,
811 TRACE("hwnd=%p msg=%04x 0x%08lx 0x%08lx\n", hWnd
, msg
, wParam
, lParam
);
813 if (msg
== WM_INITDIALOG
)
814 return BrsFolder_OnCreate( hWnd
, (browse_info
*) lParam
);
816 info
= GetPropW( hWnd
, szBrowseFolderInfo
);
821 return BrsFolder_OnNotify( info
, (UINT
)wParam
, (LPNMHDR
)lParam
);
824 return BrsFolder_OnCommand( info
, wParam
);
826 case WM_WINDOWPOSCHANGING
:
827 return BrsFolder_OnWindowPosChanging( info
, (WINDOWPOS
*)lParam
);
830 if (info
->layout
) /* new style dialogs */
831 LayoutUpdate(hWnd
, info
->layout
, g_layout_info
, LAYOUT_INFO_COUNT
);
834 case BFFM_SETSTATUSTEXTA
:
835 TRACE("Set status %s\n", debugstr_a((LPSTR
)lParam
));
836 SetWindowTextA(GetDlgItem(hWnd
, IDD_STATUS
), (LPSTR
)lParam
);
839 case BFFM_SETSTATUSTEXTW
:
840 TRACE("Set status %s\n", debugstr_w((LPWSTR
)lParam
));
841 SetWindowTextW(GetDlgItem(hWnd
, IDD_STATUS
), (LPWSTR
)lParam
);
845 TRACE("Enable %ld\n", lParam
);
846 EnableWindow(GetDlgItem(hWnd
, 1), (lParam
)?TRUE
:FALSE
);
849 case BFFM_SETOKTEXT
: /* unicode only */
850 TRACE("Set OK text %s\n", debugstr_w((LPWSTR
)wParam
));
851 SetWindowTextW(GetDlgItem(hWnd
, 1), (LPWSTR
)wParam
);
854 case BFFM_SETSELECTIONA
:
855 return BrsFolder_OnSetSelectionA(info
, (LPVOID
)lParam
, (BOOL
)wParam
);
857 case BFFM_SETSELECTIONW
:
858 return BrsFolder_OnSetSelectionW(info
, (LPVOID
)lParam
, (BOOL
)wParam
);
860 case BFFM_SETEXPANDED
: /* unicode only */
861 return BrsFolder_OnSetExpanded(info
, (LPVOID
)lParam
, (BOOL
)wParam
, NULL
);
866 static const WCHAR swBrowseTemplateName
[] = {
867 'S','H','B','R','S','F','O','R','F','O','L','D','E','R','_','M','S','G','B','O','X',0};
868 static const WCHAR swNewBrowseTemplateName
[] = {
869 'S','H','N','E','W','B','R','S','F','O','R','F','O','L','D','E','R','_','M','S','G','B','O','X',0};
871 /*************************************************************************
872 * SHBrowseForFolderA [SHELL32.@]
873 * SHBrowseForFolder [SHELL32.@]
875 LPITEMIDLIST WINAPI
SHBrowseForFolderA (LPBROWSEINFOA lpbi
)
884 bi
.hwndOwner
= lpbi
->hwndOwner
;
885 bi
.pidlRoot
= lpbi
->pidlRoot
;
886 if (lpbi
->pszDisplayName
)
888 bi
.pszDisplayName
= HeapAlloc( GetProcessHeap(), 0, MAX_PATH
* sizeof(WCHAR
) );
889 MultiByteToWideChar( CP_ACP
, 0, lpbi
->pszDisplayName
, -1, bi
.pszDisplayName
, MAX_PATH
);
892 bi
.pszDisplayName
= NULL
;
896 len
= MultiByteToWideChar( CP_ACP
, 0, lpbi
->lpszTitle
, -1, NULL
, 0 );
897 title
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
898 MultiByteToWideChar( CP_ACP
, 0, lpbi
->lpszTitle
, -1, title
, len
);
903 bi
.lpszTitle
= title
;
904 bi
.ulFlags
= lpbi
->ulFlags
;
905 bi
.lpfn
= lpbi
->lpfn
;
906 bi
.lParam
= lpbi
->lParam
;
907 bi
.iImage
= lpbi
->iImage
;
908 lpid
= SHBrowseForFolderW( &bi
);
909 if (bi
.pszDisplayName
)
911 WideCharToMultiByte( CP_ACP
, 0, bi
.pszDisplayName
, -1,
912 lpbi
->pszDisplayName
, MAX_PATH
, 0, NULL
);
913 HeapFree( GetProcessHeap(), 0, bi
.pszDisplayName
);
915 HeapFree(GetProcessHeap(), 0, title
);
916 lpbi
->iImage
= bi
.iImage
;
921 /*************************************************************************
922 * SHBrowseForFolderW [SHELL32.@]
925 * crashes when passed a null pointer
927 LPITEMIDLIST WINAPI
SHBrowseForFolderW (LPBROWSEINFOW lpbi
)
932 const WCHAR
* templateName
;
936 info
.lpBrowseInfo
= lpbi
;
937 info
.hwndTreeView
= NULL
;
939 hr
= OleInitialize(NULL
);
941 if (lpbi
->ulFlags
& BIF_NEWDIALOGSTYLE
)
942 templateName
= swNewBrowseTemplateName
;
944 templateName
= swBrowseTemplateName
;
945 r
= DialogBoxParamW( shell32_hInstance
, templateName
, lpbi
->hwndOwner
,
946 BrsFolderDlgProc
, (LPARAM
)&info
);