#include "private.h"
#include "offl_cpp.h"
#include <htmlhelp.h>
#include <shdocvw.h>

#include <mluisupp.h>

// {F5175861-2688-11d0-9C5E-00AA00A45957}
const GUID CLSID_OfflineFolder = 
{ 0xf5175861, 0x2688, 0x11d0, { 0x9c, 0x5e, 0x0, 0xaa, 0x0, 0xa4, 0x59, 0x57 } };

// {F5175860-2688-11d0-9C5E-00AA00A45957}
const GUID IID_IOfflineObject = 
{ 0xf5175860, 0x2688, 0x11d0, { 0x9c, 0x5e, 0x0, 0xaa, 0x0, 0xa4, 0x59, 0x57 } };

// Column definition for the Cache Folder DefView
    
ColInfoType s_AllItems_cols[] = {
        {ICOLC_SHORTNAME,   IDS_NAME_COL,           20, LVCFMT_LEFT},
        {ICOLC_LAST,        IDS_LAST_COL,           14, LVCFMT_LEFT},
        {ICOLC_STATUS,      IDS_STATUS_COL,         14, LVCFMT_LEFT},
        {ICOLC_URL,         IDS_URL_COL,            20, LVCFMT_LEFT}, 
        {ICOLC_ACTUALSIZE,  IDS_SIZE_COL,           10, LVCFMT_LEFT}};

ColInfoType * colInfo = s_AllItems_cols;

LPMYPIDL _CreateFolderPidl(IMalloc *pmalloc, DWORD dwSize);

HRESULT OfflineFolderView_InitMenuPopup(HWND hwnd, UINT idCmdFirst, int nIndex, HMENU hMenu)
{
    UINT platform = WhichPlatform();

    if (platform != PLATFORM_INTEGRATED)    {
        MENUITEMINFO    mInfo = {0};
        mInfo.cbSize = sizeof(MENUITEMINFO);
        mInfo.fMask = MIIM_STATE;
        if (IsGlobalOffline())  {
            mInfo.fState = MFS_CHECKED;
        } else  {
            mInfo.fState = MFS_UNCHECKED;
        }
        SetMenuItemInfo(hMenu, RSVIDM_WORKOFFLINE + idCmdFirst, FALSE, &mInfo);
    }

    return NOERROR;
}

HRESULT OfflineFolderView_MergeMenu(LPQCMINFO pqcm)
{
    HMENU hmenu = NULL;
    UINT  platform = WhichPlatform();
    
    if (platform == PLATFORM_INTEGRATED) {
        hmenu = LoadMenu(MLGetHinst(), MAKEINTRESOURCE(MENU_OFFLINE_TOP));
    } else  {
        hmenu = LoadMenu(MLGetHinst(), MAKEINTRESOURCE(MENU_OFFLINE_BRONLY));
    }

    if (hmenu)
    {
        MergeMenuHierarchy(pqcm->hmenu, hmenu, pqcm->idCmdFirst, pqcm->idCmdLast, TRUE);
        DestroyMenu(hmenu);
    }

    return S_OK;
}

extern HRESULT CancelAllDownloads();

HRESULT OfflineFolderView_Command(HWND hwnd, UINT uID)
{
    switch (uID) {
    case RSVIDM_SORTBYNAME:
        ShellFolderView_ReArrange(hwnd, ICOLC_SHORTNAME);
        break;
    case RSVIDM_UPDATEALL:
        SendUpdateRequests(hwnd, NULL, 0);
        break;
    case RSVIDM_WORKOFFLINE:
        SetGlobalOffline(!IsGlobalOffline());
        break;
    case RSVIDM_HELP:
        SHHtmlHelpOnDemandWrap(hwnd, TEXT("iexplore.chm > iedefault"), 0, (DWORD_PTR) TEXT("subs_upd.htm"), ML_CROSSCODEPAGE);
        break;

    case RSVIDM_UPDATE:
        {
            LPMYPIDL *  pidlsSel = NULL;
            UINT    count = 0;

            count = (UINT) ShellFolderView_GetSelectedObjects
                                (hwnd, (LPITEMIDLIST*) &pidlsSel);

            if ((!pidlsSel) || !count)
                break;

            CLSID   * cookies = (CLSID *)MemAlloc(LPTR, count * sizeof(CLSID));
            UINT    validCount = 0;

            if (cookies)
            {
                for (UINT i = 0; i < count; i ++)   {
                    if (IS_VALID_MYPIDL(pidlsSel[i]))
                        cookies[validCount++] = pidlsSel[i]->ooe.m_Cookie;
                }

                if (validCount)
                    SendUpdateRequests(hwnd, cookies, validCount);
            
                MemFree(cookies);
                cookies = NULL;
            }
            break;
        }
    default:
        return E_FAIL;
    }
    return NOERROR;
}

