[LT2013]
[reactos.git] / dll / win32 / shell32 / brsfolder.cpp
1 /*
2 * Copyright 1999 Juergen Schmied
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 *
18 * FIXME:
19 * - many memory leaks
20 * - many flags unimplemented
21 * - implement new dialog style "make new folder" button
22 * - implement editbox
23 * - implement new dialog style resizing
24 */
25
26 #include "precomp.h"
27
28
29 WINE_DEFAULT_DEBUG_CHANNEL(shell);
30
31 /* original margins and control size */
32 typedef struct tagLAYOUT_DATA
33 {
34 LONG left, width, right;
35 LONG top, height, bottom;
36 } LAYOUT_DATA;
37
38 typedef struct tagbrowse_info
39 {
40 HWND hWnd;
41 HWND hwndTreeView;
42 LPBROWSEINFOW lpBrowseInfo;
43 LPITEMIDLIST pidlRet;
44 LAYOUT_DATA *layout; /* filled by LayoutInit, used by LayoutUpdate */
45 SIZE szMin;
46 } browse_info;
47
48 typedef struct tagTV_ITEMDATA
49 {
50 LPSHELLFOLDER lpsfParent; /* IShellFolder of the parent */
51 LPITEMIDLIST lpi; /* PIDL relative to parent */
52 LPITEMIDLIST lpifq; /* Fully qualified PIDL */
53 IEnumIDList* pEnumIL; /* Children iterator */
54 } TV_ITEMDATA, *LPTV_ITEMDATA;
55
56 typedef struct tagLAYOUT_INFO
57 {
58 int iItemId; /* control id */
59 DWORD dwAnchor; /* BF_* flags specifying which margins should remain constant */
60 } LAYOUT_INFO;
61
62 static const LAYOUT_INFO g_layout_info[] =
63 {
64 {IDC_BROWSE_FOR_FOLDER_TITLE, BF_TOP|BF_LEFT|BF_RIGHT},
65 {IDC_BROWSE_FOR_FOLDER_STATUS, BF_TOP|BF_LEFT|BF_RIGHT},
66 {IDC_BROWSE_FOR_FOLDER_FOLDER, BF_TOP|BF_LEFT|BF_RIGHT},
67 {IDC_BROWSE_FOR_FOLDER_TREEVIEW, BF_TOP|BF_BOTTOM|BF_LEFT|BF_RIGHT},
68 {IDC_BROWSE_FOR_FOLDER_FOLDER, BF_BOTTOM|BF_LEFT},
69 {IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT, BF_BOTTOM|BF_LEFT|BF_RIGHT},
70 {IDC_BROWSE_FOR_FOLDER_NEW_FOLDER, BF_BOTTOM|BF_LEFT},
71 {IDOK, BF_BOTTOM|BF_RIGHT},
72 {IDCANCEL, BF_BOTTOM|BF_RIGHT}
73 };
74
75 #define LAYOUT_INFO_COUNT (sizeof(g_layout_info)/sizeof(g_layout_info[0]))
76
77 #define SUPPORTEDFLAGS (BIF_STATUSTEXT | \
78 BIF_BROWSEFORCOMPUTER | \
79 BIF_RETURNFSANCESTORS | \
80 BIF_RETURNONLYFSDIRS | \
81 BIF_NONEWFOLDERBUTTON | \
82 BIF_NEWDIALOGSTYLE | \
83 BIF_BROWSEINCLUDEFILES)
84
85 static void FillTreeView(browse_info*, LPSHELLFOLDER,
86 LPITEMIDLIST, HTREEITEM, IEnumIDList*);
87 static HTREEITEM InsertTreeViewItem( browse_info*, IShellFolder *,
88 LPCITEMIDLIST, LPCITEMIDLIST, IEnumIDList*, HTREEITEM);
89
90 static const WCHAR szBrowseFolderInfo[] = L"__WINE_BRSFOLDERDLG_INFO";
91
92 static DWORD __inline BrowseFlagsToSHCONTF(UINT ulFlags)
93 {
94 return SHCONTF_FOLDERS | (ulFlags & BIF_BROWSEINCLUDEFILES ? SHCONTF_NONFOLDERS : 0);
95 }
96
97 static void browsefolder_callback( LPBROWSEINFOW lpBrowseInfo, HWND hWnd,
98 UINT msg, LPARAM param )
99 {
100 if (!lpBrowseInfo->lpfn)
101 return;
102 lpBrowseInfo->lpfn( hWnd, msg, param, lpBrowseInfo->lParam );
103 }
104
105 static LAYOUT_DATA *LayoutInit(HWND hwnd, const LAYOUT_INFO *layout_info, int layout_count)
106 {
107 LAYOUT_DATA *data;
108 RECT rcWnd;
109 int i;
110
111 GetClientRect(hwnd, &rcWnd);
112 data = (LAYOUT_DATA *)SHAlloc(sizeof(LAYOUT_DATA)*layout_count);
113 for (i = 0; i < layout_count; i++)
114 {
115 RECT r;
116 HWND hItem = GetDlgItem(hwnd, layout_info[i].iItemId);
117
118 if (hItem == NULL)
119 ERR("Item %d not found\n", i);
120 GetWindowRect(hItem, &r);
121 MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT)&r, 2);
122
123 data[i].left = r.left;
124 data[i].right = rcWnd.right - r.right;
125 data[i].width = r.right - r.left;
126
127 data[i].top = r.top;
128 data[i].bottom = rcWnd.bottom - r.bottom;
129 data[i].height = r.bottom - r.top;
130 }
131 return data;
132 }
133
134 static void LayoutUpdate(HWND hwnd, LAYOUT_DATA *data, const LAYOUT_INFO *layout_info, int layout_count)
135 {
136 RECT rcWnd;
137 int i;
138
139 GetClientRect(hwnd, &rcWnd);
140 for (i = 0; i < layout_count; i++)
141 {
142 RECT r;
143 HWND hItem = GetDlgItem(hwnd, layout_info[i].iItemId);
144
145 GetWindowRect(hItem, &r);
146 MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT)&r, 2);
147
148 if (layout_info[i].dwAnchor & BF_RIGHT)
149 {
150 r.right = rcWnd.right - data[i].right;
151 if (!(layout_info[i].dwAnchor & BF_LEFT))
152 r.left = r.right - data[i].width;
153 }
154
155 if (layout_info[i].dwAnchor & BF_BOTTOM)
156 {
157 r.bottom = rcWnd.bottom - data[i].bottom;
158 if (!(layout_info[i].dwAnchor & BF_TOP))
159 r.top = r.bottom - data[i].height;
160 }
161
162 SetWindowPos(hItem, NULL, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOZORDER);
163 }
164 }
165
166
167 /******************************************************************************
168 * InitializeTreeView [Internal]
169 *
170 * Called from WM_INITDIALOG handler.
171 *
172 * PARAMS
173 * hwndParent [I] The BrowseForFolder dialog
174 * root [I] ITEMIDLIST of the root shell folder
175 */
176 static void InitializeTreeView( browse_info *info )
177 {
178 LPITEMIDLIST pidlParent, pidlChild;
179 HIMAGELIST hImageList;
180 HRESULT hr;
181 IShellFolder *lpsfParent, *lpsfRoot;
182 IEnumIDList * pEnumChildren = NULL;
183 HTREEITEM item;
184 DWORD flags;
185 LPCITEMIDLIST root = info->lpBrowseInfo->pidlRoot;
186
187 TRACE("%p\n", info );
188
189 Shell_GetImageLists(NULL, &hImageList);
190
191 if (hImageList)
192 SendMessageW( info->hwndTreeView, TVM_SETIMAGELIST, 0, (LPARAM)hImageList );
193
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:
197 *
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.
202 *
203 * If root is 'Desktop', then root's parent is also 'Desktop'.
204 */
205
206 pidlParent = ILClone(root);
207 ILRemoveLastID(pidlParent);
208 pidlChild = ILClone(ILFindLastID(root));
209
210 if (_ILIsDesktop(pidlParent)) {
211 hr = SHGetDesktopFolder(&lpsfParent);
212 } else {
213 IShellFolder *lpsfDesktop;
214 hr = SHGetDesktopFolder(&lpsfDesktop);
215 if (FAILED(hr)) {
216 WARN("SHGetDesktopFolder failed! hr = %08x\n", hr);
217 ILFree(pidlChild);
218 ILFree(pidlParent);
219 return;
220 }
221 hr = lpsfDesktop->BindToObject(pidlParent, 0, IID_IShellFolder, (LPVOID *)&lpsfParent);
222 lpsfDesktop->Release();
223 }
224
225 if (FAILED(hr)) {
226 WARN("Could not bind to parent shell folder! hr = %08x\n", hr);
227 ILFree(pidlChild);
228 ILFree(pidlParent);
229 return;
230 }
231
232 if (pidlChild && pidlChild->mkid.cb) {
233 hr = lpsfParent->BindToObject(pidlChild, 0, IID_IShellFolder, (LPVOID *)&lpsfRoot);
234 } else {
235 lpsfRoot = lpsfParent;
236 hr = lpsfParent->AddRef();
237 }
238
239 if (FAILED(hr)) {
240 WARN("Could not bind to root shell folder! hr = %08x\n", hr);
241 lpsfParent->Release();
242 ILFree(pidlChild);
243 ILFree(pidlParent);
244 return;
245 }
246
247 flags = BrowseFlagsToSHCONTF( info->lpBrowseInfo->ulFlags );
248 hr = lpsfRoot->EnumObjects(info->hWnd, flags, &pEnumChildren );
249 if (FAILED(hr)) {
250 WARN("Could not get child iterator! hr = %08x\n", hr);
251 lpsfParent->Release();
252 lpsfRoot->Release();
253 ILFree(pidlChild);
254 ILFree(pidlParent);
255 return;
256 }
257
258 SendMessageW( info->hwndTreeView, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT );
259 item = InsertTreeViewItem( info, lpsfParent, pidlChild,
260 pidlParent, pEnumChildren, TVI_ROOT );
261 SendMessageW( info->hwndTreeView, TVM_EXPAND, TVE_EXPAND, (LPARAM)item );
262
263 ILFree(pidlChild);
264 ILFree(pidlParent);
265 lpsfRoot->Release();
266 lpsfParent->Release();
267 }
268
269 static int GetIcon(LPCITEMIDLIST lpi, UINT uFlags)
270 {
271 SHFILEINFOW sfi;
272 SHGetFileInfoW((LPCWSTR)lpi, 0 ,&sfi, sizeof(SHFILEINFOW), uFlags);
273 return sfi.iIcon;
274 }
275
276 static void GetNormalAndSelectedIcons(LPITEMIDLIST lpifq, LPTVITEMW lpTV_ITEM)
277 {
278 LPITEMIDLIST pidlDesktop = NULL;
279 DWORD flags;
280
281 TRACE("%p %p\n",lpifq, lpTV_ITEM);
282
283 if (!lpifq)
284 {
285 pidlDesktop = _ILCreateDesktop();
286 lpifq = pidlDesktop;
287 }
288
289 flags = SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON;
290 lpTV_ITEM->iImage = GetIcon( lpifq, flags );
291
292 flags = SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_OPENICON;
293 lpTV_ITEM->iSelectedImage = GetIcon( lpifq, flags );
294
295 if (pidlDesktop)
296 ILFree( pidlDesktop );
297 }
298
299 /******************************************************************************
300 * GetName [Internal]
301 *
302 * Query a shell folder for the display name of one of it's children
303 *
304 * PARAMS
305 * lpsf [I] IShellFolder interface of the folder to be queried.
306 * lpi [I] ITEMIDLIST of the child, relative to parent
307 * dwFlags [I] as in IShellFolder::GetDisplayNameOf
308 * lpFriendlyName [O] The desired display name in unicode
309 *
310 * RETURNS
311 * Success: TRUE
312 * Failure: FALSE
313 */
314 static BOOL GetName(LPSHELLFOLDER lpsf, LPCITEMIDLIST lpi, DWORD dwFlags, LPWSTR lpFriendlyName)
315 {
316 BOOL bSuccess=TRUE;
317 STRRET str;
318
319 TRACE("%p %p %x %p\n", lpsf, lpi, dwFlags, lpFriendlyName);
320 if (SUCCEEDED(lpsf->GetDisplayNameOf(lpi, dwFlags, &str)))
321 bSuccess = StrRetToStrNW(lpFriendlyName, MAX_PATH, &str, lpi);
322 else
323 bSuccess = FALSE;
324
325 TRACE("-- %s\n", debugstr_w(lpFriendlyName));
326 return bSuccess;
327 }
328
329 /******************************************************************************
330 * InsertTreeViewItem [Internal]
331 *
332 * PARAMS
333 * info [I] data for the dialog
334 * lpsf [I] IShellFolder interface of the item's parent shell folder
335 * pidl [I] ITEMIDLIST of the child to insert, relative to parent
336 * pidlParent [I] ITEMIDLIST of the parent shell folder
337 * pEnumIL [I] Iterator for the children of the item to be inserted
338 * hParent [I] The treeview-item that represents the parent shell folder
339 *
340 * RETURNS
341 * Success: Handle to the created and inserted treeview-item
342 * Failure: NULL
343 */
344 static HTREEITEM InsertTreeViewItem( browse_info *info, IShellFolder * lpsf,
345 LPCITEMIDLIST pidl, LPCITEMIDLIST pidlParent, IEnumIDList* pEnumIL,
346 HTREEITEM hParent)
347 {
348 TVITEMW tvi;
349 TVINSERTSTRUCTW tvins;
350 WCHAR szBuff[MAX_PATH];
351 LPTV_ITEMDATA lptvid=0;
352
353 tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
354
355 tvi.cChildren= pEnumIL ? 1 : 0;
356 tvi.mask |= TVIF_CHILDREN;
357
358 lptvid = (TV_ITEMDATA *)SHAlloc( sizeof(TV_ITEMDATA) );
359 if (!lptvid)
360 return NULL;
361
362 if (!GetName(lpsf, pidl, SHGDN_NORMAL, szBuff))
363 return NULL;
364
365 tvi.pszText = szBuff;
366 tvi.cchTextMax = MAX_PATH;
367 tvi.lParam = (LPARAM)lptvid;
368
369 lpsf->AddRef();
370 lptvid->lpsfParent = lpsf;
371 lptvid->lpi = ILClone(pidl);
372 lptvid->lpifq = pidlParent ? ILCombine(pidlParent, pidl) : ILClone(pidl);
373 lptvid->pEnumIL = pEnumIL;
374 GetNormalAndSelectedIcons(lptvid->lpifq, &tvi);
375
376 tvins.item = tvi;
377 tvins.hInsertAfter = NULL;
378 tvins.hParent = hParent;
379
380 return TreeView_InsertItem( info->hwndTreeView, &tvins );
381 }
382
383 /******************************************************************************
384 * FillTreeView [Internal]
385 *
386 * For each child (given by lpe) of the parent shell folder, which is given by
387 * lpsf and whose PIDL is pidl, insert a treeview-item right under hParent
388 *
389 * PARAMS
390 * info [I] data for the dialog
391 * lpsf [I] IShellFolder interface of the parent shell folder
392 * pidl [I] ITEMIDLIST of the parent shell folder
393 * hParent [I] The treeview item that represents the parent shell folder
394 * lpe [I] An iterator for the children of the parent shell folder
395 */
396 static void FillTreeView( browse_info *info, IShellFolder * lpsf,
397 LPITEMIDLIST pidl, HTREEITEM hParent, IEnumIDList* lpe)
398 {
399 LPITEMIDLIST pidlTemp = 0;
400 ULONG ulFetched;
401 HRESULT hr;
402 HWND hwnd = GetParent( info->hwndTreeView );
403
404 TRACE("%p %p %p %p\n",lpsf, pidl, hParent, lpe);
405
406 /* No IEnumIDList -> No children */
407 if (!lpe) return;
408
409 SetCapture( hwnd );
410 SetCursor( LoadCursorA( 0, (LPSTR)IDC_WAIT ) );
411
412 while (S_OK == lpe->Next(1,&pidlTemp,&ulFetched))
413 {
414 ULONG ulAttrs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER;
415 IEnumIDList* pEnumIL = NULL;
416 IShellFolder* pSFChild = NULL;
417 lpsf->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlTemp, &ulAttrs);
418 if (ulAttrs & SFGAO_FOLDER)
419 {
420 hr = lpsf->BindToObject(pidlTemp, NULL, IID_IShellFolder, (LPVOID *)&pSFChild);
421 if (SUCCEEDED(hr))
422 {
423 DWORD flags = BrowseFlagsToSHCONTF(info->lpBrowseInfo->ulFlags);
424 hr = pSFChild->EnumObjects(hwnd, flags, &pEnumIL);
425 if (hr == S_OK)
426 {
427 if ((pEnumIL->Skip(1) != S_OK) ||
428 FAILED(pEnumIL->Reset()))
429 {
430 pEnumIL->Release();
431 pEnumIL = NULL;
432 }
433 }
434 pSFChild->Release();
435 }
436 }
437
438 if (!InsertTreeViewItem(info, lpsf, pidlTemp, pidl, pEnumIL, hParent))
439 goto done;
440 SHFree(pidlTemp); /* Finally, free the pidl that the shell gave us... */
441 pidlTemp=NULL;
442 }
443
444 done:
445 ReleaseCapture();
446 SetCursor(LoadCursorW(0, (LPWSTR)IDC_ARROW));
447 SHFree(pidlTemp);
448 }
449
450 static BOOL __inline PIDLIsType(LPCITEMIDLIST pidl, PIDLTYPE type)
451 {
452 LPPIDLDATA data = _ILGetDataPointer(pidl);
453 if (!data)
454 return FALSE;
455 return (data->type == type);
456 }
457
458 static void BrsFolder_CheckValidSelection( browse_info *info, LPTV_ITEMDATA lptvid )
459 {
460 LPBROWSEINFOW lpBrowseInfo = info->lpBrowseInfo;
461 LPCITEMIDLIST pidl = lptvid->lpi;
462 BOOL bEnabled = TRUE;
463 DWORD dwAttributes;
464 HRESULT r;
465
466 if ((lpBrowseInfo->ulFlags & BIF_BROWSEFORCOMPUTER) &&
467 !PIDLIsType(pidl, PT_COMP))
468 bEnabled = FALSE;
469 if (lpBrowseInfo->ulFlags & BIF_RETURNFSANCESTORS)
470 {
471 dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
472 r = lptvid->lpsfParent->GetAttributesOf(1,
473 (LPCITEMIDLIST*)&lptvid->lpi, &dwAttributes);
474 if (FAILED(r) || !(dwAttributes & (SFGAO_FILESYSANCESTOR|SFGAO_FILESYSTEM)))
475 bEnabled = FALSE;
476 }
477
478 dwAttributes = SFGAO_FOLDER | SFGAO_FILESYSTEM;
479 r = lptvid->lpsfParent->GetAttributesOf(1,
480 (LPCITEMIDLIST*)&lptvid->lpi,
481 &dwAttributes);
482 if (FAILED(r) ||
483 ((dwAttributes & (SFGAO_FOLDER|SFGAO_FILESYSTEM)) != (SFGAO_FOLDER|SFGAO_FILESYSTEM)))
484 {
485 if (lpBrowseInfo->ulFlags & BIF_RETURNONLYFSDIRS)
486 bEnabled = FALSE;
487 EnableWindow(GetDlgItem(info->hWnd, IDC_BROWSE_FOR_FOLDER_NEW_FOLDER), FALSE);
488 }
489 else
490 EnableWindow(GetDlgItem(info->hWnd, IDC_BROWSE_FOR_FOLDER_NEW_FOLDER), TRUE);
491
492 SendMessageW(info->hWnd, BFFM_ENABLEOK, 0, bEnabled);
493 }
494
495 static LRESULT BrsFolder_Treeview_Delete( browse_info *info, NMTREEVIEWW *pnmtv )
496 {
497 LPTV_ITEMDATA lptvid = (LPTV_ITEMDATA)pnmtv->itemOld.lParam;
498
499 TRACE("TVN_DELETEITEMA/W %p\n", lptvid);
500
501 lptvid->lpsfParent->Release();
502 if (lptvid->pEnumIL)
503 lptvid->pEnumIL->Release();
504 SHFree(lptvid->lpi);
505 SHFree(lptvid->lpifq);
506 SHFree(lptvid);
507 return 0;
508 }
509
510 static LRESULT BrsFolder_Treeview_Expand( browse_info *info, NMTREEVIEWW *pnmtv )
511 {
512 IShellFolder *lpsf2 = NULL;
513 LPTV_ITEMDATA lptvid = (LPTV_ITEMDATA) pnmtv->itemNew.lParam;
514 HRESULT r;
515
516 TRACE("TVN_ITEMEXPANDINGA/W\n");
517
518 if ((pnmtv->itemNew.state & TVIS_EXPANDEDONCE))
519 return 0;
520
521 if (lptvid->lpi && lptvid->lpi->mkid.cb) {
522 r = lptvid->lpsfParent->BindToObject(lptvid->lpi, 0,
523 IID_IShellFolder, (LPVOID *)&lpsf2 );
524 } else {
525 lpsf2 = lptvid->lpsfParent;
526 r = lpsf2->AddRef();
527 }
528
529 if (SUCCEEDED(r))
530 {
531 FillTreeView( info, lpsf2, lptvid->lpifq, pnmtv->itemNew.hItem, lptvid->pEnumIL);
532 lpsf2->Release();
533 }
534
535 /* My Computer is already sorted and trying to do a simple text
536 * sort will only mess things up */
537 if (!_ILIsMyComputer(lptvid->lpi))
538 SendMessageW( info->hwndTreeView, TVM_SORTCHILDREN,
539 FALSE, (LPARAM)pnmtv->itemNew.hItem );
540
541 return 0;
542 }
543
544 static HRESULT BrsFolder_Treeview_Changed( browse_info *info, NMTREEVIEWW *pnmtv )
545 {
546 LPTV_ITEMDATA lptvid = (LPTV_ITEMDATA) pnmtv->itemNew.lParam;
547 WCHAR name[MAX_PATH];
548
549 ILFree(info->pidlRet);
550 info->pidlRet = ILClone(lptvid->lpifq);
551
552 if (GetName(lptvid->lpsfParent, lptvid->lpi, SHGDN_NORMAL, name))
553 SetWindowTextW( GetDlgItem(info->hWnd, IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT), name );
554
555 browsefolder_callback( info->lpBrowseInfo, info->hWnd, BFFM_SELCHANGED,
556 (LPARAM)info->pidlRet );
557 BrsFolder_CheckValidSelection( info, lptvid );
558 return 0;
559 }
560
561 static LRESULT BrsFolder_Treeview_Rename(browse_info *info, NMTVDISPINFOW *pnmtv)
562 {
563 LPTV_ITEMDATA item_data;
564 WCHAR old_path[MAX_PATH], new_path[MAX_PATH], *p;
565 NMTREEVIEWW nmtv;
566 TVITEMW item;
567
568 if(!pnmtv->item.pszText)
569 return 0;
570
571 item.mask = TVIF_HANDLE|TVIF_PARAM;
572 item.hItem = (HTREEITEM)SendMessageW(info->hwndTreeView, TVM_GETNEXTITEM, TVGN_CARET, 0);
573 SendMessageW(info->hwndTreeView, TVM_GETITEMW, 0, (LPARAM)&item);
574 item_data = (LPTV_ITEMDATA)item.lParam;
575
576 SHGetPathFromIDListW(item_data->lpifq, old_path);
577 if(!(p = strrchrW(old_path, '\\')))
578 return 0;
579 p = new_path+(p-old_path+1);
580 memcpy(new_path, old_path, (p-new_path)*sizeof(WCHAR));
581 strcpyW(p, pnmtv->item.pszText);
582
583 if(!MoveFileW(old_path, new_path))
584 return 0;
585
586 SHFree(item_data->lpifq);
587 SHFree(item_data->lpi);
588 item_data->lpifq = SHSimpleIDListFromPathW(new_path);
589 item_data->lpsfParent->ParseDisplayName(NULL, NULL, pnmtv->item.pszText,
590 NULL, &item_data->lpi, NULL);
591
592 item.mask = TVIF_HANDLE|TVIF_TEXT;
593 item.pszText = pnmtv->item.pszText;
594 SendMessageW(info->hwndTreeView, TVM_SETITEMW, 0, (LPARAM)&item);
595
596 nmtv.itemNew.lParam = item.lParam;
597 BrsFolder_Treeview_Changed(info, &nmtv);
598 return 0;
599 }
600
601 static LRESULT BrsFolder_OnNotify( browse_info *info, UINT CtlID, LPNMHDR lpnmh )
602 {
603 NMTREEVIEWW *pnmtv = (NMTREEVIEWW *)lpnmh;
604
605 TRACE("%p %x %p msg=%x\n", info, CtlID, lpnmh, pnmtv->hdr.code);
606
607 if (pnmtv->hdr.idFrom != IDC_BROWSE_FOR_FOLDER_TREEVIEW)
608 return 0;
609
610 switch (pnmtv->hdr.code)
611 {
612 case TVN_DELETEITEMA:
613 case TVN_DELETEITEMW:
614 return BrsFolder_Treeview_Delete( info, pnmtv );
615
616 case TVN_ITEMEXPANDINGA:
617 case TVN_ITEMEXPANDINGW:
618 return BrsFolder_Treeview_Expand( info, pnmtv );
619
620 case TVN_SELCHANGEDA:
621 case TVN_SELCHANGEDW:
622 return BrsFolder_Treeview_Changed( info, pnmtv );
623
624 case TVN_ENDLABELEDITA:
625 case TVN_ENDLABELEDITW:
626 return BrsFolder_Treeview_Rename( info, (LPNMTVDISPINFOW)pnmtv );
627
628 default:
629 WARN("unhandled (%d)\n", pnmtv->hdr.code);
630 break;
631 }
632
633 return 0;
634 }
635
636
637 static BOOL BrsFolder_OnCreate( HWND hWnd, browse_info *info )
638 {
639 LPBROWSEINFOW lpBrowseInfo = info->lpBrowseInfo;
640
641 info->hWnd = hWnd;
642 SetPropW( hWnd, szBrowseFolderInfo, info );
643
644 if (lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE)
645 FIXME("flags BIF_NEWDIALOGSTYLE partially implemented\n");
646 if (lpBrowseInfo->ulFlags & ~SUPPORTEDFLAGS)
647 FIXME("flags %x not implemented\n", lpBrowseInfo->ulFlags & ~SUPPORTEDFLAGS);
648
649 if (lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE)
650 {
651 RECT rcWnd;
652
653 info->layout = LayoutInit(hWnd, g_layout_info, LAYOUT_INFO_COUNT);
654
655 /* TODO: Windows allows shrinking the windows a bit */
656 GetWindowRect(hWnd, &rcWnd);
657 info->szMin.cx = rcWnd.right - rcWnd.left;
658 info->szMin.cy = rcWnd.bottom - rcWnd.top;
659 }
660 else
661 {
662 info->layout = NULL;
663 }
664
665 if (lpBrowseInfo->lpszTitle)
666 SetWindowTextW( GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_TITLE), lpBrowseInfo->lpszTitle );
667 else
668 ShowWindow( GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_TITLE), SW_HIDE );
669
670 if (!(lpBrowseInfo->ulFlags & BIF_STATUSTEXT)
671 || (lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE))
672 ShowWindow( GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_STATUS), SW_HIDE );
673
674 /* Hide "Make New Folder" Button? */
675 if ((lpBrowseInfo->ulFlags & BIF_NONEWFOLDERBUTTON)
676 || !(lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE))
677 ShowWindow( GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_NEW_FOLDER), SW_HIDE );
678
679 /* Hide the editbox? */
680 if (!(lpBrowseInfo->ulFlags & BIF_EDITBOX))
681 {
682 ShowWindow( GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_FOLDER), SW_HIDE );
683 ShowWindow( GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT), SW_HIDE );
684 }
685
686 info->hwndTreeView = GetDlgItem( hWnd, IDC_BROWSE_FOR_FOLDER_TREEVIEW );
687 if (info->hwndTreeView)
688 {
689 InitializeTreeView( info );
690
691 /* Resize the treeview if there's not editbox */
692 if ((lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE)
693 && !(lpBrowseInfo->ulFlags & BIF_EDITBOX))
694 {
695 RECT rc;
696 GetClientRect(info->hwndTreeView, &rc);
697 SetWindowPos(info->hwndTreeView, HWND_TOP, 0, 0,
698 rc.right, rc.bottom + 40, SWP_NOMOVE);
699 }
700 }
701 else
702 ERR("treeview control missing!\n");
703
704 browsefolder_callback( info->lpBrowseInfo, hWnd, BFFM_INITIALIZED, 0 );
705
706 return TRUE;
707 }
708
709 static HRESULT BrsFolder_Rename(browse_info *info, HTREEITEM rename)
710 {
711 SendMessageW(info->hwndTreeView, TVM_SELECTITEM, TVGN_CARET, (LPARAM)rename);
712 SendMessageW(info->hwndTreeView, TVM_EDITLABELW, 0, (LPARAM)rename);
713 return S_OK;
714 }
715
716 static HRESULT BrsFolder_NewFolder(browse_info *info)
717 {
718 DWORD flags = BrowseFlagsToSHCONTF(info->lpBrowseInfo->ulFlags);
719 IShellFolder *desktop, *cur;
720 ISFHelper *sfhelper;
721 WCHAR name[MAX_PATH];
722 HTREEITEM parent, added;
723 LPTV_ITEMDATA item_data;
724 LPITEMIDLIST new_item;
725 TVITEMW item;
726 HRESULT hr;
727 int len;
728
729 if(!info->pidlRet) {
730 ERR("Make new folder button should be disabled\n");
731 return E_FAIL;
732 }
733
734 /* Create new directory */
735 hr = SHGetDesktopFolder(&desktop);
736 if(FAILED(hr))
737 return hr;
738 hr = desktop->BindToObject(info->pidlRet, 0, IID_IShellFolder, (LPVOID *)&cur);
739 desktop->Release();
740 if(FAILED(hr))
741 return hr;
742
743 hr = cur->QueryInterface(IID_ISFHelper, (LPVOID *)&sfhelper);
744 if(FAILED(hr))
745 return hr;
746
747 hr = SHGetPathFromIDListW(info->pidlRet, name);
748 if(FAILED(hr))
749 goto cleanup;
750
751 len = strlenW(name);
752 if(len<MAX_PATH)
753 name[len++] = '\\';
754 hr = sfhelper->GetUniqueName(&name[len], MAX_PATH-len);
755 sfhelper->Release();
756 if(FAILED(hr))
757 goto cleanup;
758
759 hr = E_FAIL;
760 if(!CreateDirectoryW(name, NULL))
761 goto cleanup;
762
763 /* Update parent of newly created directory */
764 parent = (HTREEITEM)SendMessageW(info->hwndTreeView, TVM_GETNEXTITEM, TVGN_CARET, 0);
765 if(!parent)
766 goto cleanup;
767
768 SendMessageW(info->hwndTreeView, TVM_EXPAND, TVE_EXPAND, (LPARAM)parent);
769
770 memset(&item, 0, sizeof(TVITEMW));
771 item.mask = TVIF_PARAM|TVIF_STATE;
772 item.hItem = parent;
773 SendMessageW(info->hwndTreeView, TVM_GETITEMW, 0, (LPARAM)&item);
774 item_data = (LPTV_ITEMDATA)item.lParam;
775 if(!item_data)
776 goto cleanup;
777
778 if(item_data->pEnumIL)
779 item_data->pEnumIL->Release();
780 hr = cur->EnumObjects(info->hwndTreeView, flags, &item_data->pEnumIL);
781 if(FAILED(hr))
782 goto cleanup;
783
784 /* Update treeview */
785 if(!(item.state&TVIS_EXPANDEDONCE)) {
786 item.mask = TVIF_STATE;
787 item.state = TVIS_EXPANDEDONCE;
788 item.stateMask = TVIS_EXPANDEDONCE;
789 SendMessageW(info->hwndTreeView, TVM_SETITEMW, 0, (LPARAM)&item);
790 }
791
792 hr = cur->ParseDisplayName(NULL, NULL, name+len, NULL, &new_item, NULL);
793 if(FAILED(hr))
794 goto cleanup;
795
796 added = InsertTreeViewItem(info, cur, new_item, item_data->lpifq, NULL, parent);
797 cur->Release();
798 SHFree(new_item);
799
800 SendMessageW(info->hwndTreeView, TVM_SORTCHILDREN, FALSE, (LPARAM)parent);
801 return BrsFolder_Rename(info, added);
802
803 cleanup:
804 return hr;
805 }
806
807 static BOOL BrsFolder_OnCommand( browse_info *info, UINT id )
808 {
809 LPBROWSEINFOW lpBrowseInfo = info->lpBrowseInfo;
810
811 switch (id)
812 {
813 case IDOK:
814 /* The original pidl is owned by the treeview and will be free'd. */
815 info->pidlRet = ILClone(info->pidlRet);
816 if (info->pidlRet == NULL) /* A null pidl would mean a cancel */
817 info->pidlRet = _ILCreateDesktop();
818 pdump( info->pidlRet );
819 if (lpBrowseInfo->pszDisplayName)
820 SHGetPathFromIDListW( info->pidlRet, lpBrowseInfo->pszDisplayName );
821 EndDialog( info->hWnd, 1 );
822 return TRUE;
823
824 case IDCANCEL:
825 EndDialog( info->hWnd, 0 );
826 return TRUE;
827
828 case IDC_BROWSE_FOR_FOLDER_NEW_FOLDER:
829 BrsFolder_NewFolder(info);
830 return TRUE;
831 }
832 return FALSE;
833 }
834
835 static BOOL BrsFolder_OnSetExpanded(browse_info *info, LPVOID selection,
836 BOOL is_str, HTREEITEM *pItem)
837 {
838 LPITEMIDLIST pidlSelection = (LPITEMIDLIST)selection;
839 LPCITEMIDLIST pidlCurrent, pidlRoot;
840 TVITEMEXW item;
841 BOOL bResult = FALSE;
842
843 memset(&item, 0, sizeof(item));
844
845 /* If 'selection' is a string, convert to a Shell ID List. */
846 if (is_str) {
847 IShellFolder *psfDesktop;
848 HRESULT hr;
849
850 hr = SHGetDesktopFolder(&psfDesktop);
851 if (FAILED(hr))
852 goto done;
853
854 hr = psfDesktop->ParseDisplayName(NULL, NULL, (LPOLESTR)selection,
855 NULL, &pidlSelection, NULL);
856 psfDesktop->Release();
857 if (FAILED(hr))
858 goto done;
859 }
860
861 /* Move pidlCurrent behind the SHITEMIDs in pidlSelection, which are the root of
862 * the sub-tree currently displayed. */
863 pidlRoot = info->lpBrowseInfo->pidlRoot;
864 pidlCurrent = pidlSelection;
865 while (!_ILIsEmpty(pidlRoot) && _ILIsEqualSimple(pidlRoot, pidlCurrent)) {
866 pidlRoot = ILGetNext(pidlRoot);
867 pidlCurrent = ILGetNext(pidlCurrent);
868 }
869
870 /* The given ID List is not part of the SHBrowseForFolder's current sub-tree. */
871 if (!_ILIsEmpty(pidlRoot))
872 goto done;
873
874 /* Initialize item to point to the first child of the root folder. */
875 item.mask = TVIF_PARAM;
876 item.hItem = (HTREEITEM)SendMessageW(info->hwndTreeView, TVM_GETNEXTITEM, TVGN_ROOT, 0);
877
878 if (item.hItem)
879 item.hItem = (HTREEITEM)SendMessageW(info->hwndTreeView, TVM_GETNEXTITEM, TVGN_CHILD,
880 (LPARAM)item.hItem);
881
882 /* Walk the tree along the nodes corresponding to the remaining ITEMIDLIST */
883 while (item.hItem && !_ILIsEmpty(pidlCurrent)) {
884 LPTV_ITEMDATA pItemData;
885
886 SendMessageW(info->hwndTreeView, TVM_GETITEMW, 0, (LPARAM)&item);
887 pItemData = (LPTV_ITEMDATA)item.lParam;
888
889 if (_ILIsEqualSimple(pItemData->lpi, pidlCurrent)) {
890 pidlCurrent = ILGetNext(pidlCurrent);
891 if (!_ILIsEmpty(pidlCurrent)) {
892 /* Only expand current node and move on to it's first child,
893 * if we didn't already reach the last SHITEMID */
894 SendMessageW(info->hwndTreeView, TVM_EXPAND, TVE_EXPAND, (LPARAM)item.hItem);
895 item.hItem = (HTREEITEM)SendMessageW(info->hwndTreeView, TVM_GETNEXTITEM, TVGN_CHILD,
896 (LPARAM)item.hItem);
897 }
898 } else {
899 item.hItem = (HTREEITEM)SendMessageW(info->hwndTreeView, TVM_GETNEXTITEM, TVGN_NEXT,
900 (LPARAM)item.hItem);
901 }
902 }
903
904 if (_ILIsEmpty(pidlCurrent) && item.hItem)
905 bResult = TRUE;
906
907 done:
908 if (pidlSelection && pidlSelection != (LPITEMIDLIST)selection)
909 ILFree(pidlSelection);
910
911 if (pItem)
912 *pItem = item.hItem;
913
914 return bResult;
915 }
916
917 static BOOL BrsFolder_OnSetSelectionW(browse_info *info, LPVOID selection, BOOL is_str) {
918 HTREEITEM hItem;
919 BOOL bResult;
920
921 if (!selection) return FALSE;
922
923 bResult = BrsFolder_OnSetExpanded(info, selection, is_str, &hItem);
924 if (bResult)
925 SendMessageW(info->hwndTreeView, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hItem );
926 return bResult;
927 }
928
929 static BOOL BrsFolder_OnSetSelectionA(browse_info *info, LPVOID selection, BOOL is_str) {
930 LPWSTR selectionW = NULL;
931 BOOL result = FALSE;
932 int length;
933
934 if (!is_str)
935 return BrsFolder_OnSetSelectionW(info, selection, is_str);
936
937 if ((length = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)selection, -1, NULL, 0)) &&
938 (selectionW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, length * sizeof(WCHAR))) &&
939 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)selection, -1, selectionW, length))
940 {
941 result = BrsFolder_OnSetSelectionW(info, selectionW, is_str);
942 }
943
944 HeapFree(GetProcessHeap(), 0, selectionW);
945 return result;
946 }
947
948 static BOOL BrsFolder_OnWindowPosChanging(browse_info *info, WINDOWPOS *pos)
949 {
950 if ((info->lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE) && !(pos->flags & SWP_NOSIZE))
951 {
952 if (pos->cx < info->szMin.cx)
953 pos->cx = info->szMin.cx;
954 if (pos->cy < info->szMin.cy)
955 pos->cy = info->szMin.cy;
956 }
957 return 0;
958 }
959
960 static INT BrsFolder_OnDestroy(browse_info *info)
961 {
962 if (info->layout)
963 {
964 SHFree(info->layout);
965 info->layout = NULL;
966 }
967
968 return 0;
969 }
970
971 /*************************************************************************
972 * BrsFolderDlgProc32 (not an exported API function)
973 */
974 static INT_PTR CALLBACK BrsFolderDlgProc( HWND hWnd, UINT msg, WPARAM wParam,
975 LPARAM lParam )
976 {
977 browse_info *info;
978
979 TRACE("hwnd=%p msg=%04x 0x%08lx 0x%08lx\n", hWnd, msg, wParam, lParam );
980
981 if (msg == WM_INITDIALOG)
982 return BrsFolder_OnCreate( hWnd, (browse_info*) lParam );
983
984 info = (browse_info*) GetPropW( hWnd, szBrowseFolderInfo );
985
986 switch (msg)
987 {
988 case WM_NOTIFY:
989 return BrsFolder_OnNotify( info, (UINT)wParam, (LPNMHDR)lParam);
990
991 case WM_COMMAND:
992 return BrsFolder_OnCommand( info, wParam );
993
994 case WM_WINDOWPOSCHANGING:
995 return BrsFolder_OnWindowPosChanging( info, (WINDOWPOS *)lParam);
996
997 case WM_SIZE:
998 if (info->layout) /* new style dialogs */
999 LayoutUpdate(hWnd, info->layout, g_layout_info, LAYOUT_INFO_COUNT);
1000 return 0;
1001
1002 case BFFM_SETSTATUSTEXTA:
1003 TRACE("Set status %s\n", debugstr_a((LPSTR)lParam));
1004 SetWindowTextA(GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_STATUS), (LPSTR)lParam);
1005 break;
1006
1007 case BFFM_SETSTATUSTEXTW:
1008 TRACE("Set status %s\n", debugstr_w((LPWSTR)lParam));
1009 SetWindowTextW(GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_STATUS), (LPWSTR)lParam);
1010 break;
1011
1012 case BFFM_ENABLEOK:
1013 TRACE("Enable %ld\n", lParam);
1014 EnableWindow(GetDlgItem(hWnd, 1), (lParam)?TRUE:FALSE);
1015 break;
1016
1017 case BFFM_SETOKTEXT: /* unicode only */
1018 TRACE("Set OK text %s\n", debugstr_w((LPWSTR)wParam));
1019 SetWindowTextW(GetDlgItem(hWnd, 1), (LPWSTR)wParam);
1020 break;
1021
1022 case BFFM_SETSELECTIONA:
1023 return BrsFolder_OnSetSelectionA(info, (LPVOID)lParam, (BOOL)wParam);
1024
1025 case BFFM_SETSELECTIONW:
1026 return BrsFolder_OnSetSelectionW(info, (LPVOID)lParam, (BOOL)wParam);
1027
1028 case BFFM_SETEXPANDED: /* unicode only */
1029 return BrsFolder_OnSetExpanded(info, (LPVOID)lParam, (BOOL)wParam, NULL);
1030
1031 case WM_DESTROY:
1032 return BrsFolder_OnDestroy(info);
1033 }
1034 return FALSE;
1035 }
1036
1037
1038
1039
1040 /*************************************************************************
1041 * SHBrowseForFolderA [SHELL32.@]
1042 * SHBrowseForFolder [SHELL32.@]
1043 */
1044 LPITEMIDLIST WINAPI SHBrowseForFolderA (LPBROWSEINFOA lpbi)
1045 {
1046 BROWSEINFOW bi;
1047 LPITEMIDLIST lpid;
1048 INT len;
1049 LPWSTR title;
1050
1051 TRACE("%p\n", lpbi);
1052
1053 bi.hwndOwner = lpbi->hwndOwner;
1054 bi.pidlRoot = lpbi->pidlRoot;
1055 if (lpbi->pszDisplayName)
1056 {
1057 bi.pszDisplayName = (WCHAR *)HeapAlloc( GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR) );
1058 MultiByteToWideChar( CP_ACP, 0, lpbi->pszDisplayName, -1, bi.pszDisplayName, MAX_PATH );
1059 }
1060 else
1061 bi.pszDisplayName = NULL;
1062
1063 if (lpbi->lpszTitle)
1064 {
1065 len = MultiByteToWideChar( CP_ACP, 0, lpbi->lpszTitle, -1, NULL, 0 );
1066 title = (WCHAR *)HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1067 MultiByteToWideChar( CP_ACP, 0, lpbi->lpszTitle, -1, title, len );
1068 }
1069 else
1070 title = NULL;
1071
1072 bi.lpszTitle = title;
1073 bi.ulFlags = lpbi->ulFlags;
1074 bi.lpfn = lpbi->lpfn;
1075 bi.lParam = lpbi->lParam;
1076 bi.iImage = lpbi->iImage;
1077 lpid = SHBrowseForFolderW( &bi );
1078 if (bi.pszDisplayName)
1079 {
1080 WideCharToMultiByte( CP_ACP, 0, bi.pszDisplayName, -1,
1081 lpbi->pszDisplayName, MAX_PATH, 0, NULL);
1082 HeapFree( GetProcessHeap(), 0, bi.pszDisplayName );
1083 }
1084 HeapFree(GetProcessHeap(), 0, title);
1085 lpbi->iImage = bi.iImage;
1086 return lpid;
1087 }
1088
1089
1090 /*************************************************************************
1091 * SHBrowseForFolderW [SHELL32.@]
1092 *
1093 * NOTES
1094 * crashes when passed a null pointer
1095 */
1096 LPITEMIDLIST WINAPI SHBrowseForFolderW (LPBROWSEINFOW lpbi)
1097 {
1098 browse_info info;
1099 DWORD r;
1100 HRESULT hr;
1101 WORD wDlgId;
1102
1103 info.hWnd = 0;
1104 info.pidlRet = NULL;
1105 info.lpBrowseInfo = lpbi;
1106 info.hwndTreeView = NULL;
1107
1108 hr = OleInitialize(NULL);
1109
1110 if (lpbi->ulFlags & BIF_NEWDIALOGSTYLE)
1111 wDlgId = IDD_BROWSE_FOR_FOLDER_NEW;
1112 else
1113 wDlgId = IDD_BROWSE_FOR_FOLDER;
1114 r = DialogBoxParamW( shell32_hInstance, MAKEINTRESOURCEW(wDlgId), lpbi->hwndOwner,
1115 BrsFolderDlgProc, (LPARAM)&info );
1116 if (SUCCEEDED(hr))
1117 OleUninitialize();
1118 if (!r)
1119 {
1120 ILFree(info.pidlRet);
1121 return NULL;
1122 }
1123
1124 return info.pidlRet;
1125 }