shfldr_fs.c 60.6 KB
Newer Older
1 2

/*
3
 * file system folder
4
 *
5 6
 * Copyright 1997             Marcus Meissner
 * Copyright 1998, 1999, 2002 Juergen Schmied
7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 22 23 24
 */

#include <stdlib.h>
#include <string.h>
25
#include <stdarg.h>
26 27
#include <stdio.h>

28
#define COBJMACROS
29
#include "winerror.h"
30
#include "windef.h"
31 32
#include "winbase.h"
#include "winreg.h"
33
#include "wingdi.h"
34
#include "winuser.h"
35

36
#include "ole2.h"
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
#include "shlguid.h"

#include "pidl.h"
#include "shell32_main.h"
#include "shresdef.h"
#include "shlwapi.h"
#include "shellfolder.h"
#include "wine/debug.h"
#include "debughlp.h"
#include "shfldr.h"

WINE_DEFAULT_DEBUG_CHANNEL (shell);

/***********************************************************************
*   IShellFolder implementation
*/

typedef struct {
55 56
    IUnknown IUnknown_inner;
    LONG ref;
57
    IShellFolder2 IShellFolder2_iface;
58
    IPersistFolder3 IPersistFolder3_iface;
59
    IPersistPropertyBag IPersistPropertyBag_iface;
60 61
    IDropTarget IDropTarget_iface;
    ISFHelper ISFHelper_iface;
62
    IUnknown *outer_unk;
63

64
    const CLSID *pclsid;
65 66

    /* both paths are parsible from the desktop */
67
    LPWSTR sPathTarget;     /* complete path to target used for enumeration and ChangeNotify */
68

69
    LPITEMIDLIST pidlRoot; /* absolute pidl */
70
    DWORD drop_effects_mask;
71 72
} IGenericSFImpl;

73 74
static UINT cfShellIDList;

75 76 77 78 79
static inline IGenericSFImpl *impl_from_IUnknown(IUnknown *iface)
{
    return CONTAINING_RECORD(iface, IGenericSFImpl, IUnknown_inner);
}

80
static inline IGenericSFImpl *impl_from_IShellFolder2(IShellFolder2 *iface)
81
{
82
    return CONTAINING_RECORD(iface, IGenericSFImpl, IShellFolder2_iface);
83
}
84

85
static inline IGenericSFImpl *impl_from_IPersistFolder3(IPersistFolder3 *iface)
86
{
87
    return CONTAINING_RECORD(iface, IGenericSFImpl, IPersistFolder3_iface);
88
}
89

90 91 92 93 94
static inline IGenericSFImpl *impl_from_IPersistPropertyBag(IPersistPropertyBag *iface)
{
    return CONTAINING_RECORD(iface, IGenericSFImpl, IPersistPropertyBag_iface);
}

95
static inline IGenericSFImpl *impl_from_IDropTarget(IDropTarget *iface)
96
{
97
    return CONTAINING_RECORD(iface, IGenericSFImpl, IDropTarget_iface);
98
}
99

100
static inline IGenericSFImpl *impl_from_ISFHelper(ISFHelper *iface)
101
{
102
    return CONTAINING_RECORD(iface, IGenericSFImpl, ISFHelper_iface);
103
}
104 105

/**************************************************************************
106
* inner IUnknown
107
*/
108
static HRESULT WINAPI IUnknown_fnQueryInterface(IUnknown *iface, REFIID riid, void **ppvObj)
109
{
110
    IGenericSFImpl *This = impl_from_IUnknown(iface);
111

112
    TRACE("(%p)->(%s,%p)\n", This, shdebugstr_guid(riid), ppvObj);
113 114 115 116

    *ppvObj = NULL;

    if (IsEqualIID (riid, &IID_IUnknown))
117
        *ppvObj = &This->IUnknown_inner;
118 119
    else if (IsEqualIID(riid, &IID_IShellFolder) || IsEqualIID(riid, &IID_IShellFolder2))
        *ppvObj = &This->IShellFolder2_iface;
120 121 122
    else if (IsEqualIID(riid, &IID_IPersist) || IsEqualIID(riid, &IID_IPersistFolder) ||
            IsEqualIID(riid, &IID_IPersistFolder2) || IsEqualIID(riid, &IID_IPersistFolder3))
        *ppvObj = &This->IPersistFolder3_iface;
123 124
    else if (IsEqualIID(&IID_IPersistPropertyBag, riid))
        *ppvObj = &This->IPersistPropertyBag_iface;
125
    else if (IsEqualIID (riid, &IID_ISFHelper))
126
        *ppvObj = &This->ISFHelper_iface;
127
    else if (IsEqualIID (riid, &IID_IDropTarget)) {
128
        *ppvObj = &This->IDropTarget_iface;
129
        if (!cfShellIDList) cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLISTW);
130 131 132
    }

    if (*ppvObj) {
133
        IUnknown_AddRef((IUnknown *)*ppvObj);
134 135
        TRACE ("-- Interface = %p\n", *ppvObj);
        return S_OK;
136 137 138 139 140
    }
    TRACE ("-- Interface: E_NOINTERFACE\n");
    return E_NOINTERFACE;
}

141
static ULONG WINAPI IUnknown_fnAddRef(IUnknown *iface)
142
{
143 144
    IGenericSFImpl *This = impl_from_IUnknown(iface);
    ULONG ref = InterlockedIncrement(&This->ref);
145

146
    TRACE("(%p) ref=%ld\n", This, ref);
147

148
    return ref;
149 150
}

151
static ULONG WINAPI IUnknown_fnRelease(IUnknown *iface)
152
{
153 154
    IGenericSFImpl *This = impl_from_IUnknown(iface);
    ULONG ref = InterlockedDecrement(&This->ref);
155

156
    TRACE("(%p) ref=%ld\n", This, ref);
157

158 159
    if (!ref) {
        TRACE("-- destroying IShellFolder(%p)\n", This);
160

161 162 163
        SHFree(This->pidlRoot);
        SHFree(This->sPathTarget);
        LocalFree(This);
164
    }
165
    return ref;
166 167
}

168
static const IUnknownVtbl unkvt =
169
{
170
      IUnknown_fnQueryInterface,
171 172 173 174
      IUnknown_fnAddRef,
      IUnknown_fnRelease,
};

175 176 177 178 179 180 181
static const shvheader GenericSFHeader[] =
{
    { &FMTID_Storage, PID_STG_NAME,        IDS_SHV_COLUMN1, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,  LVCFMT_RIGHT, 15 },
    { &FMTID_Storage, PID_STG_SIZE,        IDS_SHV_COLUMN2, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,  LVCFMT_RIGHT, 10 },
    { &FMTID_Storage, PID_STG_STORAGETYPE, IDS_SHV_COLUMN3, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,  LVCFMT_RIGHT, 10 },
    { &FMTID_Storage, PID_STG_WRITETIME,   IDS_SHV_COLUMN4, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 12 },
    { &FMTID_Storage, PID_STG_ATTRIBUTES,  IDS_SHV_COLUMN5, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,  LVCFMT_RIGHT, 5  },
182 183 184 185 186 187 188
};

#define GENERICSHELLVIEWCOLUMNS 5

/**************************************************************************
 *  IShellFolder_fnQueryInterface
 */
189 190
static HRESULT WINAPI IShellFolder_fnQueryInterface(IShellFolder2 *iface, REFIID riid,
        void **ppvObj)
191
{
192
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
193

194
    return IUnknown_QueryInterface(This->outer_unk, riid, ppvObj);
195 196 197 198 199
}

/**************************************************************************
*  IShellFolder_AddRef
*/
200
static ULONG WINAPI IShellFolder_fnAddRef(IShellFolder2 *iface)
201
{
202
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
203

204
    return IUnknown_AddRef(This->outer_unk);
205 206 207 208 209
}

/**************************************************************************
 *  IShellFolder_fnRelease
 */
210
static ULONG WINAPI IShellFolder_fnRelease(IShellFolder2 *iface)
211
{
212
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
213

214
    return IUnknown_Release(This->outer_unk);
215 216
}

217 218 219 220 221
/**************************************************************************
 *  SHELL32_CreatePidlFromBindCtx  [internal]
 *
 *  If the caller bound File System Bind Data, assume it is the 
 *   find data for the path.
222
 *  This allows binding of paths that don't exist.
223 224 225 226 227
 */
LPITEMIDLIST SHELL32_CreatePidlFromBindCtx(IBindCtx *pbc, LPCWSTR path)
{
    IFileSystemBindData *fsbd = NULL;
    LPITEMIDLIST pidl = NULL;
228
    IUnknown *unk = NULL;
229 230 231 232 233 234 235 236
    HRESULT r;

    TRACE("%p %s\n", pbc, debugstr_w(path));

    if (!pbc)
        return NULL;

    /* see if the caller bound File System Bind Data */
237
    r = IBindCtx_GetObjectParam( pbc, (WCHAR *)L"File System Bind Data", &unk );
238 239 240
    if (FAILED(r))
        return NULL;

241
    r = IUnknown_QueryInterface( unk, &IID_IFileSystemBindData, (void**)&fsbd );
242 243
    if (SUCCEEDED(r))
    {
244 245
        WIN32_FIND_DATAW wfd;

246 247 248 249 250 251 252 253
        r = IFileSystemBindData_GetFindData( fsbd, &wfd );
        if (SUCCEEDED(r))
        {
            lstrcpynW( &wfd.cFileName[0], path, MAX_PATH );
            pidl = _ILCreateFromFindDataW( &wfd );
        }
        IFileSystemBindData_Release( fsbd );
    }
254
    IUnknown_Release( unk );
255

256 257 258
    return pidl;
}