//  We should make this a generic function for all types of items, even
//  for the third party items they should support these properties.

HRESULT Generic_GetDetails(PDETAILSINFO pdi, UINT iColumn)
{
    LPMYPIDL pooi = (LPMYPIDL)pdi->pidl;
    POOEntry pooe = NULL;
    TCHAR timeSTR[128];

    pdi->str.uType = STRRET_CSTR;
    pdi->str.cStr[0] = '\0';

    pooe = &(pooi->ooe);
    switch (iColumn)
    {
    case ICOLC_SHORTNAME:
        SHTCharToAnsi(NAME(pooe), pdi->str.cStr, sizeof(pdi->str.cStr));
        break;
    case ICOLC_URL:
        SHTCharToAnsi(URL(pooe), pdi->str.cStr, sizeof(pdi->str.cStr));
        break;
    case ICOLC_LAST:
        DATE2DateTimeString(pooe->m_LastUpdated, timeSTR);
        SHTCharToAnsi(timeSTR, pdi->str.cStr, sizeof(pdi->str.cStr));
        break;
    case ICOLC_STATUS:
        SHTCharToAnsi(STATUS(pooe), pdi->str.cStr, sizeof(pdi->str.cStr));
        break;
    case ICOLC_ACTUALSIZE:
        StrFormatByteSizeA(pooe->m_ActualSize * 1024, pdi->str.cStr, sizeof(pdi->str.cStr));
        break;
    }

    return S_OK;
}

HRESULT OfflineFolderView_OnGetDetailsOf(HWND hwnd, UINT iColumn, PDETAILSINFO pdi)
{
    LPMYPIDL pooi = (LPMYPIDL)pdi->pidl;

    if (iColumn > ICOLC_ACTUALSIZE)
        return E_NOTIMPL;

    if (!pooi)
    {
        pdi->str.uType = STRRET_CSTR;
        pdi->str.cStr[0] = '\0';
        MLLoadStringA(colInfo[iColumn].ids, pdi->str.cStr, sizeof(pdi->str.cStr));
        pdi->fmt = colInfo[iColumn].iFmt;
        pdi->cxChar = colInfo[iColumn].cchCol;
        return S_OK;
    }

    UINT    colId = colInfo[iColumn].iCol;
    return Generic_GetDetails(pdi, colId);
}

HRESULT OfflineFolderView_OnColumnClick(HWND hwnd, UINT iColumn)
{
    ShellFolderView_ReArrange(hwnd, colInfo[iColumn].iCol);
    return NOERROR;
}

