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
28 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
30 /* original margins and control size */
31 typedef struct tagLAYOUT_DATA
33 LONG left
, width
, right
;
34 LONG top
, height
, bottom
;
37 typedef struct tagbrowse_info
41 LPBROWSEINFOW lpBrowseInfo
;
43 LAYOUT_DATA
*layout
; /* filled by LayoutInit, used by LayoutUpdate */
47 typedef struct tagTV_ITEMDATA
49 IShellFolder
* lpsfParent
; /* IShellFolder of the parent */
50 LPITEMIDLIST lpi
; /* PIDL relative to parent */
51 LPITEMIDLIST lpifq
; /* Fully qualified PIDL */
52 IEnumIDList
* pEnumIL
; /* Children iterator */
53 } TV_ITEMDATA
, *LPTV_ITEMDATA
;
55 typedef struct tagLAYOUT_INFO
57 int iItemId
; /* control id */
58 DWORD dwAnchor
; /* BF_* flags specifying which margins should remain constant */
61 static const LAYOUT_INFO g_layout_info
[] =
63 {IDC_BROWSE_FOR_FOLDER_TITLE
, BF_TOP
|BF_LEFT
|BF_RIGHT
},
64 {IDC_BROWSE_FOR_FOLDER_STATUS
, BF_TOP
|BF_LEFT
|BF_RIGHT
},
65 {IDC_BROWSE_FOR_FOLDER_FOLDER
, BF_TOP
|BF_LEFT
|BF_RIGHT
},
66 {IDC_BROWSE_FOR_FOLDER_TREEVIEW
, BF_TOP
|BF_BOTTOM
|BF_LEFT
|BF_RIGHT
},
67 {IDC_BROWSE_FOR_FOLDER_FOLDER
, BF_BOTTOM
|BF_LEFT
},
68 {IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT
, BF_BOTTOM
|BF_LEFT
|BF_RIGHT
},
69 {IDC_BROWSE_FOR_FOLDER_NEW_FOLDER
, BF_BOTTOM
|BF_LEFT
},
70 {IDOK
, BF_BOTTOM
|BF_RIGHT
},
71 {IDCANCEL
, BF_BOTTOM
|BF_RIGHT
}
74 #define LAYOUT_INFO_COUNT (sizeof(g_layout_info)/sizeof(g_layout_info[0]))
76 #define SUPPORTEDFLAGS (BIF_STATUSTEXT | \
77 BIF_BROWSEFORCOMPUTER | \
78 BIF_RETURNFSANCESTORS | \
79 BIF_RETURNONLYFSDIRS | \
80 BIF_NONEWFOLDERBUTTON | \
81 BIF_NEWDIALOGSTYLE | \
82 BIF_BROWSEINCLUDEFILES)
84 static void FillTreeView(browse_info
*, IShellFolder
*,
85 LPITEMIDLIST
, HTREEITEM
, IEnumIDList
*);
86 static HTREEITEM
InsertTreeViewItem( browse_info
*, IShellFolder
*,
87 LPCITEMIDLIST
, LPCITEMIDLIST
, IEnumIDList
*, HTREEITEM
);
89 static const WCHAR szBrowseFolderInfo
[] = L
"__WINE_BRSFOLDERDLG_INFO";
91 static DWORD __inline
BrowseFlagsToSHCONTF(UINT ulFlags
)
93 return SHCONTF_FOLDERS
| (ulFlags
& BIF_BROWSEINCLUDEFILES
? SHCONTF_NONFOLDERS
: 0);
96 static void browsefolder_callback( LPBROWSEINFOW lpBrowseInfo
, HWND hWnd
,
97 UINT msg
, LPARAM param
)
99 if (!lpBrowseInfo
->lpfn
)
101 lpBrowseInfo
->lpfn( hWnd
, msg
, param
, lpBrowseInfo
->lParam
);
104 static LAYOUT_DATA
*LayoutInit(HWND hwnd
, const LAYOUT_INFO
*layout_info
, int layout_count
)
110 GetClientRect(hwnd
, &rcWnd
);
111 data
= (LAYOUT_DATA
*)SHAlloc(sizeof(LAYOUT_DATA
)*layout_count
);
112 for (i
= 0; i
< layout_count
; i
++)
115 HWND hItem
= GetDlgItem(hwnd
, layout_info
[i
].iItemId
);
118 ERR("Item %d not found\n", i
);
119 GetWindowRect(hItem
, &r
);
120 MapWindowPoints(HWND_DESKTOP
, hwnd
, (LPPOINT
)&r
, 2);
122 data
[i
].left
= r
.left
;
123 data
[i
].right
= rcWnd
.right
- r
.right
;
124 data
[i
].width
= r
.right
- r
.left
;
127 data
[i
].bottom
= rcWnd
.bottom
- r
.bottom
;
128 data
[i
].height
= r
.bottom
- r
.top
;
133 static void LayoutUpdate(HWND hwnd
, LAYOUT_DATA
*data
, const LAYOUT_INFO
*layout_info
, int layout_count
)
138 GetClientRect(hwnd
, &rcWnd
);
139 for (i
= 0; i
< layout_count
; i
++)
142 HWND hItem
= GetDlgItem(hwnd
, layout_info
[i
].iItemId
);
144 GetWindowRect(hItem
, &r
);
145 MapWindowPoints(HWND_DESKTOP
, hwnd
, (LPPOINT
)&r
, 2);
147 if (layout_info
[i
].dwAnchor
& BF_RIGHT
)
149 r
.right
= rcWnd
.right
- data
[i
].right
;
150 if (!(layout_info
[i
].dwAnchor
& BF_LEFT
))
151 r
.left
= r
.right
- data
[i
].width
;
154 if (layout_info
[i
].dwAnchor
& BF_BOTTOM
)
156 r
.bottom
= rcWnd
.bottom
- data
[i
].bottom
;
157 if (!(layout_info
[i
].dwAnchor
& BF_TOP
))
158 r
.top
= r
.bottom
- data
[i
].height
;
161 SetWindowPos(hItem
, NULL
, r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
, SWP_NOZORDER
);
166 /******************************************************************************
167 * InitializeTreeView [Internal]
169 * Called from WM_INITDIALOG handler.
172 * hwndParent [I] The BrowseForFolder dialog
173 * root [I] ITEMIDLIST of the root shell folder
175 static void InitializeTreeView( browse_info
*info
)
177 LPITEMIDLIST pidlParent
, pidlChild
;
178 HIMAGELIST hImageList
;
180 CComPtr
<IShellFolder
> lpsfParent
;
181 CComPtr
<IShellFolder
> lpsfRoot
;
182 CComPtr
<IEnumIDList
> pEnumChildren
;
185 LPCITEMIDLIST root
= info
->lpBrowseInfo
->pidlRoot
;
187 TRACE("%p\n", info
);
189 Shell_GetImageLists(NULL
, &hImageList
);
192 SendMessageW( info
->hwndTreeView
, TVM_SETIMAGELIST
, 0, (LPARAM
)hImageList
);
194 /* We want to call InsertTreeViewItem down the code, in order to insert
195 * the root item of the treeview. Due to InsertTreeViewItem's signature,
196 * we need the following to do this:
198 * + An ITEMIDLIST corresponding to _the parent_ of root.
199 * + An ITEMIDLIST, which is a relative path from root's parent to root
200 * (containing a single SHITEMID).
201 * + An IShellFolder interface pointer of root's parent folder.
203 * If root is 'Desktop', then root's parent is also 'Desktop'.
206 pidlParent
= ILClone(root
);
207 ILRemoveLastID(pidlParent
);
208 pidlChild
= ILClone(ILFindLastID(root
));
210 if (_ILIsDesktop(pidlParent
))
212 hr
= SHGetDesktopFolder(&lpsfParent
);
214 CComPtr
<IShellFolder
> lpsfDesktop
;
215 hr
= SHGetDesktopFolder(&lpsfDesktop
);
217 WARN("SHGetDesktopFolder failed! hr = %08x\n", hr
);
222 hr
= lpsfDesktop
->BindToObject(pidlParent
, 0, IID_PPV_ARG(IShellFolder
, &lpsfParent
));
226 WARN("Could not bind to parent shell folder! hr = %08x\n", hr
);
232 if (pidlChild
&& pidlChild
->mkid
.cb
) {
233 hr
= lpsfParent
->BindToObject(pidlChild
, 0, IID_PPV_ARG(IShellFolder
, &lpsfRoot
));
235 lpsfRoot
= lpsfParent
;
239 WARN("Could not bind to root shell folder! hr = %08x\n", hr
);
245 flags
= BrowseFlagsToSHCONTF( info
->lpBrowseInfo
->ulFlags
);
246 hr
= lpsfRoot
->EnumObjects(info
->hWnd
, flags
, &pEnumChildren
);
248 WARN("Could not get child iterator! hr = %08x\n", hr
);
254 SendMessageW( info
->hwndTreeView
, TVM_DELETEITEM
, 0, (LPARAM
)TVI_ROOT
);
255 item
= InsertTreeViewItem( info
, lpsfParent
, pidlChild
,
256 pidlParent
, pEnumChildren
, TVI_ROOT
);
257 SendMessageW( info
->hwndTreeView
, TVM_EXPAND
, TVE_EXPAND
, (LPARAM
)item
);
263 static int GetIcon(LPCITEMIDLIST lpi
, UINT uFlags
)
266 SHGetFileInfoW((LPCWSTR
)lpi
, 0 ,&sfi
, sizeof(SHFILEINFOW
), uFlags
);
270 static void GetNormalAndSelectedIcons(LPITEMIDLIST lpifq
, LPTVITEMW lpTV_ITEM
)
272 LPITEMIDLIST pidlDesktop
= NULL
;
275 TRACE("%p %p\n",lpifq
, lpTV_ITEM
);
279 pidlDesktop
= _ILCreateDesktop();
283 flags
= SHGFI_PIDL
| SHGFI_SYSICONINDEX
| SHGFI_SMALLICON
;
284 lpTV_ITEM
->iImage
= GetIcon( lpifq
, flags
);
286 flags
= SHGFI_PIDL
| SHGFI_SYSICONINDEX
| SHGFI_SMALLICON
| SHGFI_OPENICON
;
287 lpTV_ITEM
->iSelectedImage
= GetIcon( lpifq
, flags
);
290 ILFree( pidlDesktop
);
293 /******************************************************************************
296 * Query a shell folder for the display name of one of it's children
299 * lpsf [I] IShellFolder interface of the folder to be queried.
300 * lpi [I] ITEMIDLIST of the child, relative to parent
301 * dwFlags [I] as in IShellFolder::GetDisplayNameOf
302 * lpFriendlyName [O] The desired display name in unicode
308 static BOOL
GetName(IShellFolder
* lpsf
, LPCITEMIDLIST lpi
, DWORD dwFlags
, LPWSTR lpFriendlyName
)
313 TRACE("%p %p %x %p\n", lpsf
, lpi
, dwFlags
, lpFriendlyName
);
314 if (SUCCEEDED(lpsf
->GetDisplayNameOf(lpi
, dwFlags
, &str
)))
315 bSuccess
= StrRetToStrNW(lpFriendlyName
, MAX_PATH
, &str
, lpi
);
319 TRACE("-- %s\n", debugstr_w(lpFriendlyName
));
323 /******************************************************************************
324 * InsertTreeViewItem [Internal]
327 * info [I] data for the dialog
328 * lpsf [I] IShellFolder interface of the item's parent shell folder
329 * pidl [I] ITEMIDLIST of the child to insert, relative to parent
330 * pidlParent [I] ITEMIDLIST of the parent shell folder
331 * pEnumIL [I] Iterator for the children of the item to be inserted
332 * hParent [I] The treeview-item that represents the parent shell folder
335 * Success: Handle to the created and inserted treeview-item
338 static HTREEITEM
InsertTreeViewItem( browse_info
*info
, IShellFolder
* lpsf
,
339 LPCITEMIDLIST pidl
, LPCITEMIDLIST pidlParent
, IEnumIDList
* pEnumIL
,
343 TVINSERTSTRUCTW tvins
;
344 WCHAR szBuff
[MAX_PATH
];
345 LPTV_ITEMDATA lptvid
=0;
347 tvi
.mask
= TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_PARAM
;
349 tvi
.cChildren
= pEnumIL
? 1 : 0;
350 tvi
.mask
|= TVIF_CHILDREN
;
352 lptvid
= (TV_ITEMDATA
*)SHAlloc( sizeof(TV_ITEMDATA
) );
356 if (!GetName(lpsf
, pidl
, SHGDN_NORMAL
, szBuff
))
359 tvi
.pszText
= szBuff
;
360 tvi
.cchTextMax
= MAX_PATH
;
361 tvi
.lParam
= (LPARAM
)lptvid
;
365 lptvid
->lpsfParent
= lpsf
;
366 lptvid
->lpi
= ILClone(pidl
);
367 lptvid
->lpifq
= pidlParent
? ILCombine(pidlParent
, pidl
) : ILClone(pidl
);
368 lptvid
->pEnumIL
= pEnumIL
;
369 GetNormalAndSelectedIcons(lptvid
->lpifq
, &tvi
);
372 tvins
.hInsertAfter
= NULL
;
373 tvins
.hParent
= hParent
;
375 return TreeView_InsertItem( info
->hwndTreeView
, &tvins
);
378 /******************************************************************************
379 * FillTreeView [Internal]
381 * For each child (given by lpe) of the parent shell folder, which is given by
382 * lpsf and whose PIDL is pidl, insert a treeview-item right under hParent
385 * info [I] data for the dialog
386 * lpsf [I] IShellFolder interface of the parent shell folder
387 * pidl [I] ITEMIDLIST of the parent shell folder
388 * hParent [I] The treeview item that represents the parent shell folder
389 * lpe [I] An iterator for the children of the parent shell folder
391 static void FillTreeView( browse_info
*info
, IShellFolder
* lpsf
,
392 LPITEMIDLIST pidl
, HTREEITEM hParent
, IEnumIDList
* lpe
)
394 LPITEMIDLIST pidlTemp
= 0;
397 HWND hwnd
= GetParent( info
->hwndTreeView
);
399 TRACE("%p %p %p %p\n",lpsf
, pidl
, hParent
, lpe
);
401 /* No IEnumIDList -> No children */
405 SetCursor( LoadCursorA( 0, (LPSTR
)IDC_WAIT
) );
407 while (S_OK
== lpe
->Next(1,&pidlTemp
,&ulFetched
))
409 ULONG ulAttrs
= SFGAO_HASSUBFOLDER
| SFGAO_FOLDER
;
410 CComPtr
<IEnumIDList
> pEnumIL
;
411 CComPtr
<IShellFolder
> pSFChild
;
412 lpsf
->GetAttributesOf(1, (LPCITEMIDLIST
*)&pidlTemp
, &ulAttrs
);
413 if (ulAttrs
& SFGAO_FOLDER
)
415 hr
= lpsf
->BindToObject(pidlTemp
, NULL
, IID_PPV_ARG(IShellFolder
, &pSFChild
));
418 DWORD flags
= BrowseFlagsToSHCONTF(info
->lpBrowseInfo
->ulFlags
);
419 hr
= pSFChild
->EnumObjects(hwnd
, flags
, &pEnumIL
);
422 if ((pEnumIL
->Skip(1) != S_OK
) ||
423 FAILED(pEnumIL
->Reset()))
431 if (!InsertTreeViewItem(info
, lpsf
, pidlTemp
, pidl
, pEnumIL
, hParent
))
433 SHFree(pidlTemp
); /* Finally, free the pidl that the shell gave us... */
439 SetCursor(LoadCursorW(0, (LPWSTR
)IDC_ARROW
));
443 static BOOL __inline
PIDLIsType(LPCITEMIDLIST pidl
, PIDLTYPE type
)
445 LPPIDLDATA data
= _ILGetDataPointer(pidl
);
448 return (data
->type
== type
);
451 static void BrsFolder_CheckValidSelection( browse_info
*info
, LPTV_ITEMDATA lptvid
)
453 LPBROWSEINFOW lpBrowseInfo
= info
->lpBrowseInfo
;
454 LPCITEMIDLIST pidl
= lptvid
->lpi
;
455 BOOL bEnabled
= TRUE
;
459 if ((lpBrowseInfo
->ulFlags
& BIF_BROWSEFORCOMPUTER
) &&
460 !PIDLIsType(pidl
, PT_COMP
))
462 if (lpBrowseInfo
->ulFlags
& BIF_RETURNFSANCESTORS
)
464 dwAttributes
= SFGAO_FILESYSANCESTOR
| SFGAO_FILESYSTEM
;
465 r
= lptvid
->lpsfParent
->GetAttributesOf(1,
466 (LPCITEMIDLIST
*)&lptvid
->lpi
, &dwAttributes
);
467 if (FAILED(r
) || !(dwAttributes
& (SFGAO_FILESYSANCESTOR
|SFGAO_FILESYSTEM
)))
471 dwAttributes
= SFGAO_FOLDER
| SFGAO_FILESYSTEM
;
472 r
= lptvid
->lpsfParent
->GetAttributesOf(1,
473 (LPCITEMIDLIST
*)&lptvid
->lpi
,
476 ((dwAttributes
& (SFGAO_FOLDER
|SFGAO_FILESYSTEM
)) != (SFGAO_FOLDER
|SFGAO_FILESYSTEM
)))
478 if (lpBrowseInfo
->ulFlags
& BIF_RETURNONLYFSDIRS
)
480 EnableWindow(GetDlgItem(info
->hWnd
, IDC_BROWSE_FOR_FOLDER_NEW_FOLDER
), FALSE
);
483 EnableWindow(GetDlgItem(info
->hWnd
, IDC_BROWSE_FOR_FOLDER_NEW_FOLDER
), TRUE
);
485 SendMessageW(info
->hWnd
, BFFM_ENABLEOK
, 0, bEnabled
);
488 static LRESULT
BrsFolder_Treeview_Delete( browse_info
*info
, NMTREEVIEWW
*pnmtv
)
490 LPTV_ITEMDATA lptvid
= (LPTV_ITEMDATA
)pnmtv
->itemOld
.lParam
;
492 TRACE("TVN_DELETEITEMA/W %p\n", lptvid
);
494 lptvid
->lpsfParent
->Release();
496 lptvid
->pEnumIL
->Release();
498 SHFree(lptvid
->lpifq
);
503 static LRESULT
BrsFolder_Treeview_Expand( browse_info
*info
, NMTREEVIEWW
*pnmtv
)
505 CComPtr
<IShellFolder
> lpsf2
;
506 LPTV_ITEMDATA lptvid
= (LPTV_ITEMDATA
) pnmtv
->itemNew
.lParam
;
509 TRACE("TVN_ITEMEXPANDINGA/W\n");
511 if ((pnmtv
->itemNew
.state
& TVIS_EXPANDEDONCE
))
514 if (lptvid
->lpi
&& lptvid
->lpi
->mkid
.cb
) {
515 r
= lptvid
->lpsfParent
->BindToObject(lptvid
->lpi
, 0, IID_PPV_ARG(IShellFolder
, &lpsf2
));
517 lpsf2
= lptvid
->lpsfParent
;
523 FillTreeView( info
, lpsf2
, lptvid
->lpifq
, pnmtv
->itemNew
.hItem
, lptvid
->pEnumIL
);
527 /* My Computer is already sorted and trying to do a simple text
528 * sort will only mess things up */
529 if (!_ILIsMyComputer(lptvid
->lpi
))
530 SendMessageW( info
->hwndTreeView
, TVM_SORTCHILDREN
,
531 FALSE
, (LPARAM
)pnmtv
->itemNew
.hItem
);
536 static HRESULT
BrsFolder_Treeview_Changed( browse_info
*info
, NMTREEVIEWW
*pnmtv
)
538 LPTV_ITEMDATA lptvid
= (LPTV_ITEMDATA
) pnmtv
->itemNew
.lParam
;
539 WCHAR name
[MAX_PATH
];
541 ILFree(info
->pidlRet
);
542 info
->pidlRet
= ILClone(lptvid
->lpifq
);
544 if (GetName(lptvid
->lpsfParent
, lptvid
->lpi
, SHGDN_NORMAL
, name
))
545 SetWindowTextW( GetDlgItem(info
->hWnd
, IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT
), name
);
547 browsefolder_callback( info
->lpBrowseInfo
, info
->hWnd
, BFFM_SELCHANGED
,
548 (LPARAM
)info
->pidlRet
);
549 BrsFolder_CheckValidSelection( info
, lptvid
);
553 static LRESULT
BrsFolder_Treeview_Rename(browse_info
*info
, NMTVDISPINFOW
*pnmtv
)
555 LPTV_ITEMDATA item_data
;
556 WCHAR old_path
[MAX_PATH
], new_path
[MAX_PATH
], *p
;
560 if(!pnmtv
->item
.pszText
)
563 item
.mask
= TVIF_HANDLE
|TVIF_PARAM
;
564 item
.hItem
= (HTREEITEM
)SendMessageW(info
->hwndTreeView
, TVM_GETNEXTITEM
, TVGN_CARET
, 0);
565 SendMessageW(info
->hwndTreeView
, TVM_GETITEMW
, 0, (LPARAM
)&item
);
566 item_data
= (LPTV_ITEMDATA
)item
.lParam
;
568 SHGetPathFromIDListW(item_data
->lpifq
, old_path
);
569 if(!(p
= strrchrW(old_path
, '\\')))
571 p
= new_path
+(p
-old_path
+1);
572 memcpy(new_path
, old_path
, (p
-new_path
)*sizeof(WCHAR
));
573 strcpyW(p
, pnmtv
->item
.pszText
);
575 if(!MoveFileW(old_path
, new_path
))
578 SHFree(item_data
->lpifq
);
579 SHFree(item_data
->lpi
);
580 item_data
->lpifq
= SHSimpleIDListFromPathW(new_path
);
581 item_data
->lpsfParent
->ParseDisplayName(NULL
, NULL
, pnmtv
->item
.pszText
,
582 NULL
, &item_data
->lpi
, NULL
);
584 item
.mask
= TVIF_HANDLE
|TVIF_TEXT
;
585 item
.pszText
= pnmtv
->item
.pszText
;
586 SendMessageW(info
->hwndTreeView
, TVM_SETITEMW
, 0, (LPARAM
)&item
);
588 nmtv
.itemNew
.lParam
= item
.lParam
;
589 BrsFolder_Treeview_Changed(info
, &nmtv
);
593 static LRESULT
BrsFolder_OnNotify( browse_info
*info
, UINT CtlID
, LPNMHDR lpnmh
)
595 NMTREEVIEWW
*pnmtv
= (NMTREEVIEWW
*)lpnmh
;
597 TRACE("%p %x %p msg=%x\n", info
, CtlID
, lpnmh
, pnmtv
->hdr
.code
);
599 if (pnmtv
->hdr
.idFrom
!= IDC_BROWSE_FOR_FOLDER_TREEVIEW
)
602 switch (pnmtv
->hdr
.code
)
604 case TVN_DELETEITEMA
:
605 case TVN_DELETEITEMW
:
606 return BrsFolder_Treeview_Delete( info
, pnmtv
);
608 case TVN_ITEMEXPANDINGA
:
609 case TVN_ITEMEXPANDINGW
:
610 return BrsFolder_Treeview_Expand( info
, pnmtv
);
612 case TVN_SELCHANGEDA
:
613 case TVN_SELCHANGEDW
:
614 return BrsFolder_Treeview_Changed( info
, pnmtv
);
616 case TVN_ENDLABELEDITA
:
617 case TVN_ENDLABELEDITW
:
618 return BrsFolder_Treeview_Rename( info
, (LPNMTVDISPINFOW
)pnmtv
);
621 WARN("unhandled (%d)\n", pnmtv
->hdr
.code
);
629 static BOOL
BrsFolder_OnCreate( HWND hWnd
, browse_info
*info
)
631 LPBROWSEINFOW lpBrowseInfo
= info
->lpBrowseInfo
;
634 SetPropW( hWnd
, szBrowseFolderInfo
, info
);
636 if (lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
)
637 FIXME("flags BIF_NEWDIALOGSTYLE partially implemented\n");
638 if (lpBrowseInfo
->ulFlags
& ~SUPPORTEDFLAGS
)
639 FIXME("flags %x not implemented\n", lpBrowseInfo
->ulFlags
& ~SUPPORTEDFLAGS
);
641 if (lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
)
645 info
->layout
= LayoutInit(hWnd
, g_layout_info
, LAYOUT_INFO_COUNT
);
647 /* TODO: Windows allows shrinking the windows a bit */
648 GetWindowRect(hWnd
, &rcWnd
);
649 info
->szMin
.cx
= rcWnd
.right
- rcWnd
.left
;
650 info
->szMin
.cy
= rcWnd
.bottom
- rcWnd
.top
;
657 if (lpBrowseInfo
->lpszTitle
)
658 SetWindowTextW( GetDlgItem(hWnd
, IDC_BROWSE_FOR_FOLDER_TITLE
), lpBrowseInfo
->lpszTitle
);
660 ShowWindow( GetDlgItem(hWnd
, IDC_BROWSE_FOR_FOLDER_TITLE
), SW_HIDE
);
662 if (!(lpBrowseInfo
->ulFlags
& BIF_STATUSTEXT
)
663 || (lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
))
664 ShowWindow( GetDlgItem(hWnd
, IDC_BROWSE_FOR_FOLDER_STATUS
), SW_HIDE
);
666 /* Hide "Make New Folder" Button? */
667 if ((lpBrowseInfo
->ulFlags
& BIF_NONEWFOLDERBUTTON
)
668 || !(lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
))
669 ShowWindow( GetDlgItem(hWnd
, IDC_BROWSE_FOR_FOLDER_NEW_FOLDER
), SW_HIDE
);
671 /* Hide the editbox? */
672 if (!(lpBrowseInfo
->ulFlags
& BIF_EDITBOX
))
674 ShowWindow( GetDlgItem(hWnd
, IDC_BROWSE_FOR_FOLDER_FOLDER
), SW_HIDE
);
675 ShowWindow( GetDlgItem(hWnd
, IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT
), SW_HIDE
);
678 info
->hwndTreeView
= GetDlgItem( hWnd
, IDC_BROWSE_FOR_FOLDER_TREEVIEW
);
679 if (info
->hwndTreeView
)
681 InitializeTreeView( info
);
683 /* Resize the treeview if there's not editbox */
684 if ((lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
)
685 && !(lpBrowseInfo
->ulFlags
& BIF_EDITBOX
))
688 GetClientRect(info
->hwndTreeView
, &rc
);
689 SetWindowPos(info
->hwndTreeView
, HWND_TOP
, 0, 0,
690 rc
.right
, rc
.bottom
+ 40, SWP_NOMOVE
);
694 ERR("treeview control missing!\n");
696 browsefolder_callback( info
->lpBrowseInfo
, hWnd
, BFFM_INITIALIZED
, 0 );
701 static HRESULT
BrsFolder_Rename(browse_info
*info
, HTREEITEM rename
)
703 SendMessageW(info
->hwndTreeView
, TVM_SELECTITEM
, TVGN_CARET
, (LPARAM
)rename
);
704 SendMessageW(info
->hwndTreeView
, TVM_EDITLABELW
, 0, (LPARAM
)rename
);
708 static HRESULT
BrsFolder_NewFolder(browse_info
*info
)
710 DWORD flags
= BrowseFlagsToSHCONTF(info
->lpBrowseInfo
->ulFlags
);
711 CComPtr
<IShellFolder
> desktop
;
712 CComPtr
<IShellFolder
> cur
;
713 CComPtr
<ISFHelper
> sfhelper
;
714 WCHAR name
[MAX_PATH
];
715 HTREEITEM parent
, added
;
716 LPTV_ITEMDATA item_data
;
717 LPITEMIDLIST new_item
;
723 ERR("Make new folder button should be disabled\n");
727 /* Create new directory */
728 hr
= SHGetDesktopFolder(&desktop
);
731 hr
= desktop
->BindToObject(info
->pidlRet
, 0, IID_PPV_ARG(IShellFolder
, &cur
));
735 hr
= cur
->QueryInterface(IID_PPV_ARG(ISFHelper
, &sfhelper
));
739 hr
= SHGetPathFromIDListW(info
->pidlRet
, name
);
746 hr
= sfhelper
->GetUniqueName(&name
[len
], MAX_PATH
-len
);
751 if(!CreateDirectoryW(name
, NULL
))
754 /* Update parent of newly created directory */
755 parent
= (HTREEITEM
)SendMessageW(info
->hwndTreeView
, TVM_GETNEXTITEM
, TVGN_CARET
, 0);
759 SendMessageW(info
->hwndTreeView
, TVM_EXPAND
, TVE_EXPAND
, (LPARAM
)parent
);
761 memset(&item
, 0, sizeof(TVITEMW
));
762 item
.mask
= TVIF_PARAM
|TVIF_STATE
;
764 SendMessageW(info
->hwndTreeView
, TVM_GETITEMW
, 0, (LPARAM
)&item
);
765 item_data
= (LPTV_ITEMDATA
)item
.lParam
;
769 if(item_data
->pEnumIL
)
770 item_data
->pEnumIL
->Release();
771 hr
= cur
->EnumObjects(info
->hwndTreeView
, flags
, &item_data
->pEnumIL
);
775 /* Update treeview */
776 if(!(item
.state
&TVIS_EXPANDEDONCE
)) {
777 item
.mask
= TVIF_STATE
;
778 item
.state
= TVIS_EXPANDEDONCE
;
779 item
.stateMask
= TVIS_EXPANDEDONCE
;
780 SendMessageW(info
->hwndTreeView
, TVM_SETITEMW
, 0, (LPARAM
)&item
);
783 hr
= cur
->ParseDisplayName(NULL
, NULL
, name
+len
, NULL
, &new_item
, NULL
);
787 added
= InsertTreeViewItem(info
, cur
, new_item
, item_data
->lpifq
, NULL
, parent
);
790 SendMessageW(info
->hwndTreeView
, TVM_SORTCHILDREN
, FALSE
, (LPARAM
)parent
);
791 return BrsFolder_Rename(info
, added
);
797 static BOOL
BrsFolder_OnCommand( browse_info
*info
, UINT id
)
799 LPBROWSEINFOW lpBrowseInfo
= info
->lpBrowseInfo
;
804 /* The original pidl is owned by the treeview and will be free'd. */
805 info
->pidlRet
= ILClone(info
->pidlRet
);
806 if (info
->pidlRet
== NULL
) /* A null pidl would mean a cancel */
807 info
->pidlRet
= _ILCreateDesktop();
808 pdump( info
->pidlRet
);
809 if (lpBrowseInfo
->pszDisplayName
)
810 SHGetPathFromIDListW( info
->pidlRet
, lpBrowseInfo
->pszDisplayName
);
811 EndDialog( info
->hWnd
, 1 );
815 EndDialog( info
->hWnd
, 0 );
818 case IDC_BROWSE_FOR_FOLDER_NEW_FOLDER
:
819 BrsFolder_NewFolder(info
);
825 static BOOL
BrsFolder_OnSetExpanded(browse_info
*info
, LPVOID selection
,
826 BOOL is_str
, HTREEITEM
*pItem
)
828 LPITEMIDLIST pidlSelection
= (LPITEMIDLIST
)selection
;
829 LPCITEMIDLIST pidlCurrent
, pidlRoot
;
831 BOOL bResult
= FALSE
;
833 memset(&item
, 0, sizeof(item
));
835 /* If 'selection' is a string, convert to a Shell ID List. */
837 CComPtr
<IShellFolder
> psfDesktop
;
840 hr
= SHGetDesktopFolder(&psfDesktop
);
844 hr
= psfDesktop
->ParseDisplayName(NULL
, NULL
, (LPOLESTR
)selection
,
845 NULL
, &pidlSelection
, NULL
);
850 /* Move pidlCurrent behind the SHITEMIDs in pidlSelection, which are the root of
851 * the sub-tree currently displayed. */
852 pidlRoot
= info
->lpBrowseInfo
->pidlRoot
;
853 pidlCurrent
= pidlSelection
;
854 while (!_ILIsEmpty(pidlRoot
) && _ILIsEqualSimple(pidlRoot
, pidlCurrent
)) {
855 pidlRoot
= ILGetNext(pidlRoot
);
856 pidlCurrent
= ILGetNext(pidlCurrent
);
859 /* The given ID List is not part of the SHBrowseForFolder's current sub-tree. */
860 if (!_ILIsEmpty(pidlRoot
))
863 /* Initialize item to point to the first child of the root folder. */
864 item
.mask
= TVIF_PARAM
;
865 item
.hItem
= (HTREEITEM
)SendMessageW(info
->hwndTreeView
, TVM_GETNEXTITEM
, TVGN_ROOT
, 0);
868 item
.hItem
= (HTREEITEM
)SendMessageW(info
->hwndTreeView
, TVM_GETNEXTITEM
, TVGN_CHILD
,
871 /* Walk the tree along the nodes corresponding to the remaining ITEMIDLIST */
872 while (item
.hItem
&& !_ILIsEmpty(pidlCurrent
)) {
873 LPTV_ITEMDATA pItemData
;
875 SendMessageW(info
->hwndTreeView
, TVM_GETITEMW
, 0, (LPARAM
)&item
);
876 pItemData
= (LPTV_ITEMDATA
)item
.lParam
;
878 if (_ILIsEqualSimple(pItemData
->lpi
, pidlCurrent
)) {
879 pidlCurrent
= ILGetNext(pidlCurrent
);
880 if (!_ILIsEmpty(pidlCurrent
)) {
881 /* Only expand current node and move on to it's first child,
882 * if we didn't already reach the last SHITEMID */
883 SendMessageW(info
->hwndTreeView
, TVM_EXPAND
, TVE_EXPAND
, (LPARAM
)item
.hItem
);
884 item
.hItem
= (HTREEITEM
)SendMessageW(info
->hwndTreeView
, TVM_GETNEXTITEM
, TVGN_CHILD
,
888 item
.hItem
= (HTREEITEM
)SendMessageW(info
->hwndTreeView
, TVM_GETNEXTITEM
, TVGN_NEXT
,
893 if (_ILIsEmpty(pidlCurrent
) && item
.hItem
)
897 if (pidlSelection
&& pidlSelection
!= (LPITEMIDLIST
)selection
)
898 ILFree(pidlSelection
);
906 static BOOL
BrsFolder_OnSetSelectionW(browse_info
*info
, LPVOID selection
, BOOL is_str
) {
910 if (!selection
) return FALSE
;
912 bResult
= BrsFolder_OnSetExpanded(info
, selection
, is_str
, &hItem
);
914 SendMessageW(info
->hwndTreeView
, TVM_SELECTITEM
, TVGN_CARET
, (LPARAM
)hItem
);
918 static BOOL
BrsFolder_OnSetSelectionA(browse_info
*info
, LPVOID selection
, BOOL is_str
) {
919 LPWSTR selectionW
= NULL
;
924 return BrsFolder_OnSetSelectionW(info
, selection
, is_str
);
926 if ((length
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)selection
, -1, NULL
, 0)) &&
927 (selectionW
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, length
* sizeof(WCHAR
))) &&
928 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)selection
, -1, selectionW
, length
))
930 result
= BrsFolder_OnSetSelectionW(info
, selectionW
, is_str
);
933 HeapFree(GetProcessHeap(), 0, selectionW
);
937 static BOOL
BrsFolder_OnWindowPosChanging(browse_info
*info
, WINDOWPOS
*pos
)
939 if ((info
->lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
) && !(pos
->flags
& SWP_NOSIZE
))
941 if (pos
->cx
< info
->szMin
.cx
)
942 pos
->cx
= info
->szMin
.cx
;
943 if (pos
->cy
< info
->szMin
.cy
)
944 pos
->cy
= info
->szMin
.cy
;
949 static INT
BrsFolder_OnDestroy(browse_info
*info
)
953 SHFree(info
->layout
);
960 /*************************************************************************
961 * BrsFolderDlgProc32 (not an exported API function)
963 static INT_PTR CALLBACK
BrsFolderDlgProc( HWND hWnd
, UINT msg
, WPARAM wParam
,
968 TRACE("hwnd=%p msg=%04x 0x%08lx 0x%08lx\n", hWnd
, msg
, wParam
, lParam
);
970 if (msg
== WM_INITDIALOG
)
971 return BrsFolder_OnCreate( hWnd
, (browse_info
*) lParam
);
973 info
= (browse_info
*) GetPropW( hWnd
, szBrowseFolderInfo
);
978 return BrsFolder_OnNotify( info
, (UINT
)wParam
, (LPNMHDR
)lParam
);
981 return BrsFolder_OnCommand( info
, wParam
);
983 case WM_WINDOWPOSCHANGING
:
984 return BrsFolder_OnWindowPosChanging( info
, (WINDOWPOS
*)lParam
);
987 if (info
->layout
) /* new style dialogs */
988 LayoutUpdate(hWnd
, info
->layout
, g_layout_info
, LAYOUT_INFO_COUNT
);
991 case BFFM_SETSTATUSTEXTA
:
992 TRACE("Set status %s\n", debugstr_a((LPSTR
)lParam
));
993 SetWindowTextA(GetDlgItem(hWnd
, IDC_BROWSE_FOR_FOLDER_STATUS
), (LPSTR
)lParam
);
996 case BFFM_SETSTATUSTEXTW
:
997 TRACE("Set status %s\n", debugstr_w((LPWSTR
)lParam
));
998 SetWindowTextW(GetDlgItem(hWnd
, IDC_BROWSE_FOR_FOLDER_STATUS
), (LPWSTR
)lParam
);
1002 TRACE("Enable %ld\n", lParam
);
1003 EnableWindow(GetDlgItem(hWnd
, 1), (lParam
)?TRUE
:FALSE
);
1006 case BFFM_SETOKTEXT
: /* unicode only */
1007 TRACE("Set OK text %s\n", debugstr_w((LPWSTR
)wParam
));
1008 SetWindowTextW(GetDlgItem(hWnd
, 1), (LPWSTR
)wParam
);
1011 case BFFM_SETSELECTIONA
:
1012 return BrsFolder_OnSetSelectionA(info
, (LPVOID
)lParam
, (BOOL
)wParam
);
1014 case BFFM_SETSELECTIONW
:
1015 return BrsFolder_OnSetSelectionW(info
, (LPVOID
)lParam
, (BOOL
)wParam
);
1017 case BFFM_SETEXPANDED
: /* unicode only */
1018 return BrsFolder_OnSetExpanded(info
, (LPVOID
)lParam
, (BOOL
)wParam
, NULL
);
1021 return BrsFolder_OnDestroy(info
);
1029 /*************************************************************************
1030 * SHBrowseForFolderA [SHELL32.@]
1031 * SHBrowseForFolder [SHELL32.@]
1033 LPITEMIDLIST WINAPI
SHBrowseForFolderA (LPBROWSEINFOA lpbi
)
1040 TRACE("%p\n", lpbi
);
1042 bi
.hwndOwner
= lpbi
->hwndOwner
;
1043 bi
.pidlRoot
= lpbi
->pidlRoot
;
1044 if (lpbi
->pszDisplayName
)
1046 bi
.pszDisplayName
= (WCHAR
*)HeapAlloc( GetProcessHeap(), 0, MAX_PATH
* sizeof(WCHAR
) );
1047 MultiByteToWideChar( CP_ACP
, 0, lpbi
->pszDisplayName
, -1, bi
.pszDisplayName
, MAX_PATH
);
1050 bi
.pszDisplayName
= NULL
;
1052 if (lpbi
->lpszTitle
)
1054 len
= MultiByteToWideChar( CP_ACP
, 0, lpbi
->lpszTitle
, -1, NULL
, 0 );
1055 title
= (WCHAR
*)HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
1056 MultiByteToWideChar( CP_ACP
, 0, lpbi
->lpszTitle
, -1, title
, len
);
1061 bi
.lpszTitle
= title
;
1062 bi
.ulFlags
= lpbi
->ulFlags
;
1063 bi
.lpfn
= lpbi
->lpfn
;
1064 bi
.lParam
= lpbi
->lParam
;
1065 bi
.iImage
= lpbi
->iImage
;
1066 lpid
= SHBrowseForFolderW( &bi
);
1067 if (bi
.pszDisplayName
)
1069 WideCharToMultiByte( CP_ACP
, 0, bi
.pszDisplayName
, -1,
1070 lpbi
->pszDisplayName
, MAX_PATH
, 0, NULL
);
1071 HeapFree( GetProcessHeap(), 0, bi
.pszDisplayName
);
1073 HeapFree(GetProcessHeap(), 0, title
);
1074 lpbi
->iImage
= bi
.iImage
;
1079 /*************************************************************************
1080 * SHBrowseForFolderW [SHELL32.@]
1083 * crashes when passed a null pointer
1085 LPITEMIDLIST WINAPI
SHBrowseForFolderW (LPBROWSEINFOW lpbi
)
1093 info
.pidlRet
= NULL
;
1094 info
.lpBrowseInfo
= lpbi
;
1095 info
.hwndTreeView
= NULL
;
1097 hr
= OleInitialize(NULL
);
1099 if (lpbi
->ulFlags
& BIF_NEWDIALOGSTYLE
)
1100 wDlgId
= IDD_BROWSE_FOR_FOLDER_NEW
;
1102 wDlgId
= IDD_BROWSE_FOR_FOLDER
;
1103 r
= DialogBoxParamW( shell32_hInstance
, MAKEINTRESOURCEW(wDlgId
), lpbi
->hwndOwner
,
1104 BrsFolderDlgProc
, (LPARAM
)&info
);
1109 ILFree(info
.pidlRet
);
1113 return info
.pidlRet
;