259
/**************************************************************************
Jon Griffiths's avatar
Jon Griffiths committed
260 261 262 263 264 265
* IShellFolder_ParseDisplayName {SHELL32}
*
* Parse a display name.
*
* PARAMS
*  hwndOwner       [in]  Parent window for any message's
266
*  pbc             [in]  optional FileSystemBindData context
Jon Griffiths's avatar
Jon Griffiths committed
267 268 269 270
*  lpszDisplayName [in]  Unicode displayname.
*  pchEaten        [out] (unicode) characters processed
*  ppidl           [out] complex pidl to item
*  pdwAttributes   [out] items attributes
271 272
*
* NOTES
Jon Griffiths's avatar
Jon Griffiths committed
273 274 275
*  Every folder tries to parse only its own (the leftmost) pidl and creates a
*  subfolder to evaluate the remaining parts.
*  Now we can parse into namespaces implemented by shell extensions
276
*
277
*  Behaviour on win98: lpszDisplayName=NULL -> crash
278
*                      lpszDisplayName="" -> returns mycomputer-pidl
279
*
Jon Griffiths's avatar
Jon Griffiths committed
280 281 282
* FIXME
*    pdwAttributes is not set
*    pchEaten is not set like in windows
283 284 285
*/
static HRESULT WINAPI
IShellFolder_fnParseDisplayName (IShellFolder2 * iface,
286 287 288 289 290
                                 HWND hwndOwner,
                                 LPBC pbc,
                                 LPOLESTR lpszDisplayName,
                                 DWORD * pchEaten, LPITEMIDLIST * ppidl,
                                 DWORD * pdwAttributes)
291
{
292
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
293

294
    HRESULT hr = S_OK;
295
    LPCWSTR szNext = NULL;
296
    WCHAR *p, szPath[MAX_PATH];
297 298
    WIN32_FIND_DATAW find_data = { 0 };
    IFileSystemBindData *fsbd = NULL;
299
    LPITEMIDLIST pidlTemp = NULL;
300
    DWORD len;
301

302
    TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
303 304
     This, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
     pchEaten, ppidl, pdwAttributes);
305

306
    if (!lpszDisplayName || !lpszDisplayName[0] || !ppidl) return E_INVALIDARG;
307 308

    if (pchEaten)
309
        *pchEaten = 0; /* strange but like the original */
310

311
    if (pbc)
312
    {
313 314 315
        IUnknown *unk;

        /* see if the caller bound File System Bind Data */
316
        if (SUCCEEDED( IBindCtx_GetObjectParam( pbc, (WCHAR *)L"File System Bind Data", &unk )))
317 318 319 320 321
        {
            IUnknown_QueryInterface( unk, &IID_IFileSystemBindData, (void**)&fsbd );
            IUnknown_Release( unk );
        }
    }
322

323 324
    if (*lpszDisplayName)
    {
325
        /* build the full pathname to the element */
326
        lstrcpynW(szPath, This->sPathTarget, MAX_PATH - 1);
327 328
        PathAddBackslashW(szPath);
        len = lstrlenW(szPath);
329 330 331
        /* get the next element */
        szNext = GetNextElementW( lpszDisplayName, szPath + len, MAX_PATH - len );

332 333 334 335 336
        if (IsEqualCLSID( This->pclsid, &CLSID_UnixFolder ) && lpszDisplayName[0] == '/')
        {
            lstrcpynW( szPath + len, lpszDisplayName + 1, MAX_PATH - len );
            for (p = szPath + len; *p; p++) if (*p == '/') *p = '\\';
        }
337
        else if (!wcsnicmp( lpszDisplayName, L"\\\\?\\unix\\", 9 ))
338 339
        {
            lstrcpynW( szPath + len, lpszDisplayName + 9, MAX_PATH - len );
340
            if ((p = wcschr( szPath + len, '\\' )))
341 342 343 344 345
                while (*p == '\\') *p++ = 0;
            szNext = p;
        }

        /* Special case for the root folder. */
346
        if (!wcsicmp( szPath, L"\\\\?\\unix\\" ))
347
        {
348
            *ppidl = SHAlloc(sizeof(**ppidl));
349 350 351 352 353 354 355
            if (!*ppidl) return E_FAIL;
            (*ppidl)->mkid.cb = 0; /* Terminate the ITEMIDLIST */
            return S_OK;
        }

        PathRemoveBackslashW( szPath );

356 357 358 359 360 361
        if (szNext && *szNext)
        {
            hr = _ILCreateFromPathW( szPath, &pidlTemp );
            if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) && fsbd)
            {
                find_data.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
362
                lstrcpyW( find_data.cFileName, szPath + len );
363
                pidlTemp = _ILCreateFromFindDataW( &find_data );
364
            }
365 366 367 368 369 370 371 372 373 374
            if (pidlTemp) /* try to analyse the next element */
                hr = SHELL32_ParseNextElement( iface, hwndOwner, pbc, &pidlTemp,
                                               (WCHAR *)szNext, pchEaten, pdwAttributes );
        }
        else  /* it's the last element */
        {
            if (fsbd)
            {
                if (FAILED( IFileSystemBindData_GetFindData( fsbd, &find_data )))
                    find_data.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
375
                lstrcpyW( find_data.cFileName, szPath + len );
376 377 378 379 380 381
                pidlTemp = _ILCreateFromFindDataW( &find_data );
            }
            else hr = _ILCreateFromPathW(szPath, &pidlTemp);

            if (pidlTemp && pdwAttributes && *pdwAttributes)
                hr = SHELL32_GetItemAttributes(&This->IShellFolder2_iface, pidlTemp, pdwAttributes);
382
        }
383 384
    }

385
    if (SUCCEEDED(hr))
386
        *ppidl = pidlTemp;
387
    else
388
        *ppidl = NULL;
389

390
    TRACE ("(%p)->(-- pidl=%p ret=0x%08lx)\n", This, *ppidl, hr);
391

392
    if (fsbd) IFileSystemBindData_Release( fsbd );
393 394 395 396
    return hr;
}

/**************************************************************************
397
* IShellFolder_fnEnumObjects
398 399 400 401 402 403
* PARAMETERS
*  HWND          hwndOwner,    //[in ] Parent Window
*  DWORD         grfFlags,     //[in ] SHCONTF enumeration mask
*  LPENUMIDLIST* ppenumIDList  //[out] IEnumIDList interface
*/
static HRESULT WINAPI
404 405
IShellFolder_fnEnumObjects (IShellFolder2 * iface, HWND hwndOwner,
                            DWORD dwFlags, LPENUMIDLIST * ppEnumIDList)
406
{
407
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
408
    IEnumIDListImpl *list;
409

410
    TRACE ("(%p)->(HWND=%p flags=0x%08lx pplist=%p)\n", This, hwndOwner,
411
     dwFlags, ppEnumIDList);
412

413 414
    if (!(list = IEnumIDList_Constructor()))
        return E_OUTOFMEMORY;
415
    CreateFolderEnumList(list, This->sPathTarget, dwFlags);
416
    *ppEnumIDList = &list->IEnumIDList_iface;
417 418 419

    TRACE ("-- (%p)->(new ID List: %p)\n", This, *ppEnumIDList);

420
    return S_OK;
421 422 423
}

/**************************************************************************
424
* IShellFolder_fnBindToObject
425 426
* PARAMETERS
*  LPCITEMIDLIST pidl,       //[in ] relative pidl to open
427
*  LPBC          pbc,        //[in ] optional FileSystemBindData context
428 429 430 431
*  REFIID        riid,       //[in ] Initial Interface
*  LPVOID*       ppvObject   //[out] Interface*
*/
static HRESULT WINAPI
432 433
IShellFolder_fnBindToObject (IShellFolder2 * iface, LPCITEMIDLIST pidl,
                             LPBC pbc, REFIID riid, LPVOID * ppvOut)
434
{
435
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
436
    const CLSID *clsid = This->pclsid;
437

438 439
    TRACE ("(%p)->(pidl=%p,%p,%s,%p)\n", This, pidl, pbc,
     shdebugstr_guid (riid), ppvOut);
440

441 442 443 444
    if (!IsEqualCLSID( clsid, &CLSID_UnixFolder ) && !IsEqualCLSID( clsid, &CLSID_UnixDosFolder ))
        clsid = &CLSID_ShellFSFolder;

    return SHELL32_BindToChild (This->pidlRoot, clsid, This->sPathTarget, pidl, riid, ppvOut);
445 446 447 448 449 450 451 452 453 454 455
}

/**************************************************************************
*  IShellFolder_fnBindToStorage
* PARAMETERS
*  LPCITEMIDLIST pidl,       //[in ] complex pidl to store
*  LPBC          pbc,        //[in ] reserved
*  REFIID        riid,       //[in ] Initial storage interface
*  LPVOID*       ppvObject   //[out] Interface* returned
*/
static HRESULT WINAPI
456 457
IShellFolder_fnBindToStorage (IShellFolder2 * iface, LPCITEMIDLIST pidl,
                              LPBC pbcReserved, REFIID riid, LPVOID * ppvOut)
458
{
459
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
460

461 462
    FIXME ("(%p)->(pidl=%p,%p,%s,%p) stub\n", This, pidl, pbcReserved,
     shdebugstr_guid (riid), ppvOut);
463 464 465 466 467 468 469 470 471 472

    *ppvOut = NULL;
    return E_NOTIMPL;
}

/**************************************************************************
*  IShellFolder_fnCompareIDs
*/