const TBBUTTON c_tbOffline[] = {
    { 0, RSVIDM_UPDATE,       TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
    { 1, RSVIDM_UPDATEALL,    TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
    { 0,  0,                  TBSTATE_ENABLED, TBSTYLE_SEP   , {0,0}, 0L, -1 },
    };

HRESULT OfflineFolderView_OnGetButtons(HWND hwnd, UINT idCmdFirst, LPTBBUTTON ptButton)
{
    UINT i;
    LONG_PTR iBtnOffset;
    IShellBrowser * psb = FileCabinet_GetIShellBrowser(hwnd);
    TBADDBITMAP ab;

    // add the toolbar button bitmap, get it's offset
    ab.hInst =g_hInst;
    ab.nID   = IDB_TB_SMALL;        // std bitmaps
    psb->SendControlMsg(FCW_TOOLBAR, TB_ADDBITMAP, 2, (LPARAM)&ab, &iBtnOffset);

    for (i = 0; i < ARRAYSIZE(c_tbOffline); i++)
    {
        ptButton[i] = c_tbOffline[i];

        if (!(c_tbOffline[i].fsStyle & TBSTYLE_SEP))
        {
            ptButton[i].idCommand += idCmdFirst;
            ptButton[i].iBitmap += (int)iBtnOffset;
        }
    }

    return S_OK;
}

HRESULT OfflineFolderView_OnGetButtonInfo(TBINFO * ptbInfo)
{
    ptbInfo->uFlags = TBIF_PREPEND;
    ptbInfo->cbuttons = ARRAYSIZE(c_tbOffline);
    return S_OK;
}

HRESULT OfflineFolderView_DidDragDrop(HWND hwnd,IDataObject *pdo,DWORD dwEffect)
{
    if ((dwEffect & (DROPEFFECT_MOVE |DROPEFFECT_COPY)) == DROPEFFECT_MOVE)
    {
        COfflineObjectItem *pOOItem;
        
        if (SUCCEEDED(pdo->QueryInterface(IID_IOfflineObject, (void **)&pOOItem)))
        {
            BOOL fDel = ConfirmDelete(hwnd, pOOItem->_cItems, pOOItem->_ppooi);
            if (!fDel)  {
                pOOItem->Release();
                return S_FALSE;
            }

            for (UINT i = 0; i < pOOItem->_cItems; i++)
            {
                if (SUCCEEDED(DoDeleteSubscription(&(pOOItem->_ppooi[i]->ooe)))) {
                    _GenerateEvent(SHCNE_DELETE, 
                            (LPITEMIDLIST)pOOItem->_ppooi[i], 
                            NULL);
                }
            }

            pOOItem->Release();
            return S_OK;
        }
    }
    return E_FAIL;
}

HRESULT CALLBACK OfflineFolderView_ViewCallback(
     IShellView *psvOuter,
     IShellFolder *psf,
     HWND hwnd,
     UINT uMsg,
     WPARAM wParam,
     LPARAM lParam)
{
    HRESULT hres = NOERROR;

    switch (uMsg)
    {
    case DVM_GETHELPTEXT:
    case DVM_GETTOOLTIPTEXT:
    {
        UINT id = LOWORD(wParam);
        UINT cchBuf = HIWORD(wParam);
        if (g_fIsWinNT)   
        {
            WCHAR * pszBuf = (WCHAR *)lParam;
            MLLoadStringW(id + IDS_SB_FIRST, pszBuf, cchBuf);
        }
        else  
        {
            CHAR * pszBuf = (CHAR *)lParam;
            MLLoadStringA(id + IDS_SB_FIRST, pszBuf, cchBuf);
        }
    }   
        break;

    case DVM_DIDDRAGDROP:
        hres = OfflineFolderView_DidDragDrop(hwnd,(IDataObject *)lParam,(DWORD)wParam);
        break;
      
    case DVM_INITMENUPOPUP:
        hres = OfflineFolderView_InitMenuPopup(hwnd, LOWORD(wParam), HIWORD(wParam), (HMENU)lParam);
        break;

    case DVM_INVOKECOMMAND:
        OfflineFolderView_Command(hwnd, (UINT)wParam);
        break;

    case DVM_COLUMNCLICK:
        hres = OfflineFolderView_OnColumnClick(hwnd, (UINT)wParam);
        break;

    case DVM_GETDETAILSOF:
        hres = OfflineFolderView_OnGetDetailsOf(hwnd, (UINT)wParam, (PDETAILSINFO)lParam);
        break;

    case DVM_MERGEMENU:
        hres = OfflineFolderView_MergeMenu((LPQCMINFO)lParam);
        break;

    case DVM_DEFVIEWMODE:
        *(FOLDERVIEWMODE *)lParam = FVM_DETAILS;
        break;

    case DVM_GETBUTTONINFO:
        hres = OfflineFolderView_OnGetButtonInfo((TBINFO *)lParam);
        break;

    case DVM_GETBUTTONS:
        hres = OfflineFolderView_OnGetButtons(hwnd, LOWORD(wParam), (TBBUTTON *)lParam);
        break;

    default:
        hres = E_FAIL;
    }

    return hres;
}

HRESULT OfflineFolderView_CreateInstance(COfflineFolder *pOOFolder, LPCITEMIDLIST pidl, void **ppvOut)
{
    CSFV csfv;

    csfv.cbSize = sizeof(csfv);
    csfv.pshf = (IShellFolder *)pOOFolder;
    csfv.psvOuter = NULL;
    csfv.pidl = pidl;
    csfv.lEvents = SHCNE_DELETE | SHCNE_CREATE | SHCNE_RENAMEITEM | SHCNE_UPDATEITEM | SHCNE_UPDATEDIR;
    csfv.pfnCallback = OfflineFolderView_ViewCallback;
    csfv.fvm = (FOLDERVIEWMODE)0;         // Have defview restore the folder view mode

    return SHCreateShellFolderViewEx(&csfv, (IShellView**)ppvOut); // &this->psv);
}



//////////////////////////////////////////////////////////////////////////////
//
// COfflineFolderEnum Object
//
//////////////////////////////////////////////////////////////////////////////


COfflineFolderEnum::COfflineFolderEnum(DWORD grfFlags)
{
    TraceMsg(TF_SUBSFOLDER, "hcfe - COfflineFolderEnum() called");
    
    m_cRef = 1;
    DllAddRef();

    m_grfFlags = grfFlags;
}

IMalloc *COfflineFolderEnum::s_pMalloc = NULL;

void COfflineFolderEnum::EnsureMalloc()
{
    if (NULL == s_pMalloc)
    {
        SHGetMalloc(&s_pMalloc);
    }

    ASSERT(NULL != s_pMalloc);
}


COfflineFolderEnum::~COfflineFolderEnum()
{
    ASSERT(m_cRef == 0);         // we should always have a zero ref count here

    SAFERELEASE(m_pFolder);
    SAFEDELETE(m_pCookies);

    TraceMsg(TF_SUBSFOLDER, "hcfe - ~COfflineFolderEnum() called.");
    DllRelease();
}

HRESULT COfflineFolderEnum::Initialize(COfflineFolder *pFolder)
{
    HRESULT hr = S_OK;

    ASSERT(pFolder);
    
    if (NULL != pFolder)
    {
        m_pFolder = pFolder;
        m_pFolder->AddRef();

        hr = CoInitialize(NULL);

        if (SUCCEEDED(hr))
        {
            ISubscriptionMgr2 *pSubsMgr2;
            hr = CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER, 
                                  IID_ISubscriptionMgr2, (void **)&pSubsMgr2);
            if (SUCCEEDED(hr))
            {
                IEnumSubscription *pes;

                hr = pSubsMgr2->EnumSubscriptions(0, &pes);
                if (SUCCEEDED(hr))
                {
                    pes->GetCount(&m_nCount);

                    if (m_nCount > 0)
                    {
                        m_pCookies = new SUBSCRIPTIONCOOKIE[m_nCount];

                        if (NULL != m_pCookies)
                        {
                            hr = pes->Next(m_nCount, m_pCookies, &m_nCount);
                        }
                        else
                        {
                            hr = E_OUTOFMEMORY;
                        }
                    }
                    pes->Release();
                }
                pSubsMgr2->Release();
            }

            CoUninitialize();
        }
    }
    else
    {
        hr = E_INVALIDARG;
    }
    
    return hr;
}

HRESULT COfflineFolderEnum_CreateInstance(DWORD grfFlags, COfflineFolder *pFolder, 
                                          LPENUMIDLIST *ppeidl)
{
    HRESULT hr;

    *ppeidl = NULL;

    COfflineFolderEnum *pOOFE = new COfflineFolderEnum(grfFlags);
    
    if (NULL != pOOFE)
    {
        hr = pOOFE->Initialize(pFolder);

        if (SUCCEEDED(hr))
        {
            *ppeidl = pOOFE;
        }
        else
        {
            pOOFE->Release();
        }
    }
    else
    {
        hr = E_OUTOFMEMORY;
    }
    
    return hr;
}


//////////////////////////////////
//
// IUnknown Methods...
//

HRESULT COfflineFolderEnum::QueryInterface(REFIID iid,void **ppv)
{
//    TraceMsg(TF_SUBSFOLDER, "COfflineFolderEnum - QI called.");
    
    if ((iid == IID_IEnumIDList) || (iid == IID_IUnknown))
    {
        *ppv = (IEnumIDList *)this;
        AddRef();
        return S_OK;
    }
    
    *ppv = NULL;
    return E_NOINTERFACE;
}

ULONG COfflineFolderEnum::AddRef(void)
{
    return ++m_cRef;
}

ULONG COfflineFolderEnum::Release(void)
{
    if (0L != --m_cRef)
        return m_cRef;

    delete this;
    return 0;
}

LPMYPIDL COfflineFolderEnum::NewPidl(DWORD dwSize)
{
    LPMYPIDL pidl;

//  TraceMsg(TF_MEMORY, "NewPidl called");

    EnsureMalloc();

    pidl = _CreateFolderPidl(s_pMalloc, dwSize);

//  TraceMsg(TF_MEMORY, "\tNewPidl returned with 0x%x", pidl);

    return pidl;
}

void COfflineFolderEnum::FreePidl(LPMYPIDL pidl)
{
    ASSERT(NULL != pidl);

//  TraceMsg(TF_MEMORY, "FreePidl on (0x%x) called", pidl);

    EnsureMalloc();

    s_pMalloc->Free(pidl);
}

// IEnumIDList Methods 

HRESULT COfflineFolderEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
{
    HRESULT hr = S_OK;
    ULONG nCopied;
    DWORD dwBuffSize;
    OOEBuf ooeBuf;

    if ((0 == celt) || 
        ((celt > 1) && (NULL == pceltFetched)) ||
        (NULL == rgelt))
    {
        return E_INVALIDARG;
    }

    memset(&ooeBuf, 0, sizeof(ooeBuf));

    for (nCopied = 0; (S_OK == hr) && (m_nCurrent < m_nCount) && (nCopied < celt); 
         m_nCurrent++, nCopied++)
    {
        rgelt[nCopied] = NULL;
        hr = LoadOOEntryInfo(&ooeBuf, &m_pCookies[m_nCurrent], &dwBuffSize);

        if (SUCCEEDED(hr))
        {
            if (IsNativeAgent(ooeBuf.clsidDest))
            {
                CLSID   cookie;
                HRESULT hrTmp = ReadCookieFromInetDB(ooeBuf.m_URL, &cookie);
                if (S_OK != hrTmp)
                {
                    hrTmp = WriteCookieToInetDB(ooeBuf.m_URL,&(ooeBuf.m_Cookie), FALSE);
                    ASSERT(SUCCEEDED(hrTmp));
                }
            }

            LPMYPIDL pooi = NewPidl(dwBuffSize);
            if (pooi)
            {
                CopyToMyPooe(&ooeBuf, &(pooi->ooe));  //  Always succeeds!
                rgelt[nCopied] = (LPITEMIDLIST)pooi;
            }
            else 
            {
                hr = E_OUTOFMEMORY;
            }
        }
    }

    if (SUCCEEDED(hr))
    {
        hr = (celt == nCopied) ? S_OK : S_FALSE;
    }
    else
    {
        for (ULONG i = 0; i < nCopied; i++)
        {
            FreePidl((LPMYPIDL)rgelt[i]);
        }
    }

    if (NULL != pceltFetched)
    {
        *pceltFetched = SUCCEEDED(hr) ? nCopied : 0;
    }
    
    return hr;
}

HRESULT COfflineFolderEnum::Skip(ULONG celt)
{
    HRESULT hr;
    
    m_nCurrent += celt;

    if (m_nCurrent > (m_nCount - 1))
    {
        m_nCurrent = m_nCount;  //  Passed the last one
        hr = S_FALSE;
    }
    else
    {
        hr = S_OK;
    }
    
    return hr;
}

HRESULT COfflineFolderEnum::Reset()
{
    m_nCurrent = 0;

    return S_OK;
}

HRESULT COfflineFolderEnum::Clone(IEnumIDList **ppenum)
{
    return E_NOTIMPL;
}

//////////////////////////////////////////////////////////////////////////////
//
// COfflineFolder Object
//
//////////////////////////////////////////////////////////////////////////////

COfflineFolder::COfflineFolder(void) 
{
    TraceMsg(TF_SUBSFOLDER, "Folder - COfflineFolder() called.");
    _cRef = 1;
    viewMode = 0;
    colInfo = s_AllItems_cols;
    DllAddRef();
}       

COfflineFolder::~COfflineFolder()
{
    Assert(_cRef == 0);                 // should always have zero
    TraceMsg(TF_SUBSFOLDER, "Folder - ~COfflineFolder() called.");

    if (_pidl)
        ILFree(_pidl);
        
    DllRelease();
}    

//////////////////////////////////
//
// IUnknown Methods...
//
HRESULT COfflineFolder::QueryInterface(REFIID iid, void **ppvObj)
{
    *ppvObj = NULL;     // null the out param
    
    if (iid == IID_IUnknown) {
         *ppvObj = (void *)this;
    }
    else if (iid == IID_IShellFolder) {
         *ppvObj = (void *)(IShellFolder *)this;
    }
    else if ((iid == IID_IPersistFolder) || (iid == IID_IPersist) || (iid == IID_IPersistFolder2)) {
         *ppvObj = (void *)(IPersistFolder *)this;
    }
    else if (iid == IID_IContextMenu)
    {
         *ppvObj = (void *)(IContextMenu *)this;
    }
    else if (iid == IID_IShellView)
    {
         return OfflineFolderView_CreateInstance(this, _pidl, ppvObj);
    }
    else if (iid == IID_IOfflineObject)
    {
         *ppvObj = (void *)this;
    }
    else if (iid == IID_IDropTarget)
    {
        // APPCOMPAT: Implementation of IDropTarget didn't follow the COM rules.
        //  We create following object by aggregattion but QI on it for IUnknown
        //  won't get us ptr THIS.
        COfflineDropTarget * podt = new COfflineDropTarget(GetDesktopWindow());
        if (podt)
        {
            HRESULT hr = podt->QueryInterface(iid, ppvObj);
            podt->Release();
        }
        else
        {
            return E_OUTOFMEMORY;
        }     
    }

    if (*ppvObj) 
    {
        ((IUnknown *)*ppvObj)->AddRef();
        return S_OK;
    }

    return E_NOINTERFACE;
}

ULONG COfflineFolder::AddRef()
{
    return ++_cRef;
}

ULONG COfflineFolder::Release()
{
    if (0L != --_cRef)
        return _cRef;

    delete this;
    return 0;   
}

//////////////////////////////////
//
// IShellFolder methods...
//
HRESULT COfflineFolder::ParseDisplayName(HWND hwndOwner, LPBC pbcReserved,
                        LPOLESTR lpszDisplayName, ULONG *pchEaten,
                        LPITEMIDLIST *ppidl, ULONG *pdwAttributes)
{
//    TraceMsg(TF_SUBSFOLDER, "Folder:ISF - ParseDisplayName.");
    *ppidl = NULL;
    return E_FAIL;
}


HRESULT COfflineFolder::EnumObjects(HWND hwndOwner, DWORD grfFlags,
                        LPENUMIDLIST *ppenumIDList)
{
//    TraceMsg(TF_SUBSFOLDER, "Folder:ISF - EnumObjects.");
    return COfflineFolderEnum_CreateInstance(grfFlags, this, ppenumIDList);
}


HRESULT COfflineFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbcReserved,
                        REFIID riid, void **ppvOut)
{
//    TraceMsg(TF_SUBSFOLDER, "Folder:ISF - BindToObject.");
    *ppvOut = NULL;
    return E_FAIL;
}

