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
28 #define NONAMELESSUNION
30 #include "wine/debug.h"
32 #include "shell32_main.h"
33 #include "commoncontrols.h"
36 #include "shellfolder.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
40 #define SHV_CHANGE_NOTIFY (WM_USER + 0x1111)
42 /* original margins and control size */
43 typedef struct tagLAYOUT_DATA
45 LONG left
, width
, right
;
46 LONG top
, height
, bottom
;
49 typedef struct tagbrowse_info
53 LPBROWSEINFOW lpBrowseInfo
;
55 LAYOUT_DATA
*layout
; /* filled by LayoutInit, used by LayoutUpdate */
57 ULONG hNotify
; /* change notification handle */
60 typedef struct tagTV_ITEMDATA
62 LPSHELLFOLDER lpsfParent
; /* IShellFolder of the parent */
63 LPITEMIDLIST lpi
; /* PIDL relative to parent */
64 LPITEMIDLIST lpifq
; /* Fully qualified PIDL */
65 IEnumIDList
* pEnumIL
; /* Children iterator */
66 } TV_ITEMDATA
, *LPTV_ITEMDATA
;
68 typedef struct tagLAYOUT_INFO
70 int iItemId
; /* control id */
71 DWORD dwAnchor
; /* BF_* flags specifying which margins should remain constant */
74 static const LAYOUT_INFO g_layout_info
[] =
76 {IDD_TITLE
, BF_TOP
|BF_LEFT
|BF_RIGHT
},
77 {IDD_STATUS
, BF_TOP
|BF_LEFT
|BF_RIGHT
},
78 {IDD_FOLDER
, BF_TOP
|BF_LEFT
|BF_RIGHT
},
79 {IDD_TREEVIEW
, BF_TOP
|BF_BOTTOM
|BF_LEFT
|BF_RIGHT
},
80 {IDD_FOLDER
, BF_BOTTOM
|BF_LEFT
},
81 {IDD_FOLDERTEXT
, BF_BOTTOM
|BF_LEFT
|BF_RIGHT
},
82 {IDD_MAKENEWFOLDER
, BF_BOTTOM
|BF_LEFT
},
83 {IDOK
, BF_BOTTOM
|BF_RIGHT
},
84 {IDCANCEL
, BF_BOTTOM
|BF_RIGHT
}
87 #define SUPPORTEDFLAGS (BIF_STATUSTEXT | \
88 BIF_BROWSEFORCOMPUTER | \
89 BIF_RETURNFSANCESTORS | \
90 BIF_RETURNONLYFSDIRS | \
91 BIF_NONEWFOLDERBUTTON | \
92 BIF_NEWDIALOGSTYLE | \
93 BIF_BROWSEINCLUDEFILES)
95 static void FillTreeView(browse_info
*, LPSHELLFOLDER
,
96 LPITEMIDLIST
, HTREEITEM
, IEnumIDList
*);
97 static HTREEITEM
InsertTreeViewItem( browse_info
*, IShellFolder
*,
98 LPCITEMIDLIST
, LPCITEMIDLIST
, IEnumIDList
*, HTREEITEM
);
100 static inline DWORD
BrowseFlagsToSHCONTF(UINT ulFlags
)
102 return SHCONTF_FOLDERS
| (ulFlags
& BIF_BROWSEINCLUDEFILES
? SHCONTF_NONFOLDERS
: 0);
105 static void browsefolder_callback( LPBROWSEINFOW lpBrowseInfo
, HWND hWnd
,
106 UINT msg
, LPARAM param
)
108 if (!lpBrowseInfo
->lpfn
)
110 lpBrowseInfo
->lpfn( hWnd
, msg
, param
, lpBrowseInfo
->lParam
);
113 static LAYOUT_DATA
*LayoutInit(HWND hwnd
, const LAYOUT_INFO
*layout_info
, int layout_count
)
119 GetClientRect(hwnd
, &rcWnd
);
120 data
= SHAlloc(sizeof(LAYOUT_DATA
)*layout_count
);
121 for (i
= 0; i
< layout_count
; i
++)
124 HWND hItem
= GetDlgItem(hwnd
, layout_info
[i
].iItemId
);
127 ERR("Item %d not found\n", i
);
128 GetWindowRect(hItem
, &r
);
129 MapWindowPoints(HWND_DESKTOP
, hwnd
, (LPPOINT
)&r
, 2);
131 data
[i
].left
= r
.left
;
132 data
[i
].right
= rcWnd
.right
- r
.right
;
133 data
[i
].width
= r
.right
- r
.left
;
136 data
[i
].bottom
= rcWnd
.bottom
- r
.bottom
;
137 data
[i
].height
= r
.bottom
- r
.top
;
142 static void LayoutUpdate(HWND hwnd
, LAYOUT_DATA
*data
, const LAYOUT_INFO
*layout_info
, int layout_count
)
147 GetClientRect(hwnd
, &rcWnd
);
148 for (i
= 0; i
< layout_count
; i
++)
151 HWND hItem
= GetDlgItem(hwnd
, layout_info
[i
].iItemId
);
153 GetWindowRect(hItem
, &r
);
154 MapWindowPoints(HWND_DESKTOP
, hwnd
, (LPPOINT
)&r
, 2);
156 if (layout_info
[i
].dwAnchor
& BF_RIGHT
)
158 r
.right
= rcWnd
.right
- data
[i
].right
;
159 if (!(layout_info
[i
].dwAnchor
& BF_LEFT
))
160 r
.left
= r
.right
- data
[i
].width
;
163 if (layout_info
[i
].dwAnchor
& BF_BOTTOM
)
165 r
.bottom
= rcWnd
.bottom
- data
[i
].bottom
;
166 if (!(layout_info
[i
].dwAnchor
& BF_TOP
))
167 r
.top
= r
.bottom
- data
[i
].height
;
170 SetWindowPos(hItem
, NULL
, r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
, SWP_NOZORDER
);
175 /******************************************************************************
176 * InitializeTreeView [Internal]
178 * Called from WM_INITDIALOG handler.
181 * hwndParent [I] The BrowseForFolder dialog
182 * root [I] ITEMIDLIST of the root shell folder
184 static void InitializeTreeView( browse_info
*info
)
186 LPITEMIDLIST pidlParent
, pidlChild
;
187 HIMAGELIST hImageList
;
189 IShellFolder
*lpsfParent
, *lpsfRoot
;
190 IEnumIDList
* pEnumChildren
= NULL
;
193 LPCITEMIDLIST root
= info
->lpBrowseInfo
->pidlRoot
;
195 TRACE("%p\n", info
);
197 Shell_GetImageLists(NULL
, &hImageList
);
200 SendMessageW( info
->hwndTreeView
, TVM_SETIMAGELIST
, 0, (LPARAM
)hImageList
);
202 /* We want to call InsertTreeViewItem down the code, in order to insert
203 * the root item of the treeview. Due to InsertTreeViewItem's signature,
204 * we need the following to do this:
206 * + An ITEMIDLIST corresponding to _the parent_ of root.
207 * + An ITEMIDLIST, which is a relative path from root's parent to root
208 * (containing a single SHITEMID).
209 * + An IShellFolder interface pointer of root's parent folder.
211 * If root is 'Desktop', then root's parent is also 'Desktop'.
214 pidlParent
= ILClone(root
);
215 ILRemoveLastID(pidlParent
);
216 pidlChild
= ILClone(ILFindLastID(root
));
218 if (_ILIsDesktop(pidlParent
)) {
219 hr
= SHGetDesktopFolder(&lpsfParent
);
221 IShellFolder
*lpsfDesktop
;
222 hr
= SHGetDesktopFolder(&lpsfDesktop
);
224 WARN("SHGetDesktopFolder failed! hr = %08lx\n", hr
);
229 hr
= IShellFolder_BindToObject(lpsfDesktop
, pidlParent
, 0, &IID_IShellFolder
, (LPVOID
*)&lpsfParent
);
230 IShellFolder_Release(lpsfDesktop
);
234 WARN("Could not bind to parent shell folder! hr = %08lx\n", hr
);
240 if (!_ILIsEmpty(pidlChild
)) {
241 hr
= IShellFolder_BindToObject(lpsfParent
, pidlChild
, 0, &IID_IShellFolder
, (LPVOID
*)&lpsfRoot
);
243 lpsfRoot
= lpsfParent
;
244 hr
= IShellFolder_AddRef(lpsfParent
);
248 WARN("Could not bind to root shell folder! hr = %08lx\n", hr
);
249 IShellFolder_Release(lpsfParent
);
255 flags
= BrowseFlagsToSHCONTF( info
->lpBrowseInfo
->ulFlags
);
256 hr
= IShellFolder_EnumObjects( lpsfRoot
, info
->hWnd
, flags
, &pEnumChildren
);
258 WARN("Could not get child iterator! hr = %08lx\n", hr
);
259 IShellFolder_Release(lpsfParent
);
260 IShellFolder_Release(lpsfRoot
);
266 SendMessageW( info
->hwndTreeView
, TVM_DELETEITEM
, 0, (LPARAM
)TVI_ROOT
);
267 item
= InsertTreeViewItem( info
, lpsfParent
, pidlChild
,
268 pidlParent
, pEnumChildren
, TVI_ROOT
);
269 SendMessageW( info
->hwndTreeView
, TVM_EXPAND
, TVE_EXPAND
, (LPARAM
)item
);
273 IShellFolder_Release(lpsfRoot
);
274 IShellFolder_Release(lpsfParent
);
277 static int GetIcon(LPCITEMIDLIST lpi
, UINT uFlags
)
282 list
= (IImageList
*)SHGetFileInfoW((LPCWSTR
)lpi
, 0 ,&sfi
, sizeof(SHFILEINFOW
), uFlags
);
283 if (list
) IImageList_Release(list
);
287 static void GetNormalAndSelectedIcons(LPITEMIDLIST lpifq
, LPTVITEMW lpTV_ITEM
)
289 LPITEMIDLIST pidlDesktop
= NULL
;
292 TRACE("%p %p\n",lpifq
, lpTV_ITEM
);
296 pidlDesktop
= _ILCreateDesktop();
300 flags
= SHGFI_PIDL
| SHGFI_SYSICONINDEX
| SHGFI_SMALLICON
;
301 lpTV_ITEM
->iImage
= GetIcon( lpifq
, flags
);
303 flags
= SHGFI_PIDL
| SHGFI_SYSICONINDEX
| SHGFI_SMALLICON
| SHGFI_OPENICON
;
304 lpTV_ITEM
->iSelectedImage
= GetIcon( lpifq
, flags
);
307 ILFree( pidlDesktop
);
310 /******************************************************************************
313 * Query a shell folder for the display name of one of its children
316 * lpsf [I] IShellFolder interface of the folder to be queried.
317 * lpi [I] ITEMIDLIST of the child, relative to parent
318 * dwFlags [I] as in IShellFolder::GetDisplayNameOf
319 * lpFriendlyName [O] The desired display name in unicode
325 static BOOL
GetName(LPSHELLFOLDER lpsf
, LPCITEMIDLIST lpi
, DWORD dwFlags
, LPWSTR lpFriendlyName
)
330 TRACE("%p %p %lx %p\n", lpsf
, lpi
, dwFlags
, lpFriendlyName
);
331 if (SUCCEEDED(IShellFolder_GetDisplayNameOf(lpsf
, lpi
, dwFlags
, &str
)))
332 bSuccess
= StrRetToStrNW(lpFriendlyName
, MAX_PATH
, &str
, lpi
);
336 TRACE("-- %s\n", debugstr_w(lpFriendlyName
));
340 /******************************************************************************
341 * InsertTreeViewItem [Internal]
344 * info [I] data for the dialog
345 * lpsf [I] IShellFolder interface of the item's parent shell folder
346 * pidl [I] ITEMIDLIST of the child to insert, relative to parent
347 * pidlParent [I] ITEMIDLIST of the parent shell folder
348 * pEnumIL [I] Iterator for the children of the item to be inserted
349 * hParent [I] The treeview-item that represents the parent shell folder
352 * Success: Handle to the created and inserted treeview-item
355 static HTREEITEM
InsertTreeViewItem( browse_info
*info
, IShellFolder
* lpsf
,
356 LPCITEMIDLIST pidl
, LPCITEMIDLIST pidlParent
, IEnumIDList
* pEnumIL
,
360 TVINSERTSTRUCTW tvins
;
361 WCHAR szBuff
[MAX_PATH
];
362 LPTV_ITEMDATA lptvid
=0;
364 tvi
.mask
= TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_PARAM
;
366 tvi
.cChildren
= pEnumIL
? 1 : 0;
367 tvi
.mask
|= TVIF_CHILDREN
;
369 if (!GetName(lpsf
, pidl
, SHGDN_NORMAL
, szBuff
))
372 lptvid
= SHAlloc( sizeof(TV_ITEMDATA
) );
376 tvi
.pszText
= szBuff
;
377 tvi
.cchTextMax
= MAX_PATH
;
378 tvi
.lParam
= (LPARAM
)lptvid
;
380 IShellFolder_AddRef(lpsf
);
381 lptvid
->lpsfParent
= lpsf
;
382 lptvid
->lpi
= ILClone(pidl
);
383 lptvid
->lpifq
= pidlParent
? ILCombine(pidlParent
, pidl
) : ILClone(pidl
);
384 lptvid
->pEnumIL
= pEnumIL
;
385 GetNormalAndSelectedIcons(lptvid
->lpifq
, &tvi
);
388 tvins
.hInsertAfter
= NULL
;
389 tvins
.hParent
= hParent
;
391 return TreeView_InsertItemW( info
->hwndTreeView
, &tvins
);
394 /******************************************************************************
395 * FillTreeView [Internal]
397 * For each child (given by lpe) of the parent shell folder, which is given by
398 * lpsf and whose PIDL is pidl, insert a treeview-item right under hParent
401 * info [I] data for the dialog
402 * lpsf [I] IShellFolder interface of the parent shell folder
403 * pidl [I] ITEMIDLIST of the parent shell folder
404 * hParent [I] The treeview item that represents the parent shell folder
405 * lpe [I] An iterator for the children of the parent shell folder
407 static void FillTreeView( browse_info
*info
, IShellFolder
* lpsf
,
408 LPITEMIDLIST pidl
, HTREEITEM hParent
, IEnumIDList
* lpe
)
410 LPITEMIDLIST pidlTemp
= 0;
413 HWND hwnd
= GetParent( info
->hwndTreeView
);
415 TRACE("%p %p %p %p\n",lpsf
, pidl
, hParent
, lpe
);
417 /* No IEnumIDList -> No children */
421 SetCursor( LoadCursorA( 0, (LPSTR
)IDC_WAIT
) );
423 while (S_OK
== IEnumIDList_Next(lpe
,1,&pidlTemp
,&ulFetched
))
425 ULONG ulAttrs
= SFGAO_HASSUBFOLDER
| SFGAO_FOLDER
;
426 IEnumIDList
* pEnumIL
= NULL
;
427 IShellFolder
* pSFChild
= NULL
;
428 IShellFolder_GetAttributesOf(lpsf
, 1, (LPCITEMIDLIST
*)&pidlTemp
, &ulAttrs
);
429 if (ulAttrs
& SFGAO_FOLDER
)
431 hr
= IShellFolder_BindToObject(lpsf
,pidlTemp
,NULL
,&IID_IShellFolder
,(LPVOID
*)&pSFChild
);
434 DWORD flags
= BrowseFlagsToSHCONTF(info
->lpBrowseInfo
->ulFlags
);
435 hr
= IShellFolder_EnumObjects(pSFChild
, hwnd
, flags
, &pEnumIL
);
438 if ((IEnumIDList_Skip(pEnumIL
, 1) != S_OK
) ||
439 FAILED(IEnumIDList_Reset(pEnumIL
)))
441 IEnumIDList_Release(pEnumIL
);
445 IShellFolder_Release(pSFChild
);
449 if (!InsertTreeViewItem(info
, lpsf
, pidlTemp
, pidl
, pEnumIL
, hParent
))
451 SHFree(pidlTemp
); /* Finally, free the pidl that the shell gave us... */
457 SetCursor(LoadCursorW(0, (LPWSTR
)IDC_ARROW
));
461 static inline BOOL
PIDLIsType(LPCITEMIDLIST pidl
, PIDLTYPE type
)
463 LPPIDLDATA data
= _ILGetDataPointer(pidl
);
466 return (data
->type
== type
);
469 static void BrsFolder_CheckValidSelection( browse_info
*info
, LPTV_ITEMDATA lptvid
)
471 LPBROWSEINFOW lpBrowseInfo
= info
->lpBrowseInfo
;
472 LPCITEMIDLIST pidl
= lptvid
->lpi
;
473 BOOL bEnabled
= TRUE
;
477 if ((lpBrowseInfo
->ulFlags
& BIF_BROWSEFORCOMPUTER
) &&
478 !PIDLIsType(pidl
, PT_COMP
))
480 if (lpBrowseInfo
->ulFlags
& BIF_RETURNFSANCESTORS
)
482 dwAttributes
= SFGAO_FILESYSANCESTOR
| SFGAO_FILESYSTEM
;
483 r
= IShellFolder_GetAttributesOf(lptvid
->lpsfParent
, 1,
484 (LPCITEMIDLIST
*)&lptvid
->lpi
, &dwAttributes
);
485 if (FAILED(r
) || !(dwAttributes
& (SFGAO_FILESYSANCESTOR
|SFGAO_FILESYSTEM
)))
489 dwAttributes
= SFGAO_FOLDER
| SFGAO_FILESYSTEM
;
490 r
= IShellFolder_GetAttributesOf(lptvid
->lpsfParent
, 1,
491 (LPCITEMIDLIST
*)&lptvid
->lpi
, &dwAttributes
);
493 ((dwAttributes
& (SFGAO_FOLDER
|SFGAO_FILESYSTEM
)) != (SFGAO_FOLDER
|SFGAO_FILESYSTEM
)))
495 if (lpBrowseInfo
->ulFlags
& BIF_RETURNONLYFSDIRS
)
497 EnableWindow(GetDlgItem(info
->hWnd
, IDD_MAKENEWFOLDER
), FALSE
);
500 EnableWindow(GetDlgItem(info
->hWnd
, IDD_MAKENEWFOLDER
), TRUE
);
502 SendMessageW(info
->hWnd
, BFFM_ENABLEOK
, 0, bEnabled
);
505 static LRESULT
BrsFolder_Treeview_Delete( browse_info
*info
, NMTREEVIEWW
*pnmtv
)
507 LPTV_ITEMDATA lptvid
= (LPTV_ITEMDATA
)pnmtv
->itemOld
.lParam
;
509 TRACE("TVN_DELETEITEMA/W %p\n", lptvid
);
511 IShellFolder_Release(lptvid
->lpsfParent
);
513 IEnumIDList_Release(lptvid
->pEnumIL
);
515 SHFree(lptvid
->lpifq
);
520 static LRESULT
BrsFolder_Treeview_Expand( browse_info
*info
, NMTREEVIEWW
*pnmtv
)
522 IShellFolder
*lpsf2
= NULL
;
523 LPTV_ITEMDATA lptvid
= (LPTV_ITEMDATA
) pnmtv
->itemNew
.lParam
;
526 TRACE("TVN_ITEMEXPANDINGA/W\n");
528 if ((pnmtv
->itemNew
.state
& TVIS_EXPANDEDONCE
))
531 if (!_ILIsEmpty(lptvid
->lpi
)) {
532 r
= IShellFolder_BindToObject( lptvid
->lpsfParent
, lptvid
->lpi
, 0,
533 &IID_IShellFolder
, (void**)&lpsf2
);
535 lpsf2
= lptvid
->lpsfParent
;
536 IShellFolder_AddRef(lpsf2
);
542 FillTreeView( info
, lpsf2
, lptvid
->lpifq
, pnmtv
->itemNew
.hItem
, lptvid
->pEnumIL
);
543 IShellFolder_Release( lpsf2
);
546 /* My Computer is already sorted and trying to do a simple text
547 * sort will only mess things up */
548 if (!_ILIsMyComputer(lptvid
->lpi
))
549 SendMessageW( info
->hwndTreeView
, TVM_SORTCHILDREN
,
550 FALSE
, (LPARAM
)pnmtv
->itemNew
.hItem
);
555 static HRESULT
BrsFolder_Treeview_Changed( browse_info
*info
, NMTREEVIEWW
*pnmtv
)
557 LPTV_ITEMDATA lptvid
= (LPTV_ITEMDATA
) pnmtv
->itemNew
.lParam
;
558 WCHAR name
[MAX_PATH
];
560 ILFree(info
->pidlRet
);
561 info
->pidlRet
= ILClone(lptvid
->lpifq
);
563 if (GetName(lptvid
->lpsfParent
, lptvid
->lpi
, SHGDN_NORMAL
, name
))
564 SetWindowTextW( GetDlgItem(info
->hWnd
, IDD_FOLDERTEXT
), name
);
566 browsefolder_callback( info
->lpBrowseInfo
, info
->hWnd
, BFFM_SELCHANGED
,
567 (LPARAM
)info
->pidlRet
);
568 BrsFolder_CheckValidSelection( info
, lptvid
);
572 static LRESULT
BrsFolder_Treeview_Rename(browse_info
*info
, NMTVDISPINFOW
*pnmtv
)
574 LPTV_ITEMDATA item_data
;
575 WCHAR old_path
[MAX_PATH
], new_path
[MAX_PATH
], *p
;
579 if(!pnmtv
->item
.pszText
)
582 item
.mask
= TVIF_HANDLE
|TVIF_PARAM
;
583 item
.hItem
= (HTREEITEM
)SendMessageW(info
->hwndTreeView
, TVM_GETNEXTITEM
, TVGN_CARET
, 0);
584 SendMessageW(info
->hwndTreeView
, TVM_GETITEMW
, 0, (LPARAM
)&item
);
585 item_data
= (LPTV_ITEMDATA
)item
.lParam
;
587 SHGetPathFromIDListW(item_data
->lpifq
, old_path
);
588 if(!(p
= wcsrchr(old_path
, '\\')))
590 p
= new_path
+(p
-old_path
+1);
591 memcpy(new_path
, old_path
, (p
-new_path
)*sizeof(WCHAR
));
592 lstrcpyW(p
, pnmtv
->item
.pszText
);
594 if(!MoveFileW(old_path
, new_path
))
597 SHFree(item_data
->lpifq
);
598 SHFree(item_data
->lpi
);
599 item_data
->lpifq
= SHSimpleIDListFromPathW(new_path
);
600 IShellFolder_ParseDisplayName(item_data
->lpsfParent
, NULL
, NULL
,
601 pnmtv
->item
.pszText
, NULL
, &item_data
->lpi
, NULL
);
603 item
.mask
= TVIF_HANDLE
|TVIF_TEXT
;
604 item
.pszText
= pnmtv
->item
.pszText
;
605 SendMessageW(info
->hwndTreeView
, TVM_SETITEMW
, 0, (LPARAM
)&item
);
607 nmtv
.itemNew
.lParam
= item
.lParam
;
608 BrsFolder_Treeview_Changed(info
, &nmtv
);
612 static HRESULT
BrsFolder_Rename(browse_info
*info
, HTREEITEM rename
)
614 SendMessageW(info
->hwndTreeView
, TVM_SELECTITEM
, TVGN_CARET
, (LPARAM
)rename
);
615 SendMessageW(info
->hwndTreeView
, TVM_EDITLABELW
, 0, (LPARAM
)rename
);
619 static LRESULT
BrsFolder_Treeview_Keydown(browse_info
*info
, LPNMTVKEYDOWN keydown
)
621 HTREEITEM selected_item
;
623 /* Old dialog doesn't support those advanced features */
624 if (!(info
->lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
))
627 selected_item
= (HTREEITEM
)SendMessageW(info
->hwndTreeView
, TVM_GETNEXTITEM
, TVGN_CARET
, 0);
629 switch (keydown
->wVKey
)
632 BrsFolder_Rename(info
, selected_item
);
636 const ITEMIDLIST
*item_id
;
640 TV_ITEMDATA
*item_data
;
642 item
.mask
= TVIF_PARAM
;
643 item
.hItem
= selected_item
;
644 SendMessageW(info
->hwndTreeView
, TVM_GETITEMW
, 0, (LPARAM
)&item
);
645 item_data
= (TV_ITEMDATA
*)item
.lParam
;
646 item_id
= item_data
->lpi
;
648 hr
= IShellFolder_QueryInterface(item_data
->lpsfParent
, &IID_ISFHelper
, (void**)&psfhlp
);
652 /* perform the item deletion - tree view gets updated over shell notification */
653 ISFHelper_DeleteItems(psfhlp
, 1, &item_id
);
654 ISFHelper_Release(psfhlp
);
661 static LRESULT
BrsFolder_OnNotify( browse_info
*info
, UINT CtlID
, LPNMHDR lpnmh
)
663 NMTREEVIEWW
*pnmtv
= (NMTREEVIEWW
*)lpnmh
;
665 TRACE("%p %x %p msg=%x\n", info
, CtlID
, lpnmh
, pnmtv
->hdr
.code
);
667 if (pnmtv
->hdr
.idFrom
!= IDD_TREEVIEW
)
670 switch (pnmtv
->hdr
.code
)
672 case TVN_DELETEITEMA
:
673 case TVN_DELETEITEMW
:
674 return BrsFolder_Treeview_Delete( info
, pnmtv
);
676 case TVN_ITEMEXPANDINGA
:
677 case TVN_ITEMEXPANDINGW
:
678 return BrsFolder_Treeview_Expand( info
, pnmtv
);
680 case TVN_SELCHANGEDA
:
681 case TVN_SELCHANGEDW
:
682 return BrsFolder_Treeview_Changed( info
, pnmtv
);
684 case TVN_ENDLABELEDITA
:
685 case TVN_ENDLABELEDITW
:
686 return BrsFolder_Treeview_Rename( info
, (LPNMTVDISPINFOW
)pnmtv
);
689 return BrsFolder_Treeview_Keydown( info
, (LPNMTVKEYDOWN
)pnmtv
);
692 WARN("unhandled (%d)\n", pnmtv
->hdr
.code
);
700 static BOOL
BrsFolder_OnCreate( HWND hWnd
, browse_info
*info
)
702 LPITEMIDLIST computer_pidl
;
703 SHChangeNotifyEntry ntreg
;
704 LPBROWSEINFOW lpBrowseInfo
= info
->lpBrowseInfo
;
707 SetPropW( hWnd
, L
"__WINE_BRSFOLDERDLG_INFO", info
);
709 if (lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
)
710 FIXME("flags BIF_NEWDIALOGSTYLE partially implemented\n");
711 if (lpBrowseInfo
->ulFlags
& ~SUPPORTEDFLAGS
)
712 FIXME("flags %x not implemented\n", lpBrowseInfo
->ulFlags
& ~SUPPORTEDFLAGS
);
714 if (lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
)
718 info
->layout
= LayoutInit(hWnd
, g_layout_info
, ARRAY_SIZE(g_layout_info
));
720 /* TODO: Windows allows shrinking the windows a bit */
721 GetWindowRect(hWnd
, &rcWnd
);
722 info
->szMin
.cx
= rcWnd
.right
- rcWnd
.left
;
723 info
->szMin
.cy
= rcWnd
.bottom
- rcWnd
.top
;
730 if (lpBrowseInfo
->lpszTitle
)
731 SetWindowTextW( GetDlgItem(hWnd
, IDD_TITLE
), lpBrowseInfo
->lpszTitle
);
733 ShowWindow( GetDlgItem(hWnd
, IDD_TITLE
), SW_HIDE
);
735 if (!(lpBrowseInfo
->ulFlags
& BIF_STATUSTEXT
)
736 || (lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
))
737 ShowWindow( GetDlgItem(hWnd
, IDD_STATUS
), SW_HIDE
);
739 /* Hide "Make New Folder" Button? */
740 if ((lpBrowseInfo
->ulFlags
& BIF_NONEWFOLDERBUTTON
)
741 || !(lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
))
742 ShowWindow( GetDlgItem(hWnd
, IDD_MAKENEWFOLDER
), SW_HIDE
);
744 /* Hide the editbox? */
745 if (!(lpBrowseInfo
->ulFlags
& BIF_EDITBOX
))
747 ShowWindow( GetDlgItem(hWnd
, IDD_FOLDER
), SW_HIDE
);
748 ShowWindow( GetDlgItem(hWnd
, IDD_FOLDERTEXT
), SW_HIDE
);
751 info
->hwndTreeView
= GetDlgItem( hWnd
, IDD_TREEVIEW
);
752 if (info
->hwndTreeView
)
754 InitializeTreeView( info
);
756 /* Resize the treeview if there's not editbox */
757 if ((lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
)
758 && !(lpBrowseInfo
->ulFlags
& BIF_EDITBOX
))
761 GetClientRect(info
->hwndTreeView
, &rc
);
762 SetWindowPos(info
->hwndTreeView
, HWND_TOP
, 0, 0,
763 rc
.right
, rc
.bottom
+ 40, SWP_NOMOVE
);
767 ERR("treeview control missing!\n");
769 /* Register for change notifications */
770 SHGetFolderLocation(NULL
, CSIDL_DESKTOP
, NULL
, 0, &computer_pidl
);
772 ntreg
.pidl
= computer_pidl
;
773 ntreg
.fRecursive
= TRUE
;
775 info
->hNotify
= SHChangeNotifyRegister(hWnd
, SHCNRF_InterruptLevel
, SHCNE_ALLEVENTS
, SHV_CHANGE_NOTIFY
, 1, &ntreg
);
777 browsefolder_callback( info
->lpBrowseInfo
, hWnd
, BFFM_INITIALIZED
, 0 );
779 ILFree(computer_pidl
);
784 static HRESULT
BrsFolder_NewFolder(browse_info
*info
)
786 DWORD flags
= BrowseFlagsToSHCONTF(info
->lpBrowseInfo
->ulFlags
);
787 IShellFolder
*desktop
, *cur
;
789 WCHAR name
[MAX_PATH
];
790 HTREEITEM parent
, added
;
791 LPTV_ITEMDATA item_data
;
792 LPITEMIDLIST new_item
;
798 ERR("Make new folder button should be disabled\n");
802 /* Create new directory */
803 hr
= SHGetDesktopFolder(&desktop
);
806 hr
= IShellFolder_BindToObject(desktop
, info
->pidlRet
, 0, &IID_IShellFolder
, (void**)&cur
);
807 IShellFolder_Release(desktop
);
811 hr
= IShellFolder_QueryInterface(cur
, &IID_ISFHelper
, (void**)&sfhelper
);
815 if(!SHGetPathFromIDListW(info
->pidlRet
, name
)) {
820 len
= lstrlenW(name
);
823 hr
= ISFHelper_GetUniqueName(sfhelper
, &name
[len
], MAX_PATH
-len
);
824 ISFHelper_Release(sfhelper
);
829 if(!CreateDirectoryW(name
, NULL
))
832 /* Update parent of newly created directory */
833 parent
= (HTREEITEM
)SendMessageW(info
->hwndTreeView
, TVM_GETNEXTITEM
, TVGN_CARET
, 0);
837 SendMessageW(info
->hwndTreeView
, TVM_EXPAND
, TVE_EXPAND
, (LPARAM
)parent
);
839 memset(&item
, 0, sizeof(TVITEMW
));
840 item
.mask
= TVIF_PARAM
|TVIF_STATE
;
842 SendMessageW(info
->hwndTreeView
, TVM_GETITEMW
, 0, (LPARAM
)&item
);
843 item_data
= (LPTV_ITEMDATA
)item
.lParam
;
847 if(item_data
->pEnumIL
)
848 IEnumIDList_Release(item_data
->pEnumIL
);
849 hr
= IShellFolder_EnumObjects(cur
, info
->hwndTreeView
, flags
, &item_data
->pEnumIL
);
853 /* Update treeview */
854 if(!(item
.state
&TVIS_EXPANDEDONCE
)) {
855 item
.mask
= TVIF_STATE
;
856 item
.state
= TVIS_EXPANDEDONCE
;
857 item
.stateMask
= TVIS_EXPANDEDONCE
;
858 SendMessageW(info
->hwndTreeView
, TVM_SETITEMW
, 0, (LPARAM
)&item
);
861 hr
= IShellFolder_ParseDisplayName(cur
, NULL
, NULL
, name
+len
, NULL
, &new_item
, NULL
);
865 added
= InsertTreeViewItem(info
, cur
, new_item
, item_data
->lpifq
, NULL
, parent
);
866 IShellFolder_Release(cur
);
869 SendMessageW(info
->hwndTreeView
, TVM_SORTCHILDREN
, FALSE
, (LPARAM
)parent
);
870 return BrsFolder_Rename(info
, added
);
876 static BOOL
BrsFolder_OnCommand( browse_info
*info
, UINT id
)
878 LPBROWSEINFOW lpBrowseInfo
= info
->lpBrowseInfo
;
883 if (info
->pidlRet
== NULL
) /* A null pidl would mean a cancel */
884 info
->pidlRet
= _ILCreateDesktop();
885 pdump( info
->pidlRet
);
886 if (lpBrowseInfo
->pszDisplayName
)
887 SHGetPathFromIDListW( info
->pidlRet
, lpBrowseInfo
->pszDisplayName
);
888 EndDialog( info
->hWnd
, 1 );
892 EndDialog( info
->hWnd
, 0 );
895 case IDD_MAKENEWFOLDER
:
896 BrsFolder_NewFolder(info
);
902 static BOOL
BrsFolder_OnSetExpanded(browse_info
*info
, LPVOID selection
,
903 BOOL is_str
, HTREEITEM
*pItem
)
905 LPITEMIDLIST pidlSelection
= selection
;
906 LPCITEMIDLIST pidlCurrent
, pidlRoot
;
908 BOOL bResult
= FALSE
;
910 memset(&item
, 0, sizeof(item
));
912 /* If 'selection' is a string, convert to a Shell ID List. */
914 IShellFolder
*psfDesktop
;
917 hr
= SHGetDesktopFolder(&psfDesktop
);
921 hr
= IShellFolder_ParseDisplayName(psfDesktop
, NULL
, NULL
,
922 selection
, NULL
, &pidlSelection
, NULL
);
923 IShellFolder_Release(psfDesktop
);
928 /* Move pidlCurrent behind the SHITEMIDs in pidlSelection, which are the root of
929 * the sub-tree currently displayed. */
930 pidlRoot
= info
->lpBrowseInfo
->pidlRoot
;
931 pidlCurrent
= pidlSelection
;
932 while (!_ILIsEmpty(pidlRoot
) && _ILIsEqualSimple(pidlRoot
, pidlCurrent
)) {
933 pidlRoot
= ILGetNext(pidlRoot
);
934 pidlCurrent
= ILGetNext(pidlCurrent
);
937 /* The given ID List is not part of the SHBrowseForFolder's current sub-tree. */
938 if (!_ILIsEmpty(pidlRoot
))
941 /* Initialize item to point to the first child of the root folder. */
942 item
.mask
= TVIF_PARAM
;
943 item
.hItem
= (HTREEITEM
)SendMessageW(info
->hwndTreeView
, TVM_GETNEXTITEM
, TVGN_ROOT
, 0);
946 item
.hItem
= (HTREEITEM
)SendMessageW(info
->hwndTreeView
, TVM_GETNEXTITEM
, TVGN_CHILD
,
949 /* Walk the tree along the nodes corresponding to the remaining ITEMIDLIST */
950 while (item
.hItem
&& !_ILIsEmpty(pidlCurrent
)) {
951 LPTV_ITEMDATA pItemData
;
953 SendMessageW(info
->hwndTreeView
, TVM_GETITEMW
, 0, (LPARAM
)&item
);
954 pItemData
= (LPTV_ITEMDATA
)item
.lParam
;
956 if (_ILIsEqualSimple(pItemData
->lpi
, pidlCurrent
)) {
957 pidlCurrent
= ILGetNext(pidlCurrent
);
958 if (!_ILIsEmpty(pidlCurrent
)) {
959 /* Only expand current node and move on to its first child,
960 * if we didn't already reach the last SHITEMID */
961 SendMessageW(info
->hwndTreeView
, TVM_EXPAND
, TVE_EXPAND
, (LPARAM
)item
.hItem
);
962 item
.hItem
= (HTREEITEM
)SendMessageW(info
->hwndTreeView
, TVM_GETNEXTITEM
, TVGN_CHILD
,
966 item
.hItem
= (HTREEITEM
)SendMessageW(info
->hwndTreeView
, TVM_GETNEXTITEM
, TVGN_NEXT
,
971 if (_ILIsEmpty(pidlCurrent
) && item
.hItem
)
975 if (pidlSelection
&& pidlSelection
!= selection
)
976 ILFree(pidlSelection
);
984 static BOOL
BrsFolder_OnSetSelectionW(browse_info
*info
, LPVOID selection
, BOOL is_str
) {
988 if (!selection
) return FALSE
;
990 bResult
= BrsFolder_OnSetExpanded(info
, selection
, is_str
, &hItem
);
992 SendMessageW(info
->hwndTreeView
, TVM_SELECTITEM
, TVGN_CARET
, (LPARAM
)hItem
);
996 static BOOL
BrsFolder_OnSetSelectionA(browse_info
*info
, LPVOID selection
, BOOL is_str
) {
997 LPWSTR selectionW
= NULL
;
1002 return BrsFolder_OnSetSelectionW(info
, selection
, is_str
);
1004 if ((length
= MultiByteToWideChar(CP_ACP
, 0, selection
, -1, NULL
, 0)) &&
1005 (selectionW
= heap_alloc(length
* sizeof(WCHAR
))) &&
1006 MultiByteToWideChar(CP_ACP
, 0, selection
, -1, selectionW
, length
))
1008 result
= BrsFolder_OnSetSelectionW(info
, selectionW
, is_str
);
1011 heap_free(selectionW
);
1015 static LRESULT
BrsFolder_OnWindowPosChanging(browse_info
*info
, WINDOWPOS
*pos
)
1017 if ((info
->lpBrowseInfo
->ulFlags
& BIF_NEWDIALOGSTYLE
) && !(pos
->flags
& SWP_NOSIZE
))
1019 if (pos
->cx
< info
->szMin
.cx
)
1020 pos
->cx
= info
->szMin
.cx
;
1021 if (pos
->cy
< info
->szMin
.cy
)
1022 pos
->cy
= info
->szMin
.cy
;
1027 static INT
BrsFolder_OnDestroy(browse_info
*info
)
1031 SHFree(info
->layout
);
1032 info
->layout
= NULL
;
1035 SHChangeNotifyDeregister(info
->hNotify
);
1040 /* Find a treeview node by recursively walking the treeview */
1041 static HTREEITEM
BrsFolder_FindItemByPidl(browse_info
*info
, LPCITEMIDLIST pidl
, HTREEITEM hItem
)
1044 TV_ITEMDATA
*item_data
;
1047 item
.mask
= TVIF_HANDLE
| TVIF_PARAM
;
1049 SendMessageW(info
->hwndTreeView
, TVM_GETITEMW
, 0, (LPARAM
)&item
);
1050 item_data
= (TV_ITEMDATA
*)item
.lParam
;
1052 hr
= IShellFolder_CompareIDs(item_data
->lpsfParent
, 0, item_data
->lpifq
, pidl
);
1053 if(SUCCEEDED(hr
) && !HRESULT_CODE(hr
))
1056 hItem
= (HTREEITEM
)SendMessageW(info
->hwndTreeView
, TVM_GETNEXTITEM
, TVGN_CHILD
, (LPARAM
)hItem
);
1060 HTREEITEM newItem
= BrsFolder_FindItemByPidl(info
, pidl
, hItem
);
1063 hItem
= (HTREEITEM
)SendMessageW(info
->hwndTreeView
, TVM_GETNEXTITEM
, TVGN_NEXT
, (LPARAM
)hItem
);
1068 static LRESULT
BrsFolder_OnChange(browse_info
*info
, const LPCITEMIDLIST
*pidls
, LONG event
)
1072 TRACE("(%p)->(%p, %p, 0x%08lx)\n", info
, pidls
[0], pidls
[1], event
);
1079 HTREEITEM handle_root
= (HTREEITEM
)SendMessageW(info
->hwndTreeView
, TVM_GETNEXTITEM
, TVGN_ROOT
, 0);
1080 HTREEITEM handle_item
= BrsFolder_FindItemByPidl(info
, pidls
[0], handle_root
);
1083 SendMessageW(info
->hwndTreeView
, TVM_DELETEITEM
, 0, (LPARAM
)handle_item
);
1091 /*************************************************************************
1092 * BrsFolderDlgProc32 (not an exported API function)
1094 static INT_PTR CALLBACK
BrsFolderDlgProc( HWND hWnd
, UINT msg
, WPARAM wParam
,
1099 TRACE("hwnd=%p msg=%04x 0x%08Ix 0x%08Ix\n", hWnd
, msg
, wParam
, lParam
);
1101 if (msg
== WM_INITDIALOG
)
1102 return BrsFolder_OnCreate( hWnd
, (browse_info
*) lParam
);
1104 info
= GetPropW( hWnd
, L
"__WINE_BRSFOLDERDLG_INFO" );
1111 return BrsFolder_OnNotify( info
, (UINT
)wParam
, (LPNMHDR
)lParam
);
1114 return BrsFolder_OnCommand( info
, wParam
);
1116 case WM_WINDOWPOSCHANGING
:
1117 return BrsFolder_OnWindowPosChanging( info
, (WINDOWPOS
*)lParam
);
1120 if (info
->layout
) /* new style dialogs */
1121 LayoutUpdate(hWnd
, info
->layout
, g_layout_info
, ARRAY_SIZE(g_layout_info
));
1124 case BFFM_SETSTATUSTEXTA
:
1125 TRACE("Set status %s\n", debugstr_a((LPSTR
)lParam
));
1126 SetWindowTextA(GetDlgItem(hWnd
, IDD_STATUS
), (LPSTR
)lParam
);
1129 case BFFM_SETSTATUSTEXTW
:
1130 TRACE("Set status %s\n", debugstr_w((LPWSTR
)lParam
));
1131 SetWindowTextW(GetDlgItem(hWnd
, IDD_STATUS
), (LPWSTR
)lParam
);
1135 TRACE("Enable %Id\n", lParam
);
1136 EnableWindow(GetDlgItem(hWnd
, 1), lParam
!= 0);
1139 case BFFM_SETOKTEXT
: /* unicode only */
1140 TRACE("Set OK text %s\n", debugstr_w((LPWSTR
)lParam
));
1141 SetWindowTextW(GetDlgItem(hWnd
, 1), (LPWSTR
)lParam
);
1144 case BFFM_SETSELECTIONA
:
1145 return BrsFolder_OnSetSelectionA(info
, (LPVOID
)lParam
, (BOOL
)wParam
);
1147 case BFFM_SETSELECTIONW
:
1148 return BrsFolder_OnSetSelectionW(info
, (LPVOID
)lParam
, (BOOL
)wParam
);
1150 case BFFM_SETEXPANDED
: /* unicode only */
1151 return BrsFolder_OnSetExpanded(info
, (LPVOID
)lParam
, (BOOL
)wParam
, NULL
);
1153 case SHV_CHANGE_NOTIFY
:
1154 return BrsFolder_OnChange(info
, (const LPCITEMIDLIST
*)wParam
, (LONG
)lParam
);
1157 return BrsFolder_OnDestroy(info
);
1162 /*************************************************************************
1163 * SHBrowseForFolderA [SHELL32.@]
1164 * SHBrowseForFolder [SHELL32.@]
1166 LPITEMIDLIST WINAPI
SHBrowseForFolderA (LPBROWSEINFOA lpbi
)
1173 TRACE("%p\n", lpbi
);
1175 bi
.hwndOwner
= lpbi
->hwndOwner
;
1176 bi
.pidlRoot
= lpbi
->pidlRoot
;
1177 if (lpbi
->pszDisplayName
)
1178 bi
.pszDisplayName
= heap_alloc( MAX_PATH
* sizeof(WCHAR
) );
1180 bi
.pszDisplayName
= NULL
;
1182 if (lpbi
->lpszTitle
)
1184 len
= MultiByteToWideChar( CP_ACP
, 0, lpbi
->lpszTitle
, -1, NULL
, 0 );
1185 title
= heap_alloc( len
* sizeof(WCHAR
) );
1186 MultiByteToWideChar( CP_ACP
, 0, lpbi
->lpszTitle
, -1, title
, len
);
1191 bi
.lpszTitle
= title
;
1192 bi
.ulFlags
= lpbi
->ulFlags
;
1193 bi
.lpfn
= lpbi
->lpfn
;
1194 bi
.lParam
= lpbi
->lParam
;
1195 bi
.iImage
= lpbi
->iImage
;
1196 lpid
= SHBrowseForFolderW( &bi
);
1197 if (bi
.pszDisplayName
)
1199 WideCharToMultiByte( CP_ACP
, 0, bi
.pszDisplayName
, -1,
1200 lpbi
->pszDisplayName
, MAX_PATH
, 0, NULL
);
1201 heap_free( bi
.pszDisplayName
);
1204 lpbi
->iImage
= bi
.iImage
;
1209 /*************************************************************************
1210 * SHBrowseForFolderW [SHELL32.@]
1213 * crashes when passed a null pointer
1215 LPITEMIDLIST WINAPI
SHBrowseForFolderW (LPBROWSEINFOW lpbi
)
1220 const WCHAR
* templateName
;
1221 INITCOMMONCONTROLSEX icex
;
1224 info
.pidlRet
= NULL
;
1225 info
.lpBrowseInfo
= lpbi
;
1226 info
.hwndTreeView
= NULL
;
1228 icex
.dwSize
= sizeof( icex
);
1229 icex
.dwICC
= ICC_TREEVIEW_CLASSES
;
1230 InitCommonControlsEx( &icex
);
1232 hr
= OleInitialize(NULL
);
1234 if (lpbi
->ulFlags
& BIF_NEWDIALOGSTYLE
)
1235 templateName
= L
"SHNEWBRSFORFOLDER_MSGBOX";
1237 templateName
= L
"SHBRSFORFOLDER_MSGBOX";
1238 r
= DialogBoxParamW( shell32_hInstance
, templateName
, lpbi
->hwndOwner
,
1239 BrsFolderDlgProc
, (LPARAM
)&info
);
1244 ILFree(info
.pidlRet
);
1248 return info
.pidlRet
;