static HRESULT WINAPI
473 474
IShellFolder_fnCompareIDs (IShellFolder2 * iface, LPARAM lParam,
                           LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
475
{
476
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
477 478 479

    int nReturn;

480
    TRACE ("(%p)->(0x%08Ix,pidl1=%p,pidl2=%p)\n", This, lParam, pidl1, pidl2);
481
    nReturn = SHELL32_CompareIDs(&This->IShellFolder2_iface, lParam, pidl1, pidl2);
482 483 484 485 486
    TRACE ("-- %i\n", nReturn);
    return nReturn;
}

/**************************************************************************
487
* IShellFolder_fnCreateViewObject
488 489
*/
static HRESULT WINAPI
490 491
IShellFolder_fnCreateViewObject (IShellFolder2 * iface, HWND hwndOwner,
                                 REFIID riid, LPVOID * ppvOut)
492
{
493
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
494 495 496 497

    LPSHELLVIEW pShellView;
    HRESULT hr = E_INVALIDARG;

498 499
    TRACE ("(%p)->(hwnd=%p,%s,%p)\n", This, hwndOwner, shdebugstr_guid (riid),
     ppvOut);
500 501

    if (ppvOut) {
502 503 504
        *ppvOut = NULL;

        if (IsEqualIID (riid, &IID_IDropTarget)) {
505
            hr = IShellFolder2_QueryInterface (iface, &IID_IDropTarget, ppvOut);
506
        } else if (IsEqualIID (riid, &IID_IContextMenu)) {
507
            hr = BackgroundMenu_Constructor((IShellFolder*)iface, FALSE, riid, ppvOut);
508 509 510 511 512 513 514
        } else if (IsEqualIID (riid, &IID_IShellView)) {
            pShellView = IShellView_Constructor ((IShellFolder *) iface);
            if (pShellView) {
                hr = IShellView_QueryInterface (pShellView, riid, ppvOut);
                IShellView_Release (pShellView);
            }
        }
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
    }
    TRACE ("-- (%p)->(interface=%p)\n", This, ppvOut);
    return hr;
}

/**************************************************************************
*  IShellFolder_fnGetAttributesOf
*
* PARAMETERS
*  UINT            cidl,     //[in ] num elements in pidl array
*  LPCITEMIDLIST*  apidl,    //[in ] simple pidl array
*  ULONG*          rgfInOut) //[out] result array
*
*/
static HRESULT WINAPI
530 531
IShellFolder_fnGetAttributesOf (IShellFolder2 * iface, UINT cidl,
                                LPCITEMIDLIST * apidl, DWORD * rgfInOut)
532
{
533
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
534 535 536

    HRESULT hr = S_OK;

537
    TRACE ("(%p)->(cidl=%d apidl=%p mask=%p (0x%08lx))\n", This, cidl, apidl,
538
     rgfInOut, rgfInOut ? *rgfInOut : 0);
539

540 541 542
    if (!rgfInOut)
        return E_INVALIDARG;
    if (cidl && !apidl)
543
        return E_INVALIDARG;
544

545
    if (*rgfInOut == 0)
546
        *rgfInOut = ~0;
547

548
    if(cidl == 0){
549
        IShellFolder2 *parent = NULL;
550 551
        LPCITEMIDLIST rpidl = NULL;

552 553 554 555 556 557 558 559 560 561 562 563
        if (_ILIsSpecialFolder(This->pidlRoot))
        {
            *rgfInOut &= (SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR |
                          SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANRENAME);
        }
        else
        {
            hr = SHBindToParent(This->pidlRoot, &IID_IShellFolder2, (void **)&parent, &rpidl);
            if(SUCCEEDED(hr)) {
                SHELL32_GetItemAttributes(parent, rpidl, rgfInOut);
                IShellFolder2_Release(parent);
            }
564
        }
565 566 567 568
    }
    else {
        while (cidl > 0 && *apidl) {
            pdump (*apidl);
569
            SHELL32_GetItemAttributes(&This->IShellFolder2_iface, *apidl, rgfInOut);
570 571 572
            apidl++;
            cidl--;
        }
573
    }
574 575
    /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
    *rgfInOut &= ~SFGAO_VALIDATE;
576

577
    TRACE ("-- result=0x%08lx\n", *rgfInOut);
578 579 580 581

    return hr;
}

582 583 584
/**************************************************************************
 * SHELL32_CreateExtensionUIObject (internal)
 */
585
static HRESULT SHELL32_CreateExtensionUIObject(IShellFolder2 *iface,
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
        LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut)
{
    IPersistFile *persist_file;
    char extensionA[20];
    WCHAR extensionW[20], buf[MAX_PATH];
    DWORD size = MAX_PATH;
    STRRET path;
    WCHAR *file;
    GUID guid;
    HKEY key;
    HRESULT hr;


    if(!_ILGetExtension(pidl, extensionA, 20))
        return S_FALSE;

    MultiByteToWideChar(CP_ACP, 0, extensionA, -1, extensionW, 20);

604 605
    swprintf(buf, ARRAY_SIZE(buf), L".%s\\ShellEx\\{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
             extensionW, riid->Data1, riid->Data2, riid->Data3,
606 607 608 609 610 611 612
            riid->Data4[0], riid->Data4[1], riid->Data4[2], riid->Data4[3],
            riid->Data4[4], riid->Data4[5], riid->Data4[6], riid->Data4[7]);

    if(RegGetValueW(HKEY_CLASSES_ROOT, buf, NULL, RRF_RT_REG_SZ,
                NULL, buf, &size) != ERROR_SUCCESS)
        return S_FALSE;

613
    if(RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Blocked", 0, 0, 0,
614 615 616 617 618 619 620
                KEY_READ, NULL, &key, NULL) != ERROR_SUCCESS)
        return E_FAIL;
    if(RegQueryValueExW(key, buf, 0, NULL, NULL, NULL)
            != ERROR_FILE_NOT_FOUND)
        return E_ACCESSDENIED;
    RegCloseKey(key);

621
    if(RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Blocked", 0, 0, 0,
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
                KEY_READ, NULL, &key, NULL) != ERROR_SUCCESS)
        return E_FAIL;
    if(RegQueryValueExW(key, buf, 0, NULL, NULL, NULL)
            != ERROR_FILE_NOT_FOUND)
        return E_ACCESSDENIED;
    RegCloseKey(key);

    if(!GUIDFromStringW(buf, &guid))
        return E_FAIL;

    hr = CoCreateInstance(&guid, NULL, CLSCTX_INPROC_SERVER,
            &IID_IPersistFile, (void**)&persist_file);
    if(FAILED(hr))
        return hr;

637
    hr = IShellFolder2_GetDisplayNameOf(iface, pidl, SHGDN_FORPARSING, &path);
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
    if(SUCCEEDED(hr))
        hr = StrRetToStrW(&path, NULL, &file);
    if(FAILED(hr)) {
        IPersistFile_Release(persist_file);
        return hr;
    }

    hr = IPersistFile_Load(persist_file, file, STGM_READ);
    CoTaskMemFree(file);
    if(FAILED(hr)) {
        IPersistFile_Release(persist_file);
        return hr;
    }

    hr = IPersistFile_QueryInterface(persist_file, riid, ppvOut);
    IPersistFile_Release(persist_file);
    return hr;
}

657 658 659 660 661 662 663 664 665 666 667 668
/**************************************************************************
*  IShellFolder_fnGetUIObjectOf
*
* PARAMETERS
*  HWND           hwndOwner, //[in ] Parent window for any output
*  UINT           cidl,      //[in ] array size
*  LPCITEMIDLIST* apidl,     //[in ] simple pidl array
*  REFIID         riid,      //[in ] Requested Interface
*  UINT*          prgfInOut, //[   ] reserved
*  LPVOID*        ppvObject) //[out] Resulting Interface
*
* NOTES
669 670 671 672
*  This function gets asked to return "view objects" for one or more (multiple
*  select) items:
*  The viewobject typically is an COM object with one of the following
*  interfaces:
673 674
*  IExtractIcon,IDataObject,IContextMenu
*  In order to support icon positions in the default Listview your DataObject
675 676 677 678
*  must implement the SetData method (in addition to GetData :) - the shell
*  passes a barely documented "Icon positions" structure to SetData when the
*  drag starts, and GetData's it if the drop is in another explorer window that
*  needs the positions.
679 680 681
*/
static HRESULT WINAPI
IShellFolder_fnGetUIObjectOf (IShellFolder2 * iface,
682 683 684
                              HWND hwndOwner,
                              UINT cidl, LPCITEMIDLIST * apidl, REFIID riid,
                              UINT * prgfInOut, LPVOID * ppvOut)
685
{
686
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
687 688 689 690 691

    LPITEMIDLIST pidl;
    IUnknown *pObj = NULL;
    HRESULT hr = E_INVALIDARG;

692
    TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
693
     This, hwndOwner, cidl, apidl, shdebugstr_guid (riid), prgfInOut, ppvOut);
694 695

    if (ppvOut) {
696 697
        *ppvOut = NULL;

698 699 700 701 702 703
        if(cidl == 1) {
            hr = SHELL32_CreateExtensionUIObject(iface, *apidl, riid, ppvOut);
            if(hr != S_FALSE)
                return hr;
        }

704
        if (IsEqualIID (riid, &IID_IContextMenu) && (cidl >= 1)) {
705
            return ItemMenu_Constructor((IShellFolder*)iface, This->pidlRoot, apidl, cidl, riid, ppvOut);
706 707 708 709 710 711 712
        } else if (IsEqualIID (riid, &IID_IDataObject) && (cidl >= 1)) {
            pObj = (LPUNKNOWN) IDataObject_Constructor (hwndOwner,
             This->pidlRoot, apidl, cidl);
            hr = S_OK;
        } else if (IsEqualIID (riid, &IID_IExtractIconA) && (cidl == 1)) {
            pidl = ILCombine (This->pidlRoot, apidl[0]);
            pObj = (LPUNKNOWN) IExtractIconA_Constructor (pidl);
713
            ILFree(pidl);
714 715 716 717
            hr = S_OK;
        } else if (IsEqualIID (riid, &IID_IExtractIconW) && (cidl == 1)) {
            pidl = ILCombine (This->pidlRoot, apidl[0]);
            pObj = (LPUNKNOWN) IExtractIconW_Constructor (pidl);
718
            ILFree(pidl);
719 720
            hr = S_OK;
        } else if (IsEqualIID (riid, &IID_IDropTarget) && (cidl >= 1)) {
721
            hr = IShellFolder2_QueryInterface (iface, &IID_IDropTarget,
722 723 724 725
             (LPVOID *) & pObj);
        } else if ((IsEqualIID(riid,&IID_IShellLinkW) ||
         IsEqualIID(riid,&IID_IShellLinkA)) && (cidl == 1)) {
            pidl = ILCombine (This->pidlRoot, apidl[0]);
726
            hr = IShellLink_ConstructFromFile(NULL, riid, pidl, &pObj);
727
            ILFree(pidl);
728 729 730 731 732 733 734 735
        } else {
            hr = E_NOINTERFACE;
        }

        if (SUCCEEDED(hr) && !pObj)
            hr = E_OUTOFMEMORY;

        *ppvOut = pObj;
736
    }