HRESULT COfflineFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbcReserved,
                        REFIID riid, void **ppvObj)
{
//    TraceMsg(TF_SUBSFOLDER, "Folder:ISF - BindToStorage.");
    *ppvObj = NULL;
    return E_NOTIMPL;
}

HRESULT COfflineFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
    int iRet;

//    TraceMsg(TF_SUBSFOLDER, "Folder:ISF - CompareIDs(%d).", lParam);

    if (!IS_VALID_MYPIDL(pidl1) || !IS_VALID_MYPIDL(pidl2))
        return E_FAIL;

    switch (lParam) {
        case ICOLC_SHORTNAME:
            iRet = _CompareShortName((LPMYPIDL)pidl1, (LPMYPIDL)pidl2);
            break;
        case ICOLC_URL:
            iRet = _CompareURL((LPMYPIDL)pidl1, (LPMYPIDL)pidl2);
            break;
        case ICOLC_STATUS:
            iRet = _CompareStatus((LPMYPIDL)pidl1, (LPMYPIDL)pidl2);
            break;
        case ICOLC_LAST:
            iRet = _CompareLastUpdate((LPMYPIDL)pidl1, (LPMYPIDL)pidl2);
            break;
        case ICOLC_ACTUALSIZE:
            iRet = (((LPMYPIDL)pidl1)->ooe.m_ActualSize - ((LPMYPIDL)pidl2)->ooe.m_ActualSize);
            break;
        default:
            iRet = -1;
            break;
    }
    return ResultFromShort((SHORT)iRet);
}