737
    TRACE ("(%p)->hr=0x%08lx\n", This, hr);
738 739 740
    return hr;
}

741 742 743 744 745 746 747 748 749 750 751 752 753
/******************************************************************************
 * SHELL_FS_HideExtension [Internal]
 *
 * Query the registry if the filename extension of a given path should be 
 * hidden.
 *
 * PARAMS
 *  szPath [I] Relative or absolute path of a file
 *  
 * RETURNS
 *  TRUE, if the filename's extension should be hidden
 *  FALSE, otherwise.
 */
754
static BOOL SHELL_FS_HideExtension(LPCWSTR szPath)
755
{
756 757 758 759
    HKEY hKey;
    DWORD dwData;
    DWORD dwDataSize = sizeof (DWORD);
    BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */
760 761 762 763

    if (!RegCreateKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced",
                         0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0)) {
        if (!RegQueryValueExW(hKey, L"HideFileExt", 0, 0, (LPBYTE) &dwData, &dwDataSize))
764 765 766
            doHide = dwData;
        RegCloseKey (hKey);
    }
767

768 769
    if (!doHide) {
        LPWSTR ext = PathFindExtensionW(szPath);
770

771 772 773
        if (*ext != '\0') {
            WCHAR classname[MAX_PATH];
            LONG classlen = sizeof(classname);
774

775 776
            if (!RegQueryValueW(HKEY_CLASSES_ROOT, ext, classname, &classlen))
                if (!RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey)) {
777
                    if (!RegQueryValueExW(hKey, L"NeverShowExt", 0, NULL, NULL, NULL))
778 779 780
                        doHide = TRUE;
                    RegCloseKey(hKey);
                }
781
        }
782 783 784 785
    }
    return doHide;
}
    
786
void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags)
787 788 789 790
{
    /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */
    if (!(dwFlags & SHGDN_FORPARSING) &&
        ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) {
791 792
        if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.')
            PathRemoveExtensionW(szPath);
793 794 795
    }
}

796 797 798 799 800 801 802 803 804
static void get_display_name( WCHAR dest[MAX_PATH], const WCHAR *path, LPCITEMIDLIST pidl, BOOL is_unix )
{
    char *buffer;
    WCHAR *res;
    DWORD i, len;

    lstrcpynW( dest, path, MAX_PATH );

    /* try to get a better path than the \\?\unix one */
805
    if (!wcsnicmp( path, L"\\\\?\\unix\\", 9 ))
806 807 808 809
    {
        if (!is_unix)
        {
            len = WideCharToMultiByte( CP_UNIXCP, 0, path + 8, -1, NULL, 0, NULL, NULL );
810
            buffer = malloc( len );
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830
            len = WideCharToMultiByte( CP_UNIXCP, 0, path + 8, -1, buffer, len, NULL, NULL );
            for (i = 0; i < len; i++) if (buffer[i] == '\\') buffer[i] = '/';
            if ((res = wine_get_dos_file_name( buffer )))
            {
                lstrcpynW( dest, res, MAX_PATH );
                heap_free( res );
            }
        }
        else lstrcpynW( dest, path + 8, MAX_PATH );
    }

    if (!_ILIsDesktop(pidl))
    {
        PathAddBackslashW( dest );
        len = lstrlenW( dest );
        _ILSimpleGetTextW( pidl, dest + len, MAX_PATH - len );
    }
    if (is_unix) for (i = 0; dest[i]; i++) if (dest[i] == '\\') dest[i] = '/';
}

831 832 833 834 835 836 837 838 839 840 841 842 843 844
/**************************************************************************
*  IShellFolder_fnGetDisplayNameOf
*  Retrieves the display name for the specified file object or subfolder
*
* PARAMETERS
*  LPCITEMIDLIST pidl,    //[in ] complex pidl to item
*  DWORD         dwFlags, //[in ] SHGNO formatting flags
*  LPSTRRET      lpName)  //[out] Returned display name
*
* FIXME
*  if the name is in the pidl the ret value should be a STRRET_OFFSET
*/

static HRESULT WINAPI
845 846
IShellFolder_fnGetDisplayNameOf (IShellFolder2 * iface, LPCITEMIDLIST pidl,
                                 DWORD dwFlags, LPSTRRET strRet)
847
{
848
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
849
    LPWSTR pszPath;
850

851
    HRESULT hr = S_OK;
852

853
    TRACE ("(%p)->(pidl=%p,0x%08lx,%p)\n", This, pidl, dwFlags, strRet);
854 855
    pdump (pidl);

856
    if (!strRet)
857
        return E_INVALIDARG;
858

859 860 861 862
    pszPath = CoTaskMemAlloc((MAX_PATH +1) * sizeof(WCHAR));
    if (!pszPath)
        return E_OUTOFMEMORY;

863
    if (_ILIsDesktop(pidl)) { /* empty pidl */
864
        if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
865
            (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER))
866
        {
867 868 869
            if (This->sPathTarget)
                get_display_name( pszPath, This->sPathTarget, pidl,
                                  IsEqualCLSID( This->pclsid, &CLSID_UnixFolder ));
870
        } else {
871 872
            /* pidl has to contain exactly one non null SHITEMID */
            hr = E_INVALIDARG;
873
        }
874
    } else if (_ILIsPidlSimple(pidl)) {
875
        if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
876 877
            (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
            This->sPathTarget)
878
        {
879 880
            get_display_name( pszPath, This->sPathTarget, pidl,
                              IsEqualCLSID( This->pclsid, &CLSID_UnixFolder ));
881
        }
882
        else _ILSimpleGetTextW(pidl, pszPath, MAX_PATH);
883
        if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
884
    } else {
885
        hr = SHELL32_GetDisplayNameOfChild(iface, pidl, dwFlags, pszPath, MAX_PATH);
886 887 888
    }

    if (SUCCEEDED(hr)) {
889 890 891
        /* Win9x always returns ANSI strings, NT always returns Unicode strings */
        if (GetVersion() & 0x80000000) {
            strRet->uType = STRRET_CSTR;
892
            if (!WideCharToMultiByte(CP_ACP, 0, pszPath, -1, strRet->cStr, MAX_PATH,
893
                 NULL, NULL))
894
                strRet->cStr[0] = '\0';
895 896 897
            CoTaskMemFree(pszPath);
        } else {
            strRet->uType = STRRET_WSTR;
898
            strRet->pOleStr = pszPath;
899 900 901
        }
    } else
        CoTaskMemFree(pszPath);
902

903
    TRACE ("-- (%p)->(%s)\n", This, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
904
    return hr;
905 906 907 908 909 910 911 912 913 914 915 916 917 918
}

/**************************************************************************
*  IShellFolder_fnSetNameOf
*  Changes the name of a file object or subfolder, possibly changing its item
*  identifier in the process.
*
* PARAMETERS
*  HWND          hwndOwner,  //[in ] Owner window for output
*  LPCITEMIDLIST pidl,       //[in ] simple pidl of item to change
*  LPCOLESTR     lpszName,   //[in ] the items new display name
*  DWORD         dwFlags,    //[in ] SHGNO formatting flags
*  LPITEMIDLIST* ppidlOut)   //[out] simple pidl returned
*/
919 920 921 922 923 924
static HRESULT WINAPI IShellFolder_fnSetNameOf (IShellFolder2 * iface,
                                                HWND hwndOwner,
                                                LPCITEMIDLIST pidl,
                                                LPCOLESTR lpName,
                                                DWORD dwFlags,
                                                LPITEMIDLIST * pPidlOut)
925
{
926
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
927
    WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1];
928
    LPWSTR ptr;
929 930
    BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));

931
    TRACE ("(%p)->(%p,pidl=%p,%s,%lu,%p)\n", This, hwndOwner, pidl,
932
     debugstr_w (lpName), dwFlags, pPidlOut);
933

934 935 936
    /* pidl has to contain a single non-empty SHITEMID */
    if (_ILIsDesktop(pidl) || !_ILIsPidlSimple(pidl) || !_ILGetTextPointer(pidl)) return E_INVALIDARG;

937
    if (wcspbrk( lpName, L"\\/:*?\"<>|" )) return HRESULT_FROM_WIN32(ERROR_CANCELLED);
938

939
    /* build source path */
940
    lstrcpynW(szSrc, This->sPathTarget, MAX_PATH);
941
    ptr = PathAddBackslashW (szSrc);
942
    _ILSimpleGetTextW (pidl, ptr, MAX_PATH + 1 - (ptr - szSrc));
943 944

    /* build destination path */
945 946 947
    lstrcpynW(szDest, This->sPathTarget, MAX_PATH);
    ptr = PathAddBackslashW (szDest);
    lstrcpynW(ptr, lpName, MAX_PATH + 1 - (ptr - szDest));
948

949
    if(!(dwFlags & SHGDN_FORPARSING) && SHELL_FS_HideExtension(szSrc)) {
950 951
        WCHAR *ext = PathFindExtensionW(szSrc);
        if(*ext != '\0') {
952
            INT len = lstrlenW(szDest);
953 954 955 956
            lstrcpynW(szDest + len, ext, MAX_PATH - len);
        }
    }
    
957
    TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest));
958

959
    if (MoveFileW (szSrc, szDest)) {
960
        HRESULT hr = S_OK;
961

962
        if (pPidlOut)
963
            hr = _ILCreateFromPathW(szDest, pPidlOut);
964

965
        SHChangeNotify (bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM,
966
         SHCNF_PATHW, szSrc, szDest);
967

968
        return hr;
969
    }
970