HRESULT COfflineFolder::CreateViewObject(HWND hwndOwner, REFIID riid, void **ppvOut)
{
    HRESULT hres;

//    TraceMsg(TF_SUBSFOLDER, "Folder:ISF - CreateViewObject() called.");

    if (riid == IID_IShellView)
    {
        hres = OfflineFolderView_CreateInstance(this, _pidl, ppvOut);
    }
    else if (riid == IID_IContextMenu)
    {
        COfflineFolder * pof = new COfflineFolder();

        if (pof)
        {
            hres = pof->Initialize(this->_pidl);
            if (SUCCEEDED(hres))
                hres = pof->QueryInterface(riid, ppvOut);
            pof->Release();
        }
        else
        {
            return E_OUTOFMEMORY;
        }
    }
    else if (riid == IID_IDropTarget)
    {
        COfflineDropTarget * podt = new COfflineDropTarget(hwndOwner);

        if (podt)
        {
            hres = podt->QueryInterface(riid, ppvOut);
            podt->Release();
        }
        else
        {
            return E_OUTOFMEMORY;
        }
    }
    else if (riid == IID_IShellDetails)
    {
        COfflineDetails *pod = new COfflineDetails(hwndOwner);
        if (NULL != pod)
        {
            hres = pod->QueryInterface(IID_IShellDetails, ppvOut);
            pod->Release();
        }
        else
        {
            hres = E_OUTOFMEMORY;
        }
    }
    else
    {
        DBGIID("COfflineFolder::CreateViewObject() failed", riid);
        *ppvOut = NULL;         // null the out param
        hres = E_NOINTERFACE;
    }
    
    return hres;    
}

HRESULT COfflineFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl,
                        ULONG * prgfInOut)
{
    // Should we initialize this for each item in here?  In other words,
    // if cidl > 1, then we should initialize each entry in the prgInOut array
    Assert( cidl == 1 );
    
    UINT    attr = SFGAO_CANCOPY | SFGAO_CANDELETE | SFGAO_CANRENAME |
                    SFGAO_HASPROPSHEET;
    *prgfInOut = attr;
    
    return NOERROR;
}

HRESULT COfflineFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl,
                        REFIID riid, UINT * prgfInOut, void **ppvOut)
{
    HRESULT hres;

//    TraceMsg(TF_SUBSFOLDER, "Folder:ISF - GetUIObjectOf.");
    if ((riid == IID_IContextMenu) || (riid == IID_IDataObject) || 
        (riid == IID_IExtractIcon) || (riid == IID_IQueryInfo))
    {
        hres = COfflineObjectItem_CreateInstance(this, cidl, apidl, riid, ppvOut);
    }
    else if (riid == IID_IDropTarget)
    {
        hres = CreateViewObject(hwndOwner, IID_IDropTarget, ppvOut);
    }
    else 
    {
        *ppvOut = NULL;
        hres = E_FAIL;
        DBGIID("Unsupported interface in COfflineFolder::GetUIObjectOf()", riid);
    }
    return hres;    
}