971 972 973
    return E_FAIL;
}

974
static HRESULT WINAPI IShellFolder_fnGetDefaultSearchGUID(IShellFolder2 *iface, GUID *guid)
975
{
976
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
977
    TRACE("(%p)->(%p)\n", This, guid);
978 979
    return E_NOTIMPL;
}
980

981 982
static HRESULT WINAPI IShellFolder_fnEnumSearches (IShellFolder2 * iface,
                                                   IEnumExtraSearch ** ppenum)
983
{
984
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
985
    FIXME ("(%p)\n", This);
986 987
    return E_NOTIMPL;
}
988

989
static HRESULT WINAPI
990
IShellFolder_fnGetDefaultColumn(IShellFolder2 *iface, DWORD reserved, ULONG *sort, ULONG *display)
991
{
992
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
993

994
    TRACE("(%p)->(%#lx, %p, %p)\n", This, reserved, sort, display);
995

996
    return E_NOTIMPL;
997
}
998 999 1000 1001

static HRESULT WINAPI
IShellFolder_fnGetDefaultColumnState (IShellFolder2 * iface, UINT iColumn,
                                      DWORD * pcsFlags)
1002
{
1003
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
1004

1005
    TRACE ("(%p)\n", This);
1006 1007

    if (!pcsFlags || iColumn >= GENERICSHELLVIEWCOLUMNS)
1008
        return E_INVALIDARG;
1009 1010 1011 1012 1013

    *pcsFlags = GenericSFHeader[iColumn].pcsFlags;

    return S_OK;
}
1014

1015
static HRESULT WINAPI
1016 1017
IShellFolder_fnGetDetailsEx (IShellFolder2 * iface, LPCITEMIDLIST pidl,
                             const SHCOLUMNID * pscid, VARIANT * pv)
1018
{
1019
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
1020
    FIXME ("(%p)\n", This);
1021 1022 1023

    return E_NOTIMPL;
}
1024

1025
static HRESULT WINAPI
1026 1027
IShellFolder_fnGetDetailsOf (IShellFolder2 * iface, LPCITEMIDLIST pidl,
                             UINT iColumn, SHELLDETAILS * psd)
1028
{
1029
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
1030 1031 1032 1033

    TRACE ("(%p)->(%p %i %p)\n", This, pidl, iColumn, psd);

    if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS)
1034
        return E_INVALIDARG;
1035

1036
    if (!pidl) return SHELL32_GetColumnDetails(GenericSFHeader, iColumn, psd);
1037

1038
    return shellfolder_get_file_details( iface, pidl, GenericSFHeader, iColumn, psd );
1039
}
1040 1041

static HRESULT WINAPI
1042
IShellFolder_fnMapColumnToSCID (IShellFolder2 *iface, UINT column, SHCOLUMNID *scid)
1043
{
1044
    IGenericSFImpl *This = impl_from_IShellFolder2(iface);
1045 1046 1047 1048 1049 1050 1051

    TRACE("(%p)->(%u %p)\n", This, column, scid);

    if (column >= GENERICSHELLVIEWCOLUMNS)
        return E_INVALIDARG;

    return shellfolder_map_column_to_scid(GenericSFHeader, column, scid);
1052 1053
}

1054
static const IShellFolder2Vtbl sfvt =
1055
{
1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
    IShellFolder_fnQueryInterface,
    IShellFolder_fnAddRef,
    IShellFolder_fnRelease,
    IShellFolder_fnParseDisplayName,
    IShellFolder_fnEnumObjects,
    IShellFolder_fnBindToObject,
    IShellFolder_fnBindToStorage,
    IShellFolder_fnCompareIDs,
    IShellFolder_fnCreateViewObject,
    IShellFolder_fnGetAttributesOf,
    IShellFolder_fnGetUIObjectOf,
    IShellFolder_fnGetDisplayNameOf,
    IShellFolder_fnSetNameOf,
    /* ShellFolder2 */
    IShellFolder_fnGetDefaultSearchGUID,
    IShellFolder_fnEnumSearches,
    IShellFolder_fnGetDefaultColumn,
    IShellFolder_fnGetDefaultColumnState,
    IShellFolder_fnGetDetailsEx,
    IShellFolder_fnGetDetailsOf,
    IShellFolder_fnMapColumnToSCID
1077 1078 1079 1080 1081 1082
};

/****************************************************************************
 * ISFHelper for IShellFolder implementation
 */

1083
static HRESULT WINAPI ISFHelper_fnQueryInterface(ISFHelper *iface, REFIID riid, void **ppvObj)
1084
{
1085
    IGenericSFImpl *This = impl_from_ISFHelper(iface);
1086

1087
    return IUnknown_QueryInterface(This->outer_unk, riid, ppvObj);
1088 1089
}

1090
static ULONG WINAPI ISFHelper_fnAddRef(ISFHelper *iface)
1091
{
1092
    IGenericSFImpl *This = impl_from_ISFHelper(iface);
1093

1094
    return IUnknown_AddRef(This->outer_unk);
1095 1096
}

1097
static ULONG WINAPI ISFHelper_fnRelease(ISFHelper *iface)
1098
{
1099
    IGenericSFImpl *This = impl_from_ISFHelper(iface);
1100

1101
    return IUnknown_Release(This->outer_unk);
1102 1103 1104
}

/****************************************************************************
1105
 * ISFHelper_fnGetUniqueName
1106 1107 1108 1109
 *
 * creates a unique folder name
 */

1110
static HRESULT WINAPI
1111
ISFHelper_fnGetUniqueName (ISFHelper * iface, LPWSTR pwszName, UINT uLen)
1112
{
1113
    IGenericSFImpl *This = impl_from_ISFHelper(iface);
1114 1115
    IEnumIDList *penum;
    HRESULT hr;
1116
    WCHAR wszText[MAX_PATH];
1117
    WCHAR wszNewFolder[25];
1118

1119
    TRACE ("(%p)(%p %u)\n", This, pwszName, uLen);
1120

1121 1122
    LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, ARRAY_SIZE(wszNewFolder));
    if (uLen < ARRAY_SIZE(wszNewFolder) + 3)
1123
        return E_POINTER;
1124

1125
    lstrcpynW (pwszName, wszNewFolder, uLen);
1126

1127 1128
    hr = IShellFolder2_EnumObjects(&This->IShellFolder2_iface, 0,
            SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penum);
1129
    if (penum) {
1130 1131 1132 1133 1134 1135 1136 1137
        LPITEMIDLIST pidl;
        DWORD dwFetched;
        int i = 1;

next:
        IEnumIDList_Reset (penum);
        while (S_OK == IEnumIDList_Next (penum, 1, &pidl, &dwFetched) &&
         dwFetched) {
1138 1139
            _ILSimpleGetTextW (pidl, wszText, MAX_PATH);
            if (0 == lstrcmpiW (wszText, pwszName)) {
1140
                swprintf (pwszName, uLen, L"%s %d", wszNewFolder, i++);
1141 1142 1143 1144 1145 1146 1147 1148 1149
                if (i > 99) {
                    hr = E_FAIL;
                    break;
                }
                goto next;
            }
        }

        IEnumIDList_Release (penum);
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159
    }
    return hr;
}

/****************************************************************************
 * ISFHelper_fnAddFolder
 *
 * adds a new folder.
 */

1160
static HRESULT WINAPI
1161
ISFHelper_fnAddFolder (ISFHelper * iface, HWND hwnd, LPCWSTR pwszName,
1162
                       LPITEMIDLIST * ppidlOut)
1163
{
1164
    IGenericSFImpl *This = impl_from_ISFHelper(iface);
1165
    WCHAR wszNewDir[MAX_PATH];
1166
    BOOL bRes;
1167 1168
    HRESULT hres = E_FAIL;

1169
    TRACE ("(%p)(%s %p)\n", This, debugstr_w(pwszName), ppidlOut);
1170

1171 1172 1173 1174
    wszNewDir[0] = 0;
    if (This->sPathTarget)
        lstrcpynW(wszNewDir, This->sPathTarget, MAX_PATH);
    PathAppendW(wszNewDir, pwszName);
1175

1176
    bRes = CreateDirectoryW (wszNewDir, NULL);
1177
    if (bRes) {
1178
        LPITEMIDLIST relPidl;
1179

1180 1181
        lstrcpyW(wszNewDir, pwszName);

1182 1183
        hres = IShellFolder2_ParseDisplayName(&This->IShellFolder2_iface, hwnd, NULL, wszNewDir,
                NULL, &relPidl, NULL);
1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204

        if (SUCCEEDED(hres)) {
            LPITEMIDLIST fullPidl;

            fullPidl = ILCombine(This->pidlRoot, relPidl);

            if (fullPidl) {
                SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST, fullPidl, NULL);
                ILFree(fullPidl);

                if (ppidlOut)
                    *ppidlOut = relPidl;
                else
                    ILFree(relPidl);
            } else {
                WARN("failed to combine %s into a full PIDL\n", wine_dbgstr_w(pwszName));
                ILFree(relPidl);
            }

        } else
            WARN("failed to parse %s into a PIDL\n", wine_dbgstr_w(pwszName));
1205

1206
    } else {
1207 1208 1209
        WCHAR wszText[128 + MAX_PATH];
        WCHAR wszTempText[128];
        WCHAR wszCaption[256];
1210 1211

        /* Cannot Create folder because of permissions */
1212 1213
        LoadStringW (shell32_hInstance, IDS_CREATEFOLDER_DENIED, wszTempText, ARRAY_SIZE(wszTempText));
        LoadStringW (shell32_hInstance, IDS_CREATEFOLDER_CAPTION, wszCaption, ARRAY_SIZE(wszCaption));
1214
        swprintf (wszText, ARRAY_SIZE(wszText), wszTempText, wszNewDir);
1215
        MessageBoxW (hwnd, wszText, wszCaption, MB_OK | MB_ICONEXCLAMATION);
1216 1217 1218 1219 1220
    }

    return hres;
}

1221 1222 1223 1224 1225 1226
/****************************************************************************
 * build_paths_list
 *
 * Builds a list of paths like the one used in SHFileOperation from a table of
 * PIDLs relative to the given base folder
 */
1227
static WCHAR *build_paths_list(LPCWSTR wszBasePath, int cidl, const LPCITEMIDLIST *pidls)
1228 1229 1230 1231 1232 1233 1234
{
    WCHAR *wszPathsList;
    WCHAR *wszListPos;
    int iPathLen;
    int i;
    
    iPathLen = lstrlenW(wszBasePath);
1235
    wszPathsList = malloc(MAX_PATH * sizeof(WCHAR) * cidl + 1);
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250
    wszListPos = wszPathsList;
    
    for (i = 0; i < cidl; i++) {
        if (!_ILIsFolder(pidls[i]) && !_ILIsValue(pidls[i]))
            continue;

        lstrcpynW(wszListPos, wszBasePath, MAX_PATH);
        /* FIXME: abort if path too long */
        _ILSimpleGetTextW(pidls[i], wszListPos+iPathLen, MAX_PATH-iPathLen);
        wszListPos += lstrlenW(wszListPos)+1;
    }
    *wszListPos=0;
    return wszPathsList;
}

1251 1252 1253 1254 1255
/****************************************************************************
 * ISFHelper_fnDeleteItems
 *
 * deletes items in folder
 */
1256 1257
static HRESULT WINAPI
ISFHelper_fnDeleteItems (ISFHelper * iface, UINT cidl, LPCITEMIDLIST * apidl)
1258
{
1259
    IGenericSFImpl *This = impl_from_ISFHelper(iface);
1260
    UINT i;
1261
    SHFILEOPSTRUCTW op;
1262
    WCHAR wszPath[MAX_PATH];
1263 1264 1265
    WCHAR *wszPathsList;
    HRESULT ret;
    WCHAR *wszCurrentPath;
1266 1267

    TRACE ("(%p)(%u %p)\n", This, cidl, apidl);
1268
    if (cidl==0) return S_OK;
1269

1270 1271 1272 1273 1274
    if (This->sPathTarget)
        lstrcpynW(wszPath, This->sPathTarget, MAX_PATH);
    else
        wszPath[0] = '\0';
    PathAddBackslashW(wszPath);
1275 1276 1277 1278 1279 1280
    wszPathsList = build_paths_list(wszPath, cidl, apidl);

    ZeroMemory(&op, sizeof(op));
    op.hwnd = GetActiveWindow();
    op.wFunc = FO_DELETE;
    op.pFrom = wszPathsList;
1281
    op.fFlags = FOF_ALLOWUNDO;
1282 1283 1284 1285 1286 1287 1288
    if (SHFileOperationW(&op))
    {
        WARN("SHFileOperation failed\n");
        ret = E_FAIL;
    }
    else
        ret = S_OK;
1289

1290 1291 1292 1293 1294
    /* we currently need to manually send the notifies */
    wszCurrentPath = wszPathsList;
    for (i = 0; i < cidl; i++)
    {
        LONG wEventId;
1295

1296 1297 1298 1299 1300 1301
        if (_ILIsFolder(apidl[i]))
            wEventId = SHCNE_RMDIR;
        else if (_ILIsValue(apidl[i]))
            wEventId = SHCNE_DELETE;
        else
            continue;
1302

1303 1304 1305 1306 1307
        /* check if file exists */
        if (GetFileAttributesW(wszCurrentPath) == INVALID_FILE_ATTRIBUTES)
        {
            LPITEMIDLIST pidl = ILCombine(This->pidlRoot, apidl[i]);
            SHChangeNotify(wEventId, SHCNF_IDLIST, pidl, NULL);
1308
            ILFree(pidl);
1309
        }
1310

1311
        wszCurrentPath += lstrlenW(wszCurrentPath)+1;
1312
    }
1313
    free(wszPathsList);
1314
    return ret;
1315 1316 1317 1318 1319 1320 1321 1322
}

/****************************************************************************
 * ISFHelper_fnCopyItems
 *
 * copies items to this folder
 */
static HRESULT WINAPI
1323 1324
ISFHelper_fnCopyItems (ISFHelper * iface, IShellFolder * pSFFrom, UINT cidl,
                       LPCITEMIDLIST * apidl)
1325
{
1326
    HRESULT ret=E_FAIL;
1327
    IPersistFolder2 *ppf2 = NULL;
1328 1329 1330
    WCHAR wszSrcPathRoot[MAX_PATH],
      wszDstPath[MAX_PATH+1];
    WCHAR *wszSrcPathsList;
1331
    IGenericSFImpl *This = impl_from_ISFHelper(iface);
1332

1333 1334
    SHFILEOPSTRUCTW fop;

1335 1336
    TRACE ("(%p)->(%p,%u,%p)\n", This, pSFFrom, cidl, apidl);

1337 1338
    IShellFolder_QueryInterface (pSFFrom, &IID_IPersistFolder2,
     (LPVOID *) & ppf2);
1339
    if (ppf2) {
1340 1341 1342
        LPITEMIDLIST pidl;

        if (SUCCEEDED (IPersistFolder2_GetCurFolder (ppf2, &pidl))) {
1343 1344 1345
            SHGetPathFromIDListW (pidl, wszSrcPathRoot);
            if (This->sPathTarget)
                lstrcpynW(wszDstPath, This->sPathTarget, MAX_PATH);
1346 1347
            else
                wszDstPath[0] = 0;
1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361
            PathAddBackslashW(wszSrcPathRoot);
            PathAddBackslashW(wszDstPath);
            wszSrcPathsList = build_paths_list(wszSrcPathRoot, cidl, apidl);
            ZeroMemory(&fop, sizeof(fop));
            fop.hwnd = GetActiveWindow();
            fop.wFunc = FO_COPY;
            fop.pFrom = wszSrcPathsList;
            fop.pTo = wszDstPath;
            fop.fFlags = FOF_ALLOWUNDO;
            ret = S_OK;
            if(SHFileOperationW(&fop))
            {
                WARN("Copy failed\n");
                ret = E_FAIL;
1362
            }
1363
            free(wszSrcPathsList);
1364
        }
1365 1366
        SHFree(pidl);
        IPersistFolder2_Release(ppf2);
1367
    }
1368
    return ret;
1369 1370
}

1371
static const ISFHelperVtbl shvt =
1372
{
1373 1374 1375 1376 1377 1378 1379
    ISFHelper_fnQueryInterface,
    ISFHelper_fnAddRef,
    ISFHelper_fnRelease,
    ISFHelper_fnGetUniqueName,
    ISFHelper_fnAddFolder,
    ISFHelper_fnDeleteItems,
    ISFHelper_fnCopyItems
1380 1381 1382
};

/************************************************************************
1383
 * IFSFldr_PersistFolder3_QueryInterface
1384 1385
 *
 */
1386 1387
static HRESULT WINAPI IFSFldr_PersistFolder3_QueryInterface(IPersistFolder3 *iface, REFIID iid,
        void **ppv)
1388
{
1389
    IGenericSFImpl *This = impl_from_IPersistFolder3(iface);
1390

1391
    return IUnknown_QueryInterface(This->outer_unk, iid, ppv);
1392 1393 1394
}

/************************************************************************
1395
 * IFSFldr_PersistFolder3_AddRef
1396 1397
 *
 */
1398
static ULONG WINAPI IFSFldr_PersistFolder3_AddRef(IPersistFolder3 *iface)
1399
{
1400
    IGenericSFImpl *This = impl_from_IPersistFolder3(iface);
1401

1402
    return IUnknown_AddRef(This->outer_unk);
1403 1404 1405
}

/************************************************************************
1406
 * IFSFldr_PersistFolder3_Release
1407 1408
 *
 */
1409
static ULONG WINAPI IFSFldr_PersistFolder3_Release(IPersistFolder3 *iface)
1410
{
1411
    IGenericSFImpl *This = impl_from_IPersistFolder3(iface);
1412

1413
    return IUnknown_Release(This->outer_unk);
1414 1415 1416
}

/************************************************************************
1417
 * IFSFldr_PersistFolder3_GetClassID
1418
 */
1419 1420
static HRESULT WINAPI
IFSFldr_PersistFolder3_GetClassID (IPersistFolder3 * iface, CLSID * lpClassId)
1421
{
1422
    IGenericSFImpl *This = impl_from_IPersistFolder3(iface);
1423 1424 1425 1426

    TRACE ("(%p)\n", This);

    if (!lpClassId)
1427
        return E_POINTER;
1428 1429 1430 1431 1432 1433
    *lpClassId = *This->pclsid;

    return S_OK;
}

/************************************************************************
1434
 * IFSFldr_PersistFolder3_Initialize
1435 1436 1437 1438
 *
 * NOTES
 *  sPathTarget is not set. Don't know how to handle in a non rooted environment.
 */