HRESULT COfflineFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName)
{
//    TraceMsg(TF_SUBSFOLDER, "Folde:ISF - GetDisplayNameOf.");
    
    if (!IS_VALID_MYPIDL(pidl))
    {
        lpName->uType = 0;
        return E_FAIL;
    }

    lpName->uType = STRRET_CSTR;
    lpName->cStr[0] = '\0';

    PCTSTR pszNameLocal;
    LPTSTR szNameUnaligned = NAME(&(((LPMYPIDL)pidl)->ooe));

    TSTR_ALIGNED_STACK_COPY( &pszNameLocal, szNameUnaligned );

    SHTCharToAnsi( pszNameLocal, lpName->cStr, ARRAYSIZE(lpName->cStr) );

    TraceMsg(TF_ALWAYS, "COfflineFolder::GetDisplayNameOf() - pszNameLocal='%s'", pszNameLocal );

    TraceMsg(TF_ALWAYS, "COfflineFolder::GetDisplayNameOf() - lpName->cStr='%S'", lpName->cStr );

    return NOERROR;    
}

HRESULT COfflineFolder::SetNameOf(HWND hwndOwner, LPCITEMIDLIST pidl,
                        LPCOLESTR lpszName, DWORD uFlags, LPITEMIDLIST * ppidlOut)
{
    OOEBuf  ooeBuf;
    POOEntry    pooe = NULL;
//    TraceMsg(TF_SUBSFOLDER, "Folde:ISF - SetNameOf.");
    
    if (ppidlOut)  {
        *ppidlOut = NULL;               // null the out param
    }

    if (!IS_VALID_MYPIDL(pidl))
        return E_FAIL;

    HRESULT hr;
    WCHAR szTempName[MAX_PATH];

    ASSERT(lpszName);

    StrCpyNW(szTempName, lpszName, ARRAYSIZE(szTempName));

    PathRemoveBlanks(szTempName);

    if (szTempName[0])
    {   
        memset(&ooeBuf, 0, sizeof(ooeBuf));

        pooe = &(((LPMYPIDL)pidl)->ooe);
        CopyToOOEBuf(pooe, &ooeBuf);
        MyOleStrToStrN(ooeBuf.m_Name, MAX_NAME, szTempName);

        ooeBuf.dwFlags = PROP_WEBCRAWL_NAME;
        hr = SaveBufferChange(&ooeBuf, FALSE);
        
        if (ppidlOut)   {
            DWORD   dwSize = BufferSize(&ooeBuf);
            *ppidlOut = (LPITEMIDLIST)COfflineFolderEnum::NewPidl(dwSize);
            if (*ppidlOut)  {
                pooe = &(((LPMYPIDL)(*ppidlOut))->ooe);
                CopyToMyPooe(&ooeBuf, pooe);
            }
        }
    }
    else
    {
        WCMessageBox(hwndOwner, IDS_NONULLNAME, IDS_RENAME, MB_OK | MB_ICONSTOP);
        hr = E_FAIL;
    }
    return hr;    
}

//////////////////////////////////
//
// IPersistFolder Methods...
//
HRESULT COfflineFolder::GetClassID(LPCLSID lpClassID)
{
//    TraceMsg(TF_SUBSFOLDER, "hcf - pf - GetClassID.");
    
    *lpClassID = CLSID_OfflineFolder;
    return S_OK;
}


HRESULT COfflineFolder::Initialize(LPCITEMIDLIST pidlInit)
{
    if (_pidl)
        ILFree(_pidl);

    _pidl = ILClone(pidlInit);

    if (!_pidl)
        return E_OUTOFMEMORY;

    return NOERROR;
}

HRESULT COfflineFolder::GetCurFolder(LPITEMIDLIST *ppidl)
{
    if (_pidl)
    {
        *ppidl = ILClone(_pidl);
        return *ppidl ? NOERROR : E_OUTOFMEMORY;
    }

    *ppidl = NULL;      
    return S_FALSE; // success but empty
}

//////////////////////////////////
//
// IContextMenu Methods...
//
HRESULT COfflineFolder::QueryContextMenu
(
    HMENU hmenu, 
    UINT indexMenu, 
    UINT idCmdFirst,
    UINT idCmdLast, 
    UINT uFlags)
{
    USHORT cItems = 0;

//    TraceMsg(TF_SUBSFOLDER, "Folder:IContextMenu- QueryContextMenu.");
    if (uFlags == CMF_NORMAL)
    {
        HMENU hmenuHist = LoadMenu(MLGetHinst(), MAKEINTRESOURCE(CONTEXT_MENU_OFFLINE));
        if (hmenuHist)
        {
            cItems = (USHORT) MergeMenuHierarchy(hmenu, hmenuHist, idCmdFirst, idCmdLast, TRUE);

            DestroyMenu(hmenuHist);
        }
    }
    
    return ResultFromShort(cItems);    // number of menu items    
}