1439 1440
static HRESULT WINAPI
IFSFldr_PersistFolder3_Initialize (IPersistFolder3 * iface, LPCITEMIDLIST pidl)
1441
{
1442
    WCHAR wszTemp[MAX_PATH];
1443
    int len;
1444
    IGenericSFImpl *This = impl_from_IPersistFolder3(iface);
1445 1446 1447

    TRACE ("(%p)->(%p)\n", This, pidl);

1448 1449
    wszTemp[0] = 0;

1450
    SHFree (This->pidlRoot);     /* free the old pidl */
1451
    This->pidlRoot = ILClone (pidl); /* set my pidl */
1452

1453 1454 1455 1456 1457
    /* FolderShortcuts' Initialize method only sets the ITEMIDLIST, which
     * specifies the location in the shell namespace, but leaves the
     * target folder alone */
    if (IsEqualCLSID( This->pclsid, &CLSID_FolderShortcut )) return S_OK;

1458 1459
    SHFree (This->sPathTarget);
    This->sPathTarget = NULL;
1460 1461

    /* set my path */
1462 1463 1464 1465 1466 1467 1468
    if (_ILIsSpecialFolder(pidl) && IsEqualCLSID( This->pclsid, _ILGetGUIDPointer(pidl) ))
    {
        if (IsEqualCLSID( This->pclsid, &CLSID_MyDocuments ))
        {
            if (!SHGetSpecialFolderPathW( 0, wszTemp, CSIDL_PERSONAL, FALSE )) return E_FAIL;
            PathAddBackslashW( wszTemp );
        }
1469
        else lstrcpyW( wszTemp, L"\\\\?\\unix\\" );
1470 1471 1472
    }
    else SHGetPathFromIDListW( pidl, wszTemp );

1473
    if ((len = lstrlenW(wszTemp)))
1474
    {
1475
        This->sPathTarget = SHAlloc((len + 1) * sizeof(WCHAR));
1476
        if (!This->sPathTarget) return E_OUTOFMEMORY;
1477
        memcpy(This->sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1478 1479
    }

1480
    TRACE ("--(%p)->(%s)\n", This, debugstr_w(This->sPathTarget));
1481 1482 1483 1484
    return S_OK;
}

/**************************************************************************
1485
 * IFSFldr_PersistFolder3_GetCurFolder
1486
 */
1487 1488 1489
static HRESULT WINAPI
IFSFldr_PersistFolder3_fnGetCurFolder (IPersistFolder3 * iface,
                                       LPITEMIDLIST * pidl)
1490
{
1491
    IGenericSFImpl *This = impl_from_IPersistFolder3(iface);
1492 1493 1494 1495 1496 1497 1498 1499 1500

    TRACE ("(%p)->(%p)\n", This, pidl);

    if (!pidl) return E_POINTER;
    *pidl = ILClone (This->pidlRoot);
    return S_OK;
}

/**************************************************************************
1501
 * IFSFldr_PersistFolder3_InitializeEx
1502
 *
1503
 * FIXME: error handling
1504 1505 1506
 */
static HRESULT WINAPI
IFSFldr_PersistFolder3_InitializeEx (IPersistFolder3 * iface,
1507 1508
                                     IBindCtx * pbc, LPCITEMIDLIST pidlRoot,
                                     const PERSIST_FOLDER_TARGET_INFO * ppfti)
1509
{
1510
    WCHAR wszTemp[MAX_PATH];
1511

1512
    IGenericSFImpl *This = impl_from_IPersistFolder3(iface);
1513 1514 1515

    TRACE ("(%p)->(%p,%p,%p)\n", This, pbc, pidlRoot, ppfti);
    if (ppfti)
1516
        TRACE ("--%p %s %s 0x%08lx 0x%08x\n",
1517 1518 1519
         ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName),
         debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes,
         ppfti->csidl);
1520 1521 1522

    pdump (pidlRoot);
    if (ppfti && ppfti->pidlTargetFolder)
1523
        pdump (ppfti->pidlTargetFolder);
1524 1525

    if (This->pidlRoot)
1526 1527 1528 1529
    {
        SHFree(This->pidlRoot);
        This->pidlRoot = NULL;
    }
1530
    if (This->sPathTarget)
1531 1532 1533 1534
    {
        SHFree(This->sPathTarget);
        This->sPathTarget = NULL;
    }
1535 1536 1537 1538 1539 1540 1541

    /*
     * Root path and pidl
     */
    This->pidlRoot = ILClone (pidlRoot);

    /*
1542
     *  the target folder is specified in csidl OR pidlTargetFolder OR
1543
     *  szTargetParsingName
1544 1545
     */
    if (ppfti) {
1546
        if (ppfti->csidl != -1) {
1547
            if (SHGetSpecialFolderPathW (0, wszTemp, ppfti->csidl,
1548
             ppfti->csidl & CSIDL_FLAG_CREATE)) {
1549
                int len = lstrlenW(wszTemp);
1550 1551 1552 1553
                This->sPathTarget = SHAlloc((len + 1) * sizeof(WCHAR));
                if (!This->sPathTarget)
                    return E_OUTOFMEMORY;
                memcpy(This->sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1554 1555
            }
        } else if (ppfti->szTargetParsingName[0]) {
1556
            int len = lstrlenW(ppfti->szTargetParsingName);
1557 1558 1559 1560 1561
            This->sPathTarget = SHAlloc((len + 1) * sizeof(WCHAR));
            if (!This->sPathTarget)
                return E_OUTOFMEMORY;
            memcpy(This->sPathTarget, ppfti->szTargetParsingName,
                   (len + 1) * sizeof(WCHAR));
1562
        } else if (ppfti->pidlTargetFolder) {
1563
            if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp)) {
1564
                int len = lstrlenW(wszTemp);
1565 1566 1567 1568
                This->sPathTarget = SHAlloc((len + 1) * sizeof(WCHAR));
                if (!This->sPathTarget)
                    return E_OUTOFMEMORY;
                memcpy(This->sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1569 1570
            }
        }
1571 1572
    }

1573
    TRACE ("--(%p)->(target=%s)\n", This, debugstr_w(This->sPathTarget));
1574 1575 1576 1577 1578
    pdump (This->pidlRoot);
    return (This->sPathTarget) ? S_OK : E_FAIL;
}

static HRESULT WINAPI
1579 1580
IFSFldr_PersistFolder3_GetFolderTargetInfo (IPersistFolder3 * iface,
                                            PERSIST_FOLDER_TARGET_INFO * ppfti)
1581
{
1582
    IGenericSFImpl *This = impl_from_IPersistFolder3(iface);
1583
    FIXME ("(%p)->(%p)\n", This, ppfti);
1584
    ZeroMemory (ppfti, sizeof (*ppfti));
1585 1586 1587
    return E_NOTIMPL;
}

1588
static const IPersistFolder3Vtbl pfvt =
1589
{
1590 1591 1592 1593 1594 1595 1596 1597
    IFSFldr_PersistFolder3_QueryInterface,
    IFSFldr_PersistFolder3_AddRef,
    IFSFldr_PersistFolder3_Release,
    IFSFldr_PersistFolder3_GetClassID,
    IFSFldr_PersistFolder3_Initialize,
    IFSFldr_PersistFolder3_fnGetCurFolder,
    IFSFldr_PersistFolder3_InitializeEx,
    IFSFldr_PersistFolder3_GetFolderTargetInfo
1598 1599
};

1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649
/****************************************************************************
 * IPersistPropertyBag implementation
 */
static HRESULT WINAPI PersistPropertyBag_QueryInterface(IPersistPropertyBag* iface,
    REFIID riid, void** ppv)
{
    IGenericSFImpl *This = impl_from_IPersistPropertyBag(iface);
    return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
}

static ULONG WINAPI PersistPropertyBag_AddRef(IPersistPropertyBag* iface)
{
    IGenericSFImpl *This = impl_from_IPersistPropertyBag(iface);
    return IUnknown_AddRef(This->outer_unk);
}

static ULONG WINAPI PersistPropertyBag_Release(IPersistPropertyBag* iface)
{
    IGenericSFImpl *This = impl_from_IPersistPropertyBag(iface);
    return IUnknown_Release(This->outer_unk);
}

static HRESULT WINAPI PersistPropertyBag_GetClassID(IPersistPropertyBag* iface, CLSID* pClassID)
{
    IGenericSFImpl *This = impl_from_IPersistPropertyBag(iface);
    return IPersistFolder3_GetClassID(&This->IPersistFolder3_iface, pClassID);
}

static HRESULT WINAPI PersistPropertyBag_InitNew(IPersistPropertyBag* iface)
{
    IGenericSFImpl *This = impl_from_IPersistPropertyBag(iface);
    FIXME("(%p): stub\n", This);
    return E_NOTIMPL;
}

static HRESULT WINAPI PersistPropertyBag_Load(IPersistPropertyBag *iface,
    IPropertyBag *pPropertyBag, IErrorLog *pErrorLog)
{
    IGenericSFImpl *This = impl_from_IPersistPropertyBag(iface);
    PERSIST_FOLDER_TARGET_INFO pftiTarget;
    VARIANT var;
    HRESULT hr;

    TRACE("(%p)->(%p %p)\n", This, pPropertyBag, pErrorLog);

    if (!pPropertyBag)
        return E_POINTER;

    /* Get 'Target' property from the property bag. */
    V_VT(&var) = VT_BSTR;
1650
    hr = IPropertyBag_Read(pPropertyBag, L"Target", &var, NULL);
1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681
    if (FAILED(hr))
        return E_FAIL;
    lstrcpyW(pftiTarget.szTargetParsingName, V_BSTR(&var));
    SysFreeString(V_BSTR(&var));

    pftiTarget.pidlTargetFolder = NULL;
    pftiTarget.szNetworkProvider[0] = 0;
    pftiTarget.dwAttributes = -1;
    pftiTarget.csidl = -1;

    return IPersistFolder3_InitializeEx(&This->IPersistFolder3_iface, NULL, NULL, &pftiTarget);
}

static HRESULT WINAPI PersistPropertyBag_Save(IPersistPropertyBag *iface,
    IPropertyBag *pPropertyBag, BOOL fClearDirty, BOOL fSaveAllProperties)
{
    IGenericSFImpl *This = impl_from_IPersistPropertyBag(iface);
    FIXME("(%p): stub\n", This);
    return E_NOTIMPL;
}

static const IPersistPropertyBagVtbl ppbvt = {
    PersistPropertyBag_QueryInterface,
    PersistPropertyBag_AddRef,
    PersistPropertyBag_Release,
    PersistPropertyBag_GetClassID,
    PersistPropertyBag_InitNew,
    PersistPropertyBag_Load,
    PersistPropertyBag_Save
};

1682 1683 1684
/****************************************************************************
 * ISFDropTarget implementation
 */
1685
static HRESULT WINAPI ISFDropTarget_QueryInterface(IDropTarget *iface, REFIID riid, void **ppv)
1686
{
1687
    IGenericSFImpl *This = impl_from_IDropTarget(iface);
1688

1689
    return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
1690 1691
}