STDMETHODIMP COfflineFolder::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
{
//    TraceMsg(TF_SUBSFOLDER, "Folder:IContextMenu - InvokeCommand.");
    
    int idCmd = _GetCmdID(pici->lpVerb);
    
    if (idCmd != RSVIDM_PASTE)
        return OfflineFolderView_Command(pici->hwnd, idCmd);
     
    IDataObject * dataSrc = NULL;
    IDropTarget * pDropTrgt = NULL;
    HRESULT hr;

    hr = OleGetClipboard(&(dataSrc));

    if (SUCCEEDED(hr))
        hr = this->QueryInterface(IID_IDropTarget, (void **) &pDropTrgt);

    if (SUCCEEDED(hr))  {
        DWORD dwPrefEffect = DROPEFFECT_COPY;
        POINTL pt = {0, 0};

        hr = pDropTrgt->DragEnter(dataSrc, 0/*keystate*/, pt, &dwPrefEffect);
        if (SUCCEEDED(hr))  {
            hr = pDropTrgt->Drop(dataSrc, 0, pt, &dwPrefEffect);
        }
    }

    if (dataSrc)
        SAFERELEASE(dataSrc);
    if (pDropTrgt)
        SAFERELEASE(pDropTrgt);

    return hr;
}

STDMETHODIMP COfflineFolder::GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT *pwReserved,
                                LPSTR pszName, UINT cchMax)
{
    HRESULT hres = E_FAIL;

//    TraceMsg(TF_SUBSFOLDER, "Folder:IContextMenu - GetCommandString.");
    if (uFlags == GCS_HELPTEXTA)
    {
        MLLoadStringA((UINT)idCmd + IDS_SB_FIRST, pszName, cchMax);
        hres = NOERROR;
    }
    return hres;
}

COfflineDetails::COfflineDetails(HWND hwndOwner)
{
    ASSERT(NULL != hwndOwner);
    m_hwndOwner = hwndOwner;
    m_cRef = 1;
}

STDMETHODIMP COfflineDetails::QueryInterface(REFIID riid, void **ppv)
{
    if ((IID_IUnknown == riid) || (IID_IShellDetails == riid))
    {
        *ppv = (IShellDetails *)this;
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
    AddRef();
    return S_OK;
}

STDMETHODIMP_(ULONG) COfflineDetails::AddRef()
{
    return ++m_cRef;
}

STDMETHODIMP_(ULONG) COfflineDetails::Release()
{
    if (--m_cRef == 0)
    {
        delete this;
        return 0;
    }

    return m_cRef;
}

STDMETHODIMP COfflineDetails::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, LPSHELLDETAILS pDetails)
{
    HRESULT hr;

    if (iColumn > ICOLC_ACTUALSIZE)
        return E_NOTIMPL;

    if (NULL == pDetails)
    {
        return E_INVALIDARG;
    }
    
    if (NULL != pidl)
    {
        DETAILSINFO di = { pidl };
        hr = Generic_GetDetails(&di, colInfo[iColumn].iCol);
        pDetails->fmt = di.fmt;
        pDetails->cxChar = di.cxChar;
        memcpy(&pDetails->str, &di.str, sizeof(di.str));
    }
    else
    {
        pDetails->str.uType = STRRET_CSTR;
        pDetails->str.cStr[0] = '\0';
        MLLoadStringA(colInfo[iColumn].ids, pDetails->str.cStr, sizeof(pDetails->str.cStr));
        pDetails->fmt = colInfo[iColumn].iFmt;
        pDetails->cxChar = colInfo[iColumn].cchCol;
        hr = S_OK;
    }

    return hr;
}

STDMETHODIMP COfflineDetails::ColumnClick(UINT iColumn)
{
    ShellFolderView_ReArrange(m_hwndOwner, colInfo[iColumn].iCol);
    return S_OK;
}


LPMYPIDL _CreateFolderPidl(IMalloc *pmalloc, DWORD dwSize)
{
    LPMYPIDL pooi = (LPMYPIDL)pmalloc->Alloc(sizeof(MYPIDL) + dwSize + sizeof(USHORT));
    if (pooi)
    {
        memset(pooi, 0, sizeof(MYPIDL) + dwSize + sizeof(USHORT));
        pooi->cb = (USHORT)(dwSize + sizeof(MYPIDL));
        pooi->usSign = (USHORT)MYPIDL_MAGIC;
//      TraceMsg(TF_MEMORY, "CreatePidl %d", sizeof(MYPIDL) + dwSize);
    }
    return pooi;
}