1692
static ULONG WINAPI ISFDropTarget_AddRef(IDropTarget *iface)
1693
{
1694
    IGenericSFImpl *This = impl_from_IDropTarget(iface);
1695

1696
    return IUnknown_AddRef(This->outer_unk);
1697 1698
}

1699
static ULONG WINAPI ISFDropTarget_Release(IDropTarget *iface)
1700
{
1701
    IGenericSFImpl *This = impl_from_IDropTarget(iface);
1702

1703
    return IUnknown_Release(This->outer_unk);
1704 1705
}

1706 1707 1708
#define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
#define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])

1709
static HRESULT WINAPI
1710 1711
ISFDropTarget_DragEnter (IDropTarget * iface, IDataObject * pDataObject,
                         DWORD dwKeyState, POINTL pt, DWORD * pdwEffect)
1712
{
1713
    IGenericSFImpl *This = impl_from_IDropTarget(iface);
1714 1715
    FORMATETC format;
    STGMEDIUM medium;
1716

1717
    TRACE("(%p)->(%p 0x%08lx {.x=%ld, .y=%ld} %p)\n", This, pDataObject, dwKeyState, pt.x, pt.y, pdwEffect);
1718

1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730
    if (!pdwEffect || !pDataObject)
        return E_INVALIDARG;

    /* Compute a mask of supported drop-effects for this shellfolder object and the given data
     * object. Dropping is only supported on folders, which represent filesystem locations. One
     * can't drop on file objects. And the 'move' drop effect is only supported, if the source
     * folder is not identical to the target folder. */
    This->drop_effects_mask = DROPEFFECT_NONE;
    InitFormatEtc(format, cfShellIDList, TYMED_HGLOBAL);
    if (_ILIsFolder(ILFindLastID(This->pidlRoot)) && /* Only drop to folders, not to files */
        SUCCEEDED(IDataObject_GetData(pDataObject, &format, &medium))) /* Only ShellIDList format */
    {
1731
        LPIDA pidaShellIDList = GlobalLock(medium.hGlobal);
1732 1733 1734 1735 1736
        This->drop_effects_mask |= DROPEFFECT_COPY|DROPEFFECT_LINK;

        if (pidaShellIDList) { /* Files can only be moved between two different folders */
            if (!ILIsEqual(HIDA_GetPIDLFolder(pidaShellIDList), This->pidlRoot))
                This->drop_effects_mask |= DROPEFFECT_MOVE;
1737
            GlobalUnlock(medium.hGlobal);
1738 1739 1740 1741
        }
    }

    *pdwEffect = KeyStateToDropEffect(dwKeyState) & This->drop_effects_mask;
1742 1743 1744 1745

    return S_OK;
}

1746 1747 1748
static HRESULT WINAPI
ISFDropTarget_DragOver (IDropTarget * iface, DWORD dwKeyState, POINTL pt,
                        DWORD * pdwEffect)
1749
{
1750
    IGenericSFImpl *This = impl_from_IDropTarget(iface);
1751

1752
    TRACE("(%p)->(0x%08lx {.x=%ld, .y=%ld} %p)\n", This, dwKeyState, pt.x, pt.y, pdwEffect);
1753 1754

    if (!pdwEffect)
1755
        return E_INVALIDARG;
1756

1757
    *pdwEffect = KeyStateToDropEffect(dwKeyState) & This->drop_effects_mask;
1758 1759 1760 1761 1762 1763

    return S_OK;
}

static HRESULT WINAPI ISFDropTarget_DragLeave (IDropTarget * iface)
{
1764
    IGenericSFImpl *This = impl_from_IDropTarget(iface);
1765

1766
    TRACE("(%p)\n", This);
1767

1768
    This->drop_effects_mask = DROPEFFECT_NONE;
1769 1770 1771 1772
    return S_OK;
}

static HRESULT WINAPI
1773 1774
ISFDropTarget_Drop (IDropTarget * iface, IDataObject * pDataObject,
                    DWORD dwKeyState, POINTL pt, DWORD * pdwEffect)
1775
{
1776
    IGenericSFImpl *This = impl_from_IDropTarget(iface);
1777 1778 1779 1780
    FORMATETC format;
    STGMEDIUM medium;
    HRESULT hr;

1781
    TRACE("(%p)->(%p %ld {.x=%ld, .y=%ld} %p) semi-stub\n",
1782 1783 1784 1785 1786 1787 1788 1789 1790
        This, pDataObject, dwKeyState, pt.x, pt.y, pdwEffect);

    InitFormatEtc(format, cfShellIDList, TYMED_HGLOBAL);
    hr = IDataObject_GetData(pDataObject, &format, &medium);
    if (FAILED(hr))
        return hr;

    if (medium.tymed == TYMED_HGLOBAL) {
        IShellFolder *psfSourceFolder, *psfDesktopFolder;
1791
        LPIDA pidaShellIDList = GlobalLock(medium.hGlobal);
1792 1793 1794 1795 1796 1797 1798 1799
        STRRET strret;
        UINT i;

        if (!pidaShellIDList)
            return HRESULT_FROM_WIN32(GetLastError());

        hr = SHGetDesktopFolder(&psfDesktopFolder);
        if (FAILED(hr)) {
1800
            GlobalUnlock(medium.hGlobal);
1801 1802 1803 1804 1805 1806 1807
            return hr;
        }

        hr = IShellFolder_BindToObject(psfDesktopFolder, HIDA_GetPIDLFolder(pidaShellIDList), NULL,
                                       &IID_IShellFolder, (LPVOID*)&psfSourceFolder);
        IShellFolder_Release(psfDesktopFolder);
        if (FAILED(hr)) {
1808
            GlobalUnlock(medium.hGlobal);
1809 1810
            return hr;
        }
1811

1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837
        for (i = 0; i < pidaShellIDList->cidl; i++) {
            WCHAR wszSourcePath[MAX_PATH];

            hr = IShellFolder_GetDisplayNameOf(psfSourceFolder, HIDA_GetPIDLItem(pidaShellIDList, i),
                                               SHGDN_FORPARSING, &strret);
            if (FAILED(hr))
                break;

            hr = StrRetToBufW(&strret, NULL, wszSourcePath, MAX_PATH);
            if (FAILED(hr))
                break;

            switch (*pdwEffect) {
                case DROPEFFECT_MOVE:
                    FIXME("Move %s to %s!\n", debugstr_w(wszSourcePath), debugstr_w(This->sPathTarget));
                    break;
                case DROPEFFECT_COPY:
                    FIXME("Copy %s to %s!\n", debugstr_w(wszSourcePath), debugstr_w(This->sPathTarget));
                    break;
                case DROPEFFECT_LINK:
                    FIXME("Link %s from %s!\n", debugstr_w(wszSourcePath), debugstr_w(This->sPathTarget));
                    break;
            }
        }

        IShellFolder_Release(psfSourceFolder);
1838
        GlobalUnlock(medium.hGlobal);
1839 1840
        return hr;
    }
1841 1842 1843 1844

    return E_NOTIMPL;
}

1845
static const IDropTargetVtbl dtvt = {
1846 1847 1848 1849 1850 1851 1852
    ISFDropTarget_QueryInterface,
    ISFDropTarget_AddRef,
    ISFDropTarget_Release,
    ISFDropTarget_DragEnter,
    ISFDropTarget_DragOver,
    ISFDropTarget_DragLeave,
    ISFDropTarget_Drop
1853
};
1854

1855
static HRESULT create_fs( IUnknown *outer_unk, REFIID riid, void **ppv, const CLSID *clsid)
1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872
{
    IGenericSFImpl *sf;
    HRESULT hr;

    TRACE("outer_unk=%p %s\n", outer_unk, shdebugstr_guid(riid));

    if (outer_unk && !IsEqualIID(riid, &IID_IUnknown))
        return CLASS_E_NOAGGREGATION;

    sf = LocalAlloc(LMEM_ZEROINIT, sizeof(*sf));
    if (!sf)
        return E_OUTOFMEMORY;

    sf->ref = 1;
    sf->IUnknown_inner.lpVtbl = &unkvt;
    sf->IShellFolder2_iface.lpVtbl = &sfvt;
    sf->IPersistFolder3_iface.lpVtbl = &pfvt;
1873
    sf->IPersistPropertyBag_iface.lpVtbl = &ppbvt;
1874 1875
    sf->IDropTarget_iface.lpVtbl = &dtvt;
    sf->ISFHelper_iface.lpVtbl = &shvt;
1876
    sf->pclsid = clsid;
1877 1878 1879 1880 1881 1882 1883 1884
    sf->outer_unk = outer_unk ? outer_unk : &sf->IUnknown_inner;

    hr = IUnknown_QueryInterface(&sf->IUnknown_inner, riid, ppv);
    IUnknown_Release(&sf->IUnknown_inner);

    TRACE ("--%p\n", *ppv);
    return hr;
}
1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909

HRESULT WINAPI IFSFolder_Constructor(IUnknown *outer_unk, REFIID riid, void **ppv)
{
    return create_fs( outer_unk, riid, ppv, &CLSID_ShellFSFolder );
}

HRESULT WINAPI UnixFolder_Constructor(IUnknown *outer_unk, REFIID riid, void **ppv)
{
    return create_fs( outer_unk, riid, ppv, &CLSID_UnixFolder );
}

HRESULT WINAPI UnixDosFolder_Constructor(IUnknown *outer_unk, REFIID riid, void **ppv)
{
    return create_fs( outer_unk, riid, ppv, &CLSID_UnixDosFolder );
}

HRESULT WINAPI FolderShortcut_Constructor(IUnknown *outer_unk, REFIID riid, void **ppv)
{
    return create_fs( outer_unk, riid, ppv, &CLSID_FolderShortcut );
}

HRESULT WINAPI MyDocuments_Constructor(IUnknown *outer_unk, REFIID riid, void **ppv)
{
    return create_fs( outer_unk, riid, ppv, &CLSID_MyDocuments );
}