#include "stdafx.h"
#include "icwcfg.h"
#pragma hdrstop

EXTERN_C const TCHAR c_szPatterns[] = TEXT("patterns");
EXTERN_C const TCHAR c_szBackgroundPreview2[] = TEXT("BackgroundPreview2");
EXTERN_C const TCHAR c_szComponentPreview[] = TEXT("ComponentPreview");
EXTERN_C const TCHAR c_szRegDeskHtmlProp[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Controls Folder\\Display\\shellex\\PropertySheetHandlers\\DeskHtmlExt");
EXTERN_C const TCHAR c_szWallPaperDir[] = TEXT("WallPaperDir");

//  98/10/01 vtan: Added local function prototypes.

//  Some of these functions are commented out. The linker may not be smart
//  enough to strip the dead code so this is done manually. These prototypes
//  will allow the code to compile but it won't link. If you get linker
//  errors, uncomment the desired function and recompile. It should then link.

//  Point arithmetic

void    SetPt (POINT& pt, LONG x, LONG y);
void    OffsetPt (POINT& pt, LONG dh, LONG dv);

//  Virtual screen calculation

BOOL    CALLBACK    GDIToTridentEnumProc (HMONITOR hMonitor, HDC hDC, RECT* rcMonitor, LPARAM lpUserData);
void    CalculateVirtualScreen (RECT& rcVirtualScreen);

//  GDI point to Trident point co-ordinate mapping

void    GDIToTrident (int& leftCoordinate, int& topCoordinate);
void    GDIToTrident (POINT& pt);
void    GDIToTrident (RECT& r);
void    GDIToTrident (HRGN hRgn);
void    TridentToGDI (int& leftCoordinate, int& topCoordinate);
void    TridentToGDI (POINT& pt);
void    TridentToGDI (RECT& r);
void    TridentToGDI (HRGN hRgn);

//  Component position validation

BOOL    CALLBACK    ValidateComponentPositionEnumProc (HMONITOR hMonitor, HDC hdcMonitor, RECT* r, LPARAM lParam);

void    GetNextComponentPosition (COMPPOS *pcp)

{
    int     iScreenWidth, iScreenHeight, iBorderSize;
    DWORD   dwComponentPosition, dwComponentLayer, dwRegDataScratch;
    HKEY    hKey;
    RECT    rcScreen;
    TCHAR   lpszDeskcomp[MAX_PATH];

    TBOOL(SystemParametersInfo(SPI_GETWORKAREA, 0, &rcScreen, false));

    // 99/04/13 vtan: A result of zero-width or zero-height occurred on a machine.
    // Make a defensive stand against this and assert that this happened but also
    // handle this cause so that division by zero doesn't happen.

    iScreenWidth = rcScreen.right - rcScreen.left;
    iScreenHeight = rcScreen.bottom - rcScreen.top;
    iBorderSize = GetSystemMetrics(SM_CYSMCAPTION);

    ASSERT(iScreenWidth > 0);       // get vtan
    ASSERT(iScreenHeight > 0);      // if any of
    ASSERT(iBorderSize > 0);        // these occur

    if ((iScreenWidth <= 0) || (iScreenHeight <= 0) || (iBorderSize <= 0))
    {
        pcp->iLeft = pcp->iTop = 0;
        pcp->dwWidth = MYCURHOME_WIDTH;
        pcp->dwHeight = MYCURHOME_HEIGHT;
    }
    else
    {

        // Get the number of components positioned. If no such registry key exists
        // or an error occurs then use 0.

        GetRegLocation(lpszDeskcomp, SIZECHARS(lpszDeskcomp), REG_DESKCOMP_GENERAL, NULL);
        dwComponentPosition = 0;
        if (RegCreateKeyEx(HKEY_CURRENT_USER, lpszDeskcomp, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &hKey, &dwRegDataScratch) == ERROR_SUCCESS)
        {
            DWORD   regDataSize;

            regDataSize = sizeof(dwComponentPosition);
            TW32(SHQueryValueEx(hKey, REG_VAL_GENERAL_CCOMPPOS, NULL, &dwRegDataScratch, &dwComponentPosition, &regDataSize));
            TW32(RegCloseKey(hKey));
        }

        // Compute the layer we live on (see below).

        dwComponentLayer = dwComponentPosition / (COMPONENT_PER_ROW * COMPONENT_PER_COL);
        if (((dwComponentLayer * iBorderSize) > (DWORD)(iScreenWidth / (COMPONENT_PER_ROW + 1))) ||
            ((dwComponentLayer * iBorderSize) > (DWORD)(iScreenHeight / (COMPONENT_PER_COL + 1))))
        {
            int     iLayerModulo;

            // 99/04/29 vtan: It's possible for SystemParametersInfo(SPI_GETWORKAREA) to
            // return a work area that's small horizontally. Here's a repro scenario for
            // that.

            // 1. Set screen resolution 1280 x 1024.
            // 2. Move the taskbar to the left of the screen.
            // 3. Grow the taskbar to the right until the center of the screen.
            // 4. Open display control panel.
            // 5. Go to "Settings" tab.
            // 6. Change monitor resolution to 640x480.
            // 7. Click either "OK" or "Apply".
            // 8. BOOM - divide zero.

            iLayerModulo = (iScreenWidth / (COMPONENT_PER_ROW + 1) / iBorderSize);
            if (iLayerModulo != 0)
                dwComponentLayer %= iLayerModulo;
        }

        // Compute the position.  Assuming 3 components per row,
        // and 2 per column, we position components thusly:
        //
        //       +-------+
        //       |x 4 2 0|
        //       |x 5 3 1| <-- screen, divided into 4x3 block coordinates
        //       |x x x x|
        //       +-------+
        //
        // The 6th component sits in a new layer, offset down
        // and to the left of component 0 by the amount iBorder.
        //
        // The first calculation for iLeft and iTop determines the
        // block coordinate value (for instance, component 0 would
        // be at block coordinate value [3,0] and component 1 at [3,1]).
        //
        // The second calculation turns the block coordinate into
        // a screen coordinate.
        //
        // The third calculation adjusts for the border (always down and
        // to the right) and the layers (always down and to the left).

        pcp->iLeft = COMPONENT_PER_ROW - ((dwComponentPosition / COMPONENT_PER_COL) % COMPONENT_PER_ROW); // 3 3 2 2 1 1 3 3 2 2 1 1 ...
        pcp->iLeft *= (iScreenWidth / (COMPONENT_PER_ROW + 1));
        pcp->iLeft += iBorderSize - (dwComponentLayer * iBorderSize);

        pcp->iTop = dwComponentPosition % COMPONENT_PER_COL;  // 0 1 0 1 0 1 ...
        pcp->iTop *= (iScreenHeight / (COMPONENT_PER_COL + 1));
        pcp->iTop += iBorderSize + (dwComponentLayer * iBorderSize);
        pcp->iTop += GET_CYCAPTION;          //vtan: Added this to allow for the title area of the component window

        pcp->dwWidth = (iScreenWidth / (COMPONENT_PER_ROW + 1)) - 2 * iBorderSize;
        pcp->dwHeight = (iScreenHeight / (COMPONENT_PER_COL + 1)) - 2 * iBorderSize;
    }

    if (IS_BIDI_LOCALIZED_SYSTEM())
    {
       pcp->iLeft = iScreenWidth - (pcp->iLeft + pcp->dwWidth);
    }
    
}

void    IncrementComponentsPositioned (void)

{
    DWORD   dwRegDataScratch;
    HKEY    hKey;
    TCHAR   lpszDeskcomp[MAX_PATH];

    // Increment the registry count. If no such count exists create it.

    GetRegLocation(lpszDeskcomp, SIZECHARS(lpszDeskcomp), REG_DESKCOMP_GENERAL, NULL);
    if (RegCreateKeyEx(HKEY_CURRENT_USER, lpszDeskcomp, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &hKey, &dwRegDataScratch) == ERROR_SUCCESS)
    {
        DWORD   dwComponentPosition, regDataSize;

        regDataSize = sizeof(dwComponentPosition);
        dwComponentPosition = 0;
        TW32(SHQueryValueEx(hKey, REG_VAL_GENERAL_CCOMPPOS, NULL, &dwRegDataScratch, &dwComponentPosition, &regDataSize));
        ++dwComponentPosition;
        TW32(RegSetValueEx(hKey, REG_VAL_GENERAL_CCOMPPOS, 0, REG_DWORD, reinterpret_cast<unsigned char*>(&dwComponentPosition), sizeof(dwComponentPosition)));
        TW32(RegCloseKey(hKey));
    }
}

//  vtan: Point arithmetic functions. Simple. It may be worth
//  converting these to inline C++ functions or macros if they
//  get used a lot.

void    SetPt (POINT& pt, LONG x, LONG y)

{
    pt.x = x;
    pt.y = y;
}

void    OffsetPt (POINT& pt, LONG dh, LONG dv)

{
    pt.x += dh;
    pt.y += dv;
}

BOOL    CALLBACK    GDIToTridentEnumProc (HMONITOR hMonitor, HDC hDC, RECT* rcMonitor, LPARAM lpUserData)

{
    RECT*   prcNew, rcOld;

    prcNew = reinterpret_cast<RECT*>(lpUserData);

    // Documentation for UnionRect does not specify whether the
    // RECT structures passed must be distinct. To be safe they
    // are passed as distinct structures.

    TBOOL(CopyRect(&rcOld, prcNew));
    TBOOL(UnionRect(prcNew, &rcOld, rcMonitor));
    return(TRUE);
}

void    CalculateVirtualScreen (RECT& rcVirtualScreen)

//  vtan: Calculates the virtual screen in GDI co-ordinates for
//  use in converting co-ordinates from trident scheme to GDI
//  scheme.

{
    TBOOL(SetRectEmpty(&rcVirtualScreen));
    TBOOL(EnumDisplayMonitors(NULL, NULL, GDIToTridentEnumProc, reinterpret_cast<LPARAM>(&rcVirtualScreen)));
}

void    GDIToTrident (int& leftCoordinate, int& topCoordinate)

{
    RECT    rcVirtualScreen;

    CalculateVirtualScreen(rcVirtualScreen);
    leftCoordinate -= rcVirtualScreen.left;
    topCoordinate -= rcVirtualScreen.top;
}

/*
void    GDIToTrident (POINT& pt)

{
    RECT    rcVirtualScreen;

    CalculateVirtualScreen(rcVirtualScreen);
    OffsetPt(pt, -rcVirtualScreen.left, -rcVirtualScreen.top);
}
*/

void    GDIToTrident (RECT& rc)

{
    RECT    rcVirtualScreen;

    CalculateVirtualScreen(rcVirtualScreen);
    TBOOL(OffsetRect(&rc, -rcVirtualScreen.left, -rcVirtualScreen.top));
}

void    GDIToTrident (HRGN hRgn)

{
    RECT    rcVirtualScreen;

    CalculateVirtualScreen(rcVirtualScreen);
    TBOOL(OffsetRgn(hRgn, -rcVirtualScreen.left, -rcVirtualScreen.top));
}

/*
void    TridentToGDI (int& leftCoordinate, int& topCoordinate)

{
    RECT    rcVirtualScreen;

    CalculateVirtualScreen(rcVirtualScreen);
    leftCoordinate += rcVirtualScreen.left;
    topCoordinate += rcVirtualScreen.top;
}
*/

/*
void    TridentToGDI (POINT& pt)

{
    RECT    rcVirtualScreen;

    CalculateVirtualScreen(rcVirtualScreen);
    OffsetPt(pt, +rcVirtualScreen.left, +rcVirtualScreen.top);
}
*/

void    TridentToGDI (RECT& rc)

{
    RECT    rcVirtualScreen;

    CalculateVirtualScreen(rcVirtualScreen);
    TBOOL(OffsetRect(&rc, +rcVirtualScreen.left, +rcVirtualScreen.top));
}

/*
void    TridentToGDI (HRGN hRgn)

{
    RECT    rcVirtualScreen;

    CalculateVirtualScreen(rcVirtualScreen);
    (BOOL)OffsetRgn(hRgn, +rcVirtualScreen.left, +rcVirtualScreen.top);
}
*/

//  98/08/14 vtan #196180, #196185: The following code validates
//  a new component's position within the current desktop area. This
//  allows a component to have co-ordinates that seem to be unusual
//  on a single monitor system (such as negative co-ordinates).

class   CRGN
{
    public:
                CRGN (void)                     {   mRgn = CreateRectRgn(0, 0, 0, 0);                               }
                CRGN (const RECT& rc)           {   mRgn = CreateRectRgnIndirect(&rc);                              }
                ~CRGN (void)                    {   TBOOL(DeleteObject(mRgn));                                      }

                operator HRGN (void)    const   {   return(mRgn);                                                   }
        void    SetRegion (const RECT& rc)      {   TBOOL(SetRectRgn(mRgn, rc.left, rc.top, rc.right, rc.bottom));  }
    private:
        HRGN    mRgn;
};

typedef struct
{
    bool    bAllowEntireDesktopRegion;
    int     iMonitorCount;
    CRGN    hRgn;
    int     iWorkAreaCount;
    RECT    *prcWorkAreaRects;
} tDesktopRegion;

void    ListView_GetWorkAreasAsGDI (HWND hWndListView, int iWorkAreaCount, RECT *prcWorkAreas)

{
    int     i;

    ListView_GetWorkAreas(hWndListView, iWorkAreaCount, prcWorkAreas);
    for (i = 0; i < iWorkAreaCount; ++i)
    {
        TridentToGDI(prcWorkAreas[i]);
    }
}

int     CopyMostSuitableListViewWorkAreaRect (const RECT *pcrcMonitor, int iListViewWorkAreaCount, const RECT *pcrcListViewWorkAreaRects, RECT *prcWorkArea)

{
    int         i, iResult;
    const RECT  *pcrcRects;

    // This function given a rectangle for a GDI monitor (typically the monitor's
    // work area) as well as given the desktop's list view work area rectangle
    // array (obtained by ListView_GetWorkArea()) will search the list view
    // work area array to find a match for the GDI monitor and use the list view
    // work area rectangle instead as this has docked toolbar information which
    // GDI does not have access to.

    // This function works on the principle that the list view rectangle is
    // always a complete subset of the GDI monitor rectangle which is true.
    // The list view rectangle may be smaller but it should never be bigger.

    // It's ok to pass a NULL pcrcListViewWorkAreaRects as long as
    // iListViewWorkAreaCount is 0.

    pcrcRects = pcrcListViewWorkAreaRects;
    iResult = -1;
    i = 0;
    while ((iResult == -1) && (i < iListViewWorkAreaCount))
    {
        RECT    rcIntersection;

        (BOOL)IntersectRect(&rcIntersection, pcrcMonitor, pcrcRects);
        if (EqualRect(&rcIntersection, pcrcRects) != 0)
        {
            iResult = i;
        }
        else
        {
            ++pcrcRects;
            ++i;
        }
    }
    if (iResult < 0)
    {
        TraceMsg(TF_WARNING, "CopyMostSuitableListViewWorkAreaRect() unable to find matching list view rectangle for GDI monitor rectangle");
        TBOOL(CopyRect(prcWorkArea, pcrcMonitor));
    }
    else
    {
        TBOOL(CopyRect(prcWorkArea, &pcrcListViewWorkAreaRects[iResult]));
    }
    return(iResult);
}

BOOL    GetMonitorInfoWithCompensation (int iMonitorCount, HMONITOR hMonitor, MONITORINFO *pMonitorInfo)

{
    BOOL    fResult;

    // 99/05/20 #338585 vtan: Transplanted the logic explained in the
    // comment below for #211510 from GetZoomRect to here so that other
    // functions can share the behavior. Remember that this ONLY applies
    // a single monitor system where there is part of the monitor's
    // rectangle excluded by a docked toolbar on the left or top of the
    // monitor. A very specific case.

    // 98/10/30 #211510 vtan: Oops. If the task bar is at the top of the
    // screen and there is only one monitor then the shell returns a work
    // area starting at (0, 0) instead of (0, 28); the same applies when
    // the task bar is at the left of the screen; this does NOT occur in
    // a multiple monitor setting. In the single monitor case GDI returns
    // a work area starting at (0, 28) so this code checks for the case
    // where there is a single monitor and offsets the GDI information to
    // (0, 0) so that it matches the shell work area which is compared
    // against in the while loop.

    fResult = GetMonitorInfo(hMonitor, pMonitorInfo);
    if ((fResult != 0) && (iMonitorCount == 1))
    {
        TBOOL(OffsetRect(&pMonitorInfo->rcWork, -pMonitorInfo->rcWork.left, -pMonitorInfo->rcWork.top));
    }
    return(fResult);
}

//  MonitorCountEnumProc()'s body is located in adjust.cpp

BOOL    CALLBACK    MonitorCountEnumProc (HMONITOR hMonitor, HDC dc, RECT *rc, LPARAM data);

BOOL    CALLBACK    ValidateComponentPositionEnumProc (HMONITOR hMonitor, HDC hdcMonitor, RECT* prc, LPARAM lpUserData)

{
    HRGN            hRgnDesktop;
    HMONITOR        hMonitorTopLeft, hMonitorTopRight;
    POINT           ptAbove;
    RECT            rcMonitor;
    MONITORINFO     monitorInfo;
    tDesktopRegion  *pDesktopRegion;

    pDesktopRegion = reinterpret_cast<tDesktopRegion*>(lpUserData);
    monitorInfo.cbSize = sizeof(monitorInfo);
    if (GetMonitorInfoWithCompensation(pDesktopRegion->iMonitorCount, hMonitor, &monitorInfo) != 0)
    {
        TINT(CopyMostSuitableListViewWorkAreaRect(&monitorInfo.rcWork, pDesktopRegion->iWorkAreaCount, pDesktopRegion->prcWorkAreaRects, &rcMonitor));
    }
    else
    {
        TBOOL(CopyRect(&rcMonitor, prc));
    }

    // If this monitor does not have a monitor above it then
    // make the monitor rectangle one pixel lower from the
    // top.

    CRGN    hRgnMonitor(rcMonitor);

    if (!pDesktopRegion->bAllowEntireDesktopRegion)
    {

        // This bizarre little algorithm calculates the margins of the current
        // monitor that do not have a monitor above them. The rcExclude is the
        // the final rectangle that contains this information and is one pixel
        // high. This calculation is only valid if the entire desktop region
        // has been DISALLOWED (not zooming a component).

        // Note that the algorithm fails if there is a monitor that is above
        // this one but is contained within the confines of it. For example,
        // this monitor is at 1024x768 and the one above is at 640x480 and
        // centered. In this case it should be possible to drop the component
        // on the exact zero pixel point but this case is disallowed due to
        // this fault. No big deal.

        SetPt(ptAbove, rcMonitor.left, rcMonitor.top - 1);
        hMonitorTopLeft = MonitorFromPoint(ptAbove, MONITOR_DEFAULTTONULL);
        SetPt(ptAbove, rcMonitor.right, rcMonitor.top - 1);
        hMonitorTopRight = MonitorFromPoint(ptAbove, MONITOR_DEFAULTTONULL);
        if ((hMonitorTopLeft == NULL) && (hMonitorTopRight == NULL))
        {

            // No monitor above this one

            ++rcMonitor.top;
            hRgnMonitor.SetRegion(rcMonitor);
        }
        else if (hMonitorTopLeft != hMonitorTopRight)
        {
            RECT    rcExclude;

            // Either one or two different monitors above this one
            // == case is the same monitor completely covers this
            // monitor.

            TBOOL(SetRect(&rcExclude, rcMonitor.left, rcMonitor.top, rcMonitor.right, rcMonitor.top + 1));
            if (hMonitorTopLeft != NULL)
            {
                TBOOL(GetMonitorInfoWithCompensation(pDesktopRegion->iMonitorCount, hMonitorTopLeft, &monitorInfo));
                rcExclude.left = monitorInfo.rcWork.right + 1;
            }
            if (hMonitorTopRight != NULL)
            {
                TBOOL(GetMonitorInfoWithCompensation(pDesktopRegion->iMonitorCount, hMonitorTopRight, &monitorInfo));
                rcExclude.right = monitorInfo.rcWork.left;
            }

            CRGN    hRgnExclude(rcExclude);

            TINT(CombineRgn(hRgnMonitor, hRgnMonitor, hRgnExclude, RGN_DIFF));
        }
    }

    hRgnDesktop = pDesktopRegion->hRgn;
    TINT(CombineRgn(hRgnDesktop, hRgnDesktop, hRgnMonitor, RGN_OR));

    return(true);
}

void    ValidateComponentPosition (COMPPOS *pcp, DWORD dwComponentState, int iComponentType, bool *pbChangedPosition, bool *pbChangedSize)

{
    bool            bChangedPosition, bChangedSize;
    HRGN            hRgnDesktop;
    HWND            hWndDesktopListView, hWndShell, hWndShellChild;
    RECT            rcComponent, rcComponentTop;
    tDesktopRegion  desktopRegion;
    COMPPOS         defaultComponentPosition;

    bChangedPosition = bChangedSize = false;
    GetNextComponentPosition(&defaultComponentPosition);
    GDIToTrident(defaultComponentPosition.iLeft, defaultComponentPosition.iTop);

    // If the component has default left or top then give it the next
    // default component position.

    if ((pcp->iLeft == COMPONENT_DEFAULT_LEFT) && (pcp->iTop == COMPONENT_DEFAULT_TOP))
    {
        pcp->iLeft = defaultComponentPosition.iLeft;
        pcp->iTop = defaultComponentPosition.iTop;
        IncrementComponentsPositioned();
        bChangedPosition = true;
    }

    // If the component has default width or height then give it the
    // next default component size unless it is type COMP_TYPE_PICTURE

    // 98/10/02 #222449 vtan: Only change the size of an unpositioned
    // component if it's not a picture.

    if ((pcp->dwWidth == COMPONENT_DEFAULT_WIDTH) && (pcp->dwHeight == COMPONENT_DEFAULT_HEIGHT) && (iComponentType != COMP_TYPE_PICTURE))
    {
        pcp->dwWidth = defaultComponentPosition.dwWidth;
        pcp->dwHeight = defaultComponentPosition.dwHeight;
        bChangedSize = false;
    }

    // Make sure that the top line of the component is visible or at
    // least one pixel below the top most part of a virtual screen.

    // Check to see if the component has a negative width and height or
    // a width and height that is too small. The only exception to this
    // is if the component is a picture.

    desktopRegion.bAllowEntireDesktopRegion = IsZoomedState(dwComponentState);
    if (iComponentType != COMP_TYPE_PICTURE)
    {
        if (static_cast<int>(pcp->dwWidth) < 10)
        {
            pcp->dwWidth = defaultComponentPosition.dwWidth;
            bChangedSize = false;
        }
        if (static_cast<int>(pcp->dwHeight) < 10)
        {
            pcp->dwHeight= defaultComponentPosition.dwHeight;
            bChangedSize = false;
        }
    }
    TBOOL(SetRect(&rcComponent, pcp->iLeft, pcp->iTop, pcp->iLeft + pcp->dwWidth, pcp->iTop + pcp->dwHeight));
    TBOOL(CopyRect(&rcComponentTop, &rcComponent));
    rcComponentTop.bottom = rcComponentTop.top + 1;

    // Before calculating the desktopRegion as a region by using GDI calls
    // get the List View work area which will have information about docked
    // toolbars in addition to the taskbar which is the only thing that GDI
    // has. This will allow this function to invalidate regions occupied by
    // toolbars also.

    desktopRegion.iWorkAreaCount = 0;
    desktopRegion.prcWorkAreaRects = NULL;

    hWndDesktopListView = NULL;
    hWndShell = GetShellWindow();
    if (hWndShell != NULL)
    {
        hWndShellChild = GetWindow(hWndShell, GW_CHILD);
        if (hWndShellChild != NULL)
        {
            hWndDesktopListView = FindWindowEx(hWndShellChild, NULL, WC_LISTVIEW, NULL);
        }
    }
    if (hWndDesktopListView != NULL)
    {
        DWORD   dwProcessID;

        GetWindowThreadProcessId(hWndDesktopListView, &dwProcessID);
        if (GetCurrentProcessId() == dwProcessID)
        {
            ListView_GetNumberOfWorkAreas(hWndDesktopListView, &desktopRegion.iWorkAreaCount);
            desktopRegion.prcWorkAreaRects = reinterpret_cast<RECT*>(LocalAlloc(GPTR, desktopRegion.iWorkAreaCount * sizeof(desktopRegion.prcWorkAreaRects[0])));
            ListView_GetWorkAreasAsGDI(hWndDesktopListView, desktopRegion.iWorkAreaCount, desktopRegion.prcWorkAreaRects);
        }
    }

    CRGN    hRgnComponentTop(rcComponentTop), hRgnResult;

    desktopRegion.iMonitorCount = 0;
    TBOOL(EnumDisplayMonitors(NULL, NULL, MonitorCountEnumProc, reinterpret_cast<LPARAM>(&desktopRegion.iMonitorCount)));
    TBOOL(EnumDisplayMonitors(NULL, NULL, ValidateComponentPositionEnumProc, reinterpret_cast<LPARAM>(&desktopRegion)));
    hRgnDesktop = desktopRegion.hRgn;
    GDIToTrident(hRgnDesktop);

    // 99/03/23 #266412 vtan: Make sure that the top pixel of the component is within
    // the visible desktop. This allows the deskmovr to be positioned over the
    // component and therefore allows it to be moved. If the deskmovr cannot be
    // positioned over it then "snap" the component back into the visible region
    // to a maximum best fit algorithm.

    if (CombineRgn(hRgnResult, hRgnDesktop, hRgnComponentTop, RGN_AND) == NULLREGION)
    {
        LONG        lDeltaX, lDeltaY;
        HMONITOR    hMonitorNearest;
        RECT        rcComponentGDI, rcMonitorWork, rcIntersection;
        MONITORINFO monitorInfo;

        TBOOL(CopyRect(&rcComponentGDI, &rcComponent));
        TridentToGDI(rcComponentGDI);
        hMonitorNearest = MonitorFromRect(&rcComponentGDI, MONITOR_DEFAULTTONEAREST);
        ASSERT(hMonitorNearest != NULL);
        monitorInfo.cbSize = sizeof(monitorInfo);
        TBOOL(GetMonitorInfoWithCompensation(desktopRegion.iMonitorCount, hMonitorNearest, &monitorInfo));
        TINT(CopyMostSuitableListViewWorkAreaRect(&monitorInfo.rcWork, desktopRegion.iWorkAreaCount, desktopRegion.prcWorkAreaRects, &rcMonitorWork));
        ++rcMonitorWork.top;
        lDeltaX = lDeltaY = 0;
        if (rcComponentGDI.left < rcMonitorWork.left)
            lDeltaX = rcMonitorWork.left - rcComponentGDI.left;
        if (rcComponentGDI.top < rcMonitorWork.top)
            lDeltaY = rcMonitorWork.top - rcComponentGDI.top;
        if (rcComponentGDI.right > rcMonitorWork.right)
            lDeltaX = rcMonitorWork.right - rcComponentGDI.right;
        if (rcComponentGDI.bottom > rcMonitorWork.bottom)
            lDeltaY = rcMonitorWork.bottom - rcComponentGDI.bottom;
        TBOOL(OffsetRect(&rcComponentGDI, lDeltaX, lDeltaY));
        TBOOL(IntersectRect(&rcIntersection, &rcComponentGDI, &rcMonitorWork));
        GDIToTrident(rcIntersection);
        pcp->iLeft = rcIntersection.left;
        pcp->iTop = rcIntersection.top;
        pcp->dwWidth = rcIntersection.right - rcIntersection.left;
        pcp->dwHeight = rcIntersection.bottom - rcIntersection.top;
        bChangedPosition = bChangedSize = true;
    }

    if (desktopRegion.prcWorkAreaRects != NULL)
        LocalFree(desktopRegion.prcWorkAreaRects);

    if (pbChangedPosition != NULL)
        *pbChangedPosition = bChangedPosition;
    if (pbChangedSize != NULL)
        *pbChangedSize = bChangedSize;
}

//  98/12/11 #250938 vtan: these two functions are lifted from
//  SHBrows2.cpp which is part of browseui.dll.

EXTERN_C    DWORD   WINAPI  IsSmartStart (void);


#ifdef NEVER
// For WinMillennium, we do not want to launch the ICW when active desktop is turned on because
// we do not have a "My Current Homepage" desktop component. So, I am disabling the following code
// This is the temporary fix for Mill bug # 98107 also.
BOOL    IsICWCompleted (void)
{
    DWORD   dwICWCompleted, dwICWSize;

    dwICWCompleted = 0;
    dwICWSize = sizeof(dwICWCompleted);
    TW32(SHGetValue(HKEY_CURRENT_USER, TEXT(ICW_REGPATHSETTINGS), TEXT(ICW_REGKEYCOMPLETED), NULL, &dwICWCompleted, &dwICWSize));

    // 99/01/15 #272829 vtan: This is a horrible hack!!! If ICW has
    // not been run but settings have been made manually then values
    // in HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections
    // exists with the values given. Look for the presence of a key
    // to resolve that settings are present but that ICW hasn't been
    // launched.

    // The ideal solution is to get ICW to make this determination
    // for us BUT TO NOT LAUNCH ICWCONN1.EXE IN THE PROCESS.
    // Currently it will only launch. There is no way to get the
    // desired result without a launch.

    // 99/02/01 #280138 vtan: Well the solution put in for #272829
    // doesn't work. So peeking at the CheckConnectionWizard()
    // source in inetcfg\export.cpp shows that it uses a
    // wininet.dll function to determine whether manually configured
    // internet settings exist. It also exports this function so
    // look for it and bind to it dynamically. This uses the
    // DELAY_LOAD macros in dllload.c

    if (dwICWCompleted == 0)
    {
        #define SMART_RUNICW    TRUE
        #define SMART_QUITICW   FALSE

        dwICWCompleted = BOOLIFY(IsSmartStart() == SMART_QUITICW);
    }
    return(dwICWCompleted != 0);
}
#else  //NEVER
BOOL    IsICWCompleted (void)
{
    return TRUE; //For Millennium we want to always return TRUE for this function.
}
#endif //NEVER

void    LaunchICW (void)

{
    static  bool    sbCheckedICW = false;

    if (!sbCheckedICW && !IsICWCompleted())
    {
        HINSTANCE   hICWInst;

        // Prevent an error in finding the ICW from causing this to
        // execute again and again and again.

        sbCheckedICW = true;
        hICWInst = LoadLibrary(TEXT("inetcfg.dll"));
        if (hICWInst != NULL)
        {
            PFNCHECKCONNECTIONWIZARD    pfnCheckConnectionWizard;

            pfnCheckConnectionWizard = reinterpret_cast<PFNCHECKCONNECTIONWIZARD>(GetProcAddress(hICWInst, "CheckConnectionWizard"));
            if (pfnCheckConnectionWizard != NULL)
            {
                DWORD   dwICWResult;

                // If the user cancels ICW then it needs to be launched
                // again. Allow this case.

                sbCheckedICW = false;

                pfnCheckConnectionWizard(ICW_LAUNCHFULL | ICW_LAUNCHMANUAL, &dwICWResult);
            }
            TBOOL(FreeLibrary(hICWInst));
        }
    }
}

BOOL    IsLocalPicture (LPCTSTR pszURL)

{
    return(!PathIsURL(pszURL) && IsUrlPicture(pszURL));
}

BOOL    DisableUndisplayableComponents (IActiveDesktop *pIAD)

{
    bool    bHasVisibleNonLocalPicture;
    int     iItemCount;

    // 98/12/16 vtan #250938: If ICW has not been run to completion then only
    // allow the user to show components that are local pictures of some sort.
    // If any components are not local pictures then hide these components,
    // tell the user why it happened and launch ICW.

    bHasVisibleNonLocalPicture = false;
    if (SUCCEEDED(pIAD->GetDesktopItemCount(&iItemCount, 0)))
    {
        int     i;

        for (i = 0; i < iItemCount; ++i)
        {
            COMPONENT   component;

            component.dwSize = sizeof(component);
            if (SUCCEEDED(pIAD->GetDesktopItem(i, &component, 0)) && (component.fChecked != 0))
            {
               bool    bIsVisibleNonLocalPicture;
               TCHAR   szComponentSource[INTERNET_MAX_URL_LENGTH];

               SHUnicodeToTChar(component.wszSource, szComponentSource, ARRAYSIZE(szComponentSource));
               bIsVisibleNonLocalPicture = !IsLocalPicture(szComponentSource);
               bHasVisibleNonLocalPicture = bHasVisibleNonLocalPicture || bIsVisibleNonLocalPicture;
               if (bIsVisibleNonLocalPicture)
               {
                   component.fChecked = FALSE;
                   THR(pIAD->ModifyDesktopItem(&component, COMP_ELEM_CHECKED));
               }
            }
         }
    }
    if (bHasVisibleNonLocalPicture)
    {

        // Apply the changes. This should recurse to CActiveDesktop::_SaveSettings()
        // but this code path is NOT taken because AD_APPLY_REFRESH is not passed in.
        // CActiveDesktop::_SaveSettings() calls this function!

        bHasVisibleNonLocalPicture = FAILED(pIAD->ApplyChanges(AD_APPLY_SAVE | AD_APPLY_HTMLGEN));

        // Notify the user what happened and launch ICW.

        ShellMessageBox(HINST_THISDLL, NULL, MAKEINTRESOURCE(IDS_COMP_ICW_DISABLE), MAKEINTRESOURCE(IDS_COMP_ICW_TITLE), MB_OK);
        LaunchICW();
    }
    return(bHasVisibleNonLocalPicture);
}

int GetIconCountForWorkArea(HWND hwndLV, LPCRECT prect, int crect, int iWorkAreaIndex)
{
    int iCount;

    iCount = ListView_GetItemCount(hwndLV);

    if (crect > 1) 
    {
        int i, iCountWorkArea = 0;

        for (i = 0; i < iCount; i++)
        {
            POINT pt;
            ListView_GetItemPosition(hwndLV, i, &pt);
            if (iWorkAreaIndex == GetWorkAreaIndexFromPoint(pt, prect, crect))
                iCountWorkArea++;
        }

        iCount = iCountWorkArea;
    }

    return iCount;
}

BOOL GetZoomRect(BOOL fFullScreen, BOOL fAdjustListview, int iTridentLeft, int iTridentTop, DWORD dwComponentWidth, DWORD dwComponentHeight, LPRECT prcZoom, LPRECT prcWork)
{
    HWND hwndShell, hwndLV;
    int icWorkAreas = 0, iWAC;
    RECT rcWork[LV_MAX_WORKAREAS];

    hwndLV = NULL;
    hwndShell = GetShellWindow();
    if (hwndShell != NULL)
    {
        HWND    hwndShellChild;

        hwndShellChild= GetWindow(hwndShell, GW_CHILD);
        if (hwndShellChild != NULL)
        {
            hwndLV = FindWindowEx(hwndShellChild, NULL, WC_LISTVIEW, NULL);
        }
    }

    //
    // First calculate the Work Areas and Work Area index for the component, then perform the
    // particular operation based on lCommand.
    //
    if (hwndLV) {
        DWORD dwpid;
        GetWindowThreadProcessId(hwndLV, &dwpid);
        // The listview doesn't thunk these messages so we can't do
        // this inter-process!
        if (dwpid == GetCurrentProcessId())
        {
            ListView_GetNumberOfWorkAreas(hwndLV, &icWorkAreas);
            if (icWorkAreas <= LV_MAX_WORKAREAS)
                ListView_GetWorkAreas(hwndLV, icWorkAreas, &rcWork);
            else
                hwndLV = NULL;
        } else {
            return FALSE;
        }
    }

    // 98/10/07 vtan: This used to use a variable icWorkAreasAdd.
    // Removed this variable and directly increment icWorkAreas.
    // This doesn't affect the call to ListView_SetWorkAreas()
    // below because in this case hwndLV is NULL.

    if (icWorkAreas == 0)
    {
        RECT rc;

        ++icWorkAreas;
        SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&rc, 0);
        rcWork[0] = rc;
        hwndLV = NULL;
    }

    // 98/10/02 #212654 vtan: Changed the calculation code to find a
    // rectangle to zoom the component to based on GDI co-ordinates.
    // The component is passed in trident co-ordinates which are
    // stored in a RECT and converted to GDI co-ordinates. The system
    // then locates the monitor which the component is on and if it
    // cannot find the monitor then defaults to the primary. The
    // dimensions of the monitor are used before converting back to
    // trident co-ordinates.

    int             i, iMonitorCount;
    HMONITOR        hMonitor;
    RECT            rcComponentRect;
    MONITORINFO     monitorInfo;

    iMonitorCount = 0;
    TBOOL(EnumDisplayMonitors(NULL, NULL, MonitorCountEnumProc, reinterpret_cast<LPARAM>(&iMonitorCount)));
    TBOOL(SetRect(&rcComponentRect, iTridentLeft, iTridentTop, iTridentLeft + dwComponentWidth, iTridentTop + dwComponentHeight));
    TridentToGDI(rcComponentRect);
    hMonitor = MonitorFromRect(&rcComponentRect, MONITOR_DEFAULTTOPRIMARY);
    ASSERT(hMonitor != NULL);
    monitorInfo.cbSize = sizeof(monitorInfo);
    TBOOL(GetMonitorInfoWithCompensation(iMonitorCount, hMonitor, &monitorInfo));
    GDIToTrident(monitorInfo.rcWork);

    // 99/05/19 #340772 vtan: Always try to key off work areas returned
    // by ListView_GetWorkAreas because these take into account docked
    // toolbars which GDI does not. In this case the listview work areas
    // will always be the same rectangle when intersected with the GDI
    // work area. Use this rule to determine which listview work area
    // to use as the basis for the zoom rectangle.

    i = CopyMostSuitableListViewWorkAreaRect(&monitorInfo.rcWork, icWorkAreas, rcWork, prcZoom);
    if (i < 0)
    {
        i = 0;
    }
    if (prcWork != NULL)
    {
        TBOOL(CopyRect(prcWork, prcZoom));
    }
    iWAC = i;

    if (!fFullScreen)
    {
        // For the split case we shrink the work area down temporarily to the smallest rectangle
        // that can bound the current number of icons.  This will force the icons into that rectangle,
        // then restore it back to the way it was before.  Finally, we set the size of the split
        // component to fill the rest of the space.
        if (hwndLV) {
            int iCount, iItemsPerColumn, icxWidth, iRightOld;
            DWORD dwSpacing;

            iCount = GetIconCountForWorkArea(hwndLV, rcWork, icWorkAreas, iWAC);
            // Decrement the count so that rounding works right
            if (iCount)     
                iCount--;

            // Calculate the new width for the view rectangle
            dwSpacing = ListView_GetItemSpacing(hwndLV, FALSE);
            iItemsPerColumn = (rcWork[iWAC].bottom - rcWork[iWAC].top) / (HIWORD(dwSpacing));
            if (iItemsPerColumn)
                icxWidth = ((iCount / iItemsPerColumn) + 1) * (LOWORD(dwSpacing));
            else
                icxWidth = LOWORD(dwSpacing);

            // Don't let it get smaller than half the screen
            if (icxWidth > ((rcWork[iWAC].right - rcWork[iWAC].left) / 2))
                icxWidth = (rcWork[iWAC].right - rcWork[iWAC].left) / 2;

            if (fAdjustListview)
            {
                // Now take the old work area rectangle and shrink it to our new width
                iRightOld = rcWork[iWAC].right;
                rcWork[iWAC].right = rcWork[iWAC].left + icxWidth;
                ListView_SetWorkAreas(hwndLV, icWorkAreas, &rcWork);

                // Finally restore the old work area
                rcWork[iWAC].right = iRightOld;
                ListView_SetWorkAreas(hwndLV, icWorkAreas, &rcWork);
            }

            // Adjust the left coordinate of the zoom rect to reflect our calculated split amount
            // the rest of the screen.
            if (IS_BIDI_LOCALIZED_SYSTEM())
            {
                prcZoom->right -= icxWidth;
            }
            else
            {
                prcZoom->left += icxWidth;
            }
        } else {
            // Fallback case, if there is no listview use 20% of the screen for the icons.
            if (IS_BIDI_LOCALIZED_SYSTEM())
            {
                prcZoom->right -= ((prcZoom->right - prcZoom->left) * 2 / 10);            
            }
            else
            {
                prcZoom->left += ((prcZoom->right - prcZoom->left) * 2 / 10);
            }    
        }
    }

    return TRUE;
}

void ZoomComponent(COMPPOS * pcp, DWORD dwCurItemState, BOOL fAdjustListview)
{
    RECT rcZoom;

    if (GetZoomRect((dwCurItemState & IS_FULLSCREEN), fAdjustListview, pcp->iLeft, pcp->iTop, pcp->dwWidth, pcp->dwHeight, &rcZoom, NULL))
    {
        // Copy the new Zoom rectangle over and put it on the bottom
        pcp->iLeft = rcZoom.left;
        pcp->iTop = rcZoom.top;
        pcp->dwWidth = rcZoom.right - rcZoom.left;
        pcp->dwHeight = rcZoom.bottom - rcZoom.top;
        pcp->izIndex = 0;
    }
    else
    {
        // Failure implies we couldn't get the zoom rectangle through inter-process calls.  Set the
        // COMPONENTS_ZOOMDIRTY bit here so that when the desktop is refreshed we will recalculate
        // the zoom rectangles in-process inside of EnsureUpdateHTML.
        SetDesktopFlags(COMPONENTS_ZOOMDIRTY, COMPONENTS_ZOOMDIRTY);
    }
}

//
// PositionComponent will assign a screen position and
// make sure it fits on the screen.
//

void PositionComponent(COMPONENTA *pcomp, COMPPOS *pcp, int iCompType, BOOL fCheckItemState)

{

//  vtan: Vastly simplified routine. The work is now done in
//  ValidateComponentPosition.

    if (ISZOOMED(pcomp))
    {
        if (fCheckItemState)
        {
            SetStateInfo(&pcomp->csiRestored, pcp, IS_NORMAL);
            SetStateInfo(&pcomp->csiOriginal, pcp, pcomp->dwCurItemState);
        }
        ZoomComponent(pcp, pcomp->dwCurItemState, FALSE);
    }
    else
    {
        ValidateComponentPosition(pcp, pcomp->dwCurItemState, iCompType, NULL, NULL);
        if (fCheckItemState)
            SetStateInfo(&pcomp->csiOriginal, pcp, pcomp->dwCurItemState);
    }
}

typedef struct _tagFILETYPEENTRY {
    DWORD dwFlag;
    int iFilterId;
} FILETYPEENTRY;

FILETYPEENTRY afte[] = {
    { GFN_URL, IDS_URL_FILTER, },
    { GFN_CDF, IDS_CDF_FILTER, },
    { GFN_LOCALHTM, IDS_HTMLDOC_FILTER, },
    { GFN_PICTURE,  IDS_IMAGES_FILTER, },
    { GFN_LOCALMHTML, IDS_MHTML_FILTER, },
};

//
// Opens either an HTML page or a picture.
//
BOOL GetFileName(HWND hdlg, LPTSTR pszFileName, int iSize, int iTypeId[], DWORD dwFlags[])
{
    BOOL fRet = FALSE;

    if (dwFlags)
    {
        int i, iIndex, cchRead;
        TCHAR szFilter[MAX_PATH*4];

        //
        // Set the friendly name.
        //
        LPTSTR pchFilter = szFilter;
        int cchFilter = ARRAYSIZE(szFilter) - 2;    // room for term chars

        for(iIndex = 0; dwFlags[iIndex]; iIndex++)
        {
            cchRead = LoadString(HINST_THISDLL, iTypeId[iIndex], pchFilter, cchFilter);
            pchFilter += cchRead + 1;
            cchFilter -= cchRead + 1;

            //
            // Append the file filters.
            //
            BOOL fAddedToString = FALSE;
            for (i=0; (cchFilter>0) && (i<ARRAYSIZE(afte)); i++)
            {
                if (dwFlags[iIndex] & afte[i].dwFlag)
                {
                    if (fAddedToString)
                    {
                        *pchFilter++ = TEXT(';');
                        cchFilter--;
                    }
                    cchRead = LoadString(HINST_THISDLL, afte[i].iFilterId,
                                     pchFilter, cchFilter);
                    pchFilter += cchRead;
                    cchFilter -= cchRead;
                    fAddedToString = TRUE;
                }
            }
            *pchFilter++ = TEXT('\0');
        }

        //
        // Double-NULL terminate the string.
        //
        *pchFilter = TEXT('\0');

        TCHAR szBrowserDir[MAX_PATH];
        lstrcpy(szBrowserDir, pszFileName);
        PathRemoveFileSpec(szBrowserDir);

        TCHAR szBuf[MAX_PATH];
        LoadString(HINST_THISDLL, IDS_BROWSE, szBuf, ARRAYSIZE(szBuf));

        *pszFileName = TEXT('\0');

        OPENFILENAME ofn = {0};
        ofn.lStructSize       = SIZEOF(ofn);
        ofn.hwndOwner         = hdlg;
        ofn.hInstance         = NULL;
        ofn.lpstrFilter       = szFilter;
        ofn.lpstrCustomFilter = NULL;
        ofn.nFilterIndex      = 1;
        ofn.nMaxCustFilter    = 0;
        ofn.lpstrFile         = pszFileName;
        ofn.nMaxFile          = iSize;
        ofn.lpstrInitialDir   = szBrowserDir;
        ofn.lpstrTitle        = szBuf;
        ofn.Flags             = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST;
        ofn.lpfnHook          = NULL;
        ofn.lpstrDefExt       = NULL;
        ofn.lpstrFileTitle    = NULL;

        fRet = GetOpenFileName(&ofn);
    }

    return fRet;
}

//
// Convert a pattern string to a bottom-up array of DWORDs,
// useful for BMP format files.
//
void PatternToDwords(LPTSTR psz, DWORD *pdwBits)
{
    DWORD i, dwVal;

    //
    // Get eight groups of numbers separated by non-numeric characters.
    //
    for (i=0; i<8; i++)
    {
        dwVal = 0;

        if (*psz != TEXT('\0'))
        {
            //
            // Skip over any non-numeric characters.
            //
            while (*psz && (!(*psz >= TEXT('0') && *psz <= TEXT('9'))))
            {
                psz++;
            }

            //
            // Get the next series of digits.
            //
            while (*psz && (*psz >= TEXT('0') && *psz <= TEXT('9')))
            {
                dwVal = dwVal*10 + *psz++ - TEXT('0');
            }
        }

        pdwBits[7-i] = dwVal;
    }
}

//
// Convert a pattern string to a top-down array of WORDs,
// useful for CreateBitmap().
//
void PatternToWords(LPTSTR psz, WORD *pwBits)
{
    WORD i, wVal;

    //
    // Get eight groups of numbers separated by non-numeric characters.
    //
    for (i=0; i<8; i++)
    {
        wVal = 0;

        if (*psz != TEXT('\0'))
        {
            //
            // Skip over any non-numeric characters.
            //
            while (*psz && (!(*psz >= TEXT('0') && *psz <= TEXT('9'))))
            {
                psz++;
            }

            //
            // Get the next series of digits.
            //
            while (*psz && ((*psz >= TEXT('0') && *psz <= TEXT('9'))))
            {
                wVal = wVal*10 + *psz++ - TEXT('0');
            }
        }

        pwBits[i] = wVal;
    }
}

BOOL IsValidPattern(LPCTSTR pszPat)
{
    BOOL fSawANumber = FALSE;

    //
    // We're mainly trying to filter multilingual upgrade cases
    // where the text for "(None)" is unpredictable.
    // 
    // 
    //
    while (*pszPat)
    {
        if ((*pszPat < TEXT('0')) || (*pszPat > TEXT('9')))
        {
            //
            // It's not a number, it better be a space.
            //
            if (*pszPat != TEXT(' '))
            {
                return FALSE;
            }
        }
        else
        {
            fSawANumber = TRUE;
        }

        //
        // We avoid the need for AnsiNext by only advancing on US TCHARs.
        //
        pszPat++;
    }

    //
    // TRUE if we saw at least one digit and there were only digits and spaces.
    //
    return fSawANumber;
}

//
// Determines if the wallpaper can be supported in non-active desktop mode.
//
BOOL IsNormalWallpaper(LPCTSTR pszFileName)
{
    BOOL fRet = TRUE;

    if (pszFileName[0] == TEXT('\0'))
    {
        fRet = TRUE;
    }
    else
    {
        LPTSTR pszExt = PathFindExtension(pszFileName);

        //Check for specific files that can be shown only in ActiveDesktop mode!
        if((StrCmpIC(pszExt, TEXT(".GIF")) == 0) || // 368690: Strange, but we must compare 'i' in both caps and lower case.
           (lstrcmpi(pszExt, TEXT(".JPG")) == 0) ||
           (lstrcmpi(pszExt, TEXT(".JPE")) == 0) ||
           (lstrcmpi(pszExt, TEXT(".JPEG")) == 0) ||
           (lstrcmpi(pszExt, TEXT(".PNG")) == 0) ||
           (lstrcmpi(pszExt, TEXT(".HTM")) == 0) ||
           (lstrcmpi(pszExt, TEXT(".HTML")) == 0) ||
           (lstrcmpi(pszExt, TEXT(".HTT")) == 0))
           return FALSE;

        //Everything else (including *.BMP files) are "normal" wallpapers
    }
    return fRet;
}

//
// Determines if the wallpaper is a picture (vs. HTML).
//
BOOL IsWallpaperPicture(LPCTSTR pszWallpaper)
{
    BOOL fRet = TRUE;

    if (pszWallpaper[0] == TEXT('\0'))
    {
        //
        // Empty wallpapers count as empty pictures.
        //
        fRet = TRUE;
    }
    else
    {
        LPTSTR pszExt = PathFindExtension(pszWallpaper);

        if ((lstrcmpi(pszExt, TEXT(".HTM")) == 0) ||
            (lstrcmpi(pszExt, TEXT(".HTML")) == 0) ||
            (lstrcmpi(pszExt, TEXT(".HTT")) == 0))
        {
            fRet = FALSE;
        }
    }

    return fRet;
}

void OnDesktopSysColorChange(void)
{
    static COLORREF clrBackground = 0xffffffff;
    static COLORREF clrWindowText = 0xffffffff;

    //Get the new colors!
    COLORREF    clrNewBackground = GetSysColor(COLOR_BACKGROUND);
    COLORREF    clrNewWindowText = GetSysColor(COLOR_WINDOWTEXT);

    //Have we initialized these before?
    if(clrBackground != 0xffffffff)  //Have we initialized the statics yet?
    {
        // Our HTML file depends only on these two system colors.
        // Check if either of them has changed!
        // If not, no need to regenerate HTML file. 
        // This avoids infinite loop. And this is a nice optimization.
        if((clrBackground == clrNewBackground) &&
           (clrWindowText == clrNewWindowText))
            return; //No need to do anything. Just return.
    }

    // Remember the new colors in the statics.
    clrBackground = clrNewBackground;
    clrWindowText = clrNewWindowText;

    //
    // The desktop got a WM_SYSCOLORCHANGE.  We need to
    // regenerate the HTML if there are any system colors
    // showing on the desktop.  Patterns and the desktop
    // color are both based on system colors.
    //
    IActiveDesktop *pad;
    if (SUCCEEDED(CActiveDesktop_InternalCreateInstance((LPUNKNOWN *)&pad, IID_IActiveDesktop)))
    {
        BOOL fRegenerateHtml = FALSE;
        WCHAR szWallpaperW[INTERNET_MAX_URL_LENGTH];

        if (SUCCEEDED(pad->GetWallpaper(szWallpaperW, ARRAYSIZE(szWallpaperW), 0)))
        {
            if (!*szWallpaperW)
            {
                //
                // No wallpaper means the desktop color
                // or a pattern is showing - we need to
                // regenerate the desktop HTML.
                //
                fRegenerateHtml = TRUE;
            }
            else
            {
                TCHAR *pszWallpaper;
#ifdef UNICODE
                pszWallpaper = szWallpaperW;
#else
                CHAR szWallpaperA[INTERNET_MAX_URL_LENGTH];
                SHUnicodeToAnsi(szWallpaperW, szWallpaperA, ARRAYSIZE(szWallpaperA));
                pszWallpaper = szWallpaperA;
#endif
                if (IsWallpaperPicture(pszWallpaper))
                {
                    WALLPAPEROPT wpo = { SIZEOF(wpo) };
                    if (SUCCEEDED(pad->GetWallpaperOptions(&wpo, 0)))
                    {
                        if (wpo.dwStyle == WPSTYLE_CENTER)
                        {
                            //
                            // We have a centered picture,
                            // the pattern or desktop color
                            // could be leaking around the edges.
                            // We need to regenerate the desktop
                            // HTML.
                            //
                            fRegenerateHtml = TRUE;
                        }
                    }
                    else
                    {
                        TraceMsg(TF_WARNING, "SYSCLRCHG: Could not get wallpaper options!");
                    }
                }
            }
        }
        else
        {
            TraceMsg(TF_WARNING, "SYSCLRCHG: Could not get selected wallpaper!");
        }

        
        if (fRegenerateHtml)
        {
            DWORD  dwFlags = AD_APPLY_FORCE | AD_APPLY_HTMLGEN | AD_APPLY_REFRESH | AD_APPLY_DYNAMICREFRESH;
            WCHAR   wszPattern[MAX_PATH];
            //If we have a pattern, then we need to force a AD_APPLY_COMPLETEREFRESH
            // because we need to re-generate the pattern.bmp file which can not be 
            // done through dynamic HTML.
            if(SUCCEEDED(pad->GetPattern(wszPattern, ARRAYSIZE(wszPattern), 0)))
            {
#ifdef UNICODE
                LPTSTR  szPattern = (LPTSTR)wszPattern;
#else
                CHAR   szPattern[MAX_PATH];
                SHUnicodeToAnsi(wszPattern, szPattern, sizeof(szPattern));
#endif //UNICODE
                if(IsValidPattern(szPattern))           //Does this have a pattern?
                    dwFlags &= ~(AD_APPLY_DYNAMICREFRESH);  //Then force a complete refresh!
                    
            }
            pad->ApplyChanges(dwFlags);
        }

        pad->Release();
    }
    else
    {
        TraceMsg(TF_WARNING, "SYSCLRCHG: Could not create CActiveDesktop!");
    }
}

//
// Convert a .URL file into its target.
//
void CheckAndResolveLocalUrlFile(LPTSTR pszFileName, int cchFileName)
{
    //
    // This function only works on *.URL files.
    //
    if (!PathIsURL(pszFileName))
    {
        LPTSTR pszExt;

        //
        // Check if the extension of this file is *.URL
        //
        pszExt = PathFindExtension(pszFileName);
        if (pszExt && *pszExt)
        {
            TCHAR  szUrl[15];
        
            LoadString(HINST_THISDLL, IDS_URL_EXTENSION, szUrl, ARRAYSIZE(szUrl));

            if (lstrcmpi(pszExt, szUrl) == 0)
            {
                HRESULT  hr;

                IUniformResourceLocator *purl;

                hr = CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER,
                              IID_IUniformResourceLocator,
                              (LPVOID *)&purl);

                if (EVAL(SUCCEEDED(hr)))  // This works for both Ansi and Unicode
                {
                    ASSERT(purl);

                    IPersistFile  *ppf;

                    hr = purl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf);
                    if (SUCCEEDED(hr))
                    {
                        WCHAR szFileW[MAX_PATH];
                        LPTSTR pszTemp;

                        SHTCharToUnicode(pszFileName, szFileW, ARRAYSIZE(szFileW));
                        ppf->Load(szFileW, STGM_READ);
                        hr = purl->GetURL(&pszTemp);    // Wow, an ANSI/UNICODE COM interface!
                        if (EVAL(SUCCEEDED(hr)))
                        {
                            StrCpyN(pszFileName, pszTemp, cchFileName);
                            CoTaskMemFree(pszTemp);
                        }
                        ppf->Release();
                    }
                    purl->Release();
                }
            }
        }
    }
}


void GetMyCurHomePageStartPos(int *piLeft, int *piTop, DWORD *pdwWidth, DWORD *pdwHeight)
{
#define INVALID_POS 0x80000000
    HKEY hkey;

    //
    // Assume nothing.
    //
    *piLeft = INVALID_POS;
    *piTop = INVALID_POS;
    *pdwWidth = INVALID_POS;
    *pdwHeight = INVALID_POS;

    //
    // Read from registry first.
    //
    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Internet Explorer\\Main"), 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS)
    {
        DWORD dwType, cbData;

        cbData = SIZEOF(*piLeft);
        SHQueryValueEx(hkey, TEXT("MyCurHome_Left"), NULL, &dwType, (LPBYTE)piLeft, &cbData);

        cbData = SIZEOF(*piTop);
        SHQueryValueEx(hkey, TEXT("MyCurHome_Top"), NULL, &dwType, (LPBYTE)piTop, &cbData);

        cbData = SIZEOF(*pdwWidth);
        SHQueryValueEx(hkey, TEXT("MyCurHome_Width"), NULL, &dwType, (LPBYTE)pdwWidth, &cbData);

        cbData = SIZEOF(*pdwHeight);
        SHQueryValueEx(hkey, TEXT("MyCurHome_Height"), NULL, &dwType, (LPBYTE)pdwHeight, &cbData);

        RegCloseKey(hkey);
    }

    //
    // Fill in defaults when registry provides no info.
    //
    if (*piLeft == INVALID_POS)
    {
        *piLeft = -MYCURHOME_WIDTH;
    }
    if (*piTop == INVALID_POS)
    {
        *piTop = MYCURHOME_TOP;
    }
    if (*pdwWidth == INVALID_POS)
    {
        *pdwWidth = MYCURHOME_WIDTH;
    }
    if (*pdwHeight == INVALID_POS)
    {
        *pdwHeight = MYCURHOME_HEIGHT;
    }

    //
    // Convert negative values into positive ones.
    //
    RECT rect;
    SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, FALSE);
    if (*piLeft < 0)
    {
        *piLeft += (rect.right - rect.left) - *pdwWidth;
    }
    if (*piTop < 0)
    {
        *piTop += (rect.bottom - rect.top) - *pdwHeight;
    }

    // Find the virtual dimensions.
    EnumMonitorsArea ema;
    GetMonitorSettings(&ema);
    // Position it in the primary monitor.
    *piLeft -= ema.rcVirtualMonitor.left;
    *piTop -= ema.rcVirtualMonitor.top;
#undef INVALID_POS

}


//
// Silently adds/removes a specified component to the desktop and use the given
//  apply flags using which you can avoid nested unnecessary HTML generation, 
//  or refreshing which may lead to racing conditions.
// 
//
BOOL AddRemoveDesktopComponentNoUI(BOOL fAdd, DWORD dwApplyFlags, LPCTSTR pszUrl, LPCTSTR pszFriendlyName, int iCompType, int iLeft, int iTop, int iWidth, int iHeight, BOOL fChecked, DWORD dwCurItemState, BOOL fNoScroll, BOOL fCanResize)
{
    COMPONENTA Comp;
    BOOL    fRet = FALSE;
    HRESULT hres;

    //
    // Build the pcomp structure.
    //
    Comp.dwSize = sizeof(COMPONENTA);
    Comp.dwID = -1;
    Comp.iComponentType = iCompType;
    Comp.fChecked = fChecked;
    Comp.fDirty = FALSE;
    Comp.fNoScroll = fNoScroll;
    Comp.cpPos.dwSize = SIZEOF(COMPPOS);
    Comp.cpPos.iLeft = iLeft;
    Comp.cpPos.iTop = iTop;
    Comp.cpPos.dwWidth = iWidth;
    Comp.cpPos.dwHeight = iHeight;
    Comp.cpPos.izIndex = (dwCurItemState & IS_NORMAL) ? COMPONENT_TOP : 0;
    Comp.cpPos.fCanResize = fCanResize;
    Comp.cpPos.fCanResizeX = fCanResize;
    Comp.cpPos.fCanResizeY = fCanResize;
    Comp.cpPos.iPreferredLeftPercent = 0;
    Comp.cpPos.iPreferredTopPercent = 0;
    Comp.dwCurItemState = dwCurItemState;
    lstrcpyn(Comp.szSource, pszUrl, ARRAYSIZE(Comp.szSource));
    lstrcpyn(Comp.szSubscribedURL, pszUrl, ARRAYSIZE(Comp.szSource));
    if (pszFriendlyName)
    {
        lstrcpyn(Comp.szFriendlyName, pszFriendlyName, ARRAYSIZE(Comp.szFriendlyName));
    }
    else
    {
        Comp.szFriendlyName[0] = TEXT('\0');
    }

    IActiveDesktop *pActiveDesk;

    //
    // Add it to the system.
    //
    hres = CActiveDesktop_InternalCreateInstance((LPUNKNOWN *)&pActiveDesk, IID_IActiveDesktop);
    if (SUCCEEDED(hres))
    {
        COMPONENT  CompW;

        CompW.dwSize = sizeof(CompW);  //Required for the MultiCompToWideComp to work properly.

        MultiCompToWideComp(&Comp, &CompW);

        if(fAdd)
            pActiveDesk->AddDesktopItem(&CompW, 0);
        else
            pActiveDesk->RemoveDesktopItem(&CompW, 0);
            
        pActiveDesk->ApplyChanges(dwApplyFlags);
        pActiveDesk->Release();
        fRet = TRUE;
    }

    return fRet;
}

//
//  Summary: 
//      On upgrade from W2K, it is possible (under certain conditions) that the Active Desktop  
//  gets turned ON automatically. This is bug #154993. The following function fixes this bug.
//
// Details of why this happens:
//
// In W2K, it is possible to enable active desktop components, hide icons, lock the components
// and then turn off active desktop. But, all the details (like what AD components were ON etc.,)
// was persisted in the registry. When such a machine is upgraded to Whister, bug #154993 surfaces
// because of the following reason:
//  In Whislter, ActiveDesktop is turned on/off silently based on whether any desktop component is 
// on etc., As a result when a W2K machine (with AD off) is upgraded to Whistler, the AD will be
// turned on automatically, if one of the following is true:
//    1. If the desktop icons were off.
//    2. If the active desktop components were locked.
//    3. If any active desktop component is ON; but, not displayed because AD was OFF..
//  Therefore on upgrade from a Win2K or older machine, we check if the AD is OFF. If so, then we 
// need to check for conditions 1, 2 and 3 and change those settings such that AD continues to be 
// OFF even after the upgrade. The following function OnUpgradeDisableActiveDesktopFeatures ()
// does precisely this.
//
// Returns: TRUE, if any setting was modified to keep the active desktop in the turned off state!
//
BOOL   OnUpgradeDisableActiveDesktopFeatures()
{
    IActiveDesktop *pActiveDesk;
    BOOL    fModified = FALSE;

    // Get the ActiveDesktop and HideIcons flags.
    SHELLSTATE  ss = {0};
    SHGetSetSettings(&ss, SSF_DESKTOPHTML | SSF_HIDEICONS, FALSE);

    //Check if ActiveDesktop is already ON.
    if(ss.fDesktopHTML)
        return FALSE;  //ActiveDesktop is already ON. No need to change any settings.

    //Active Desktop is OFF. We may need to change the other settings to be consistent with this!

    // 1. Check if Desktop icons are hidden when ActiveDesktop is on.
    if(ss.fHideIcons)
    {
        //Yes! Turn off this. Otherwise, AD will be turned on to support this!
        ss.fHideIcons = FALSE;
        SHGetSetSettings(&ss, SSF_HIDEICONS, TRUE);
        fModified = TRUE;
    }
    
    // 2. If the ActiveDesktop components are locked, un-lock them.
    DWORD dwDesktopFlags = GetDesktopFlags();
    
    if(dwDesktopFlags & COMPONENTS_LOCKED)
    {
        if(SetDesktopFlags(COMPONENTS_LOCKED, 0)) //Remove the "locked" flag!
            fModified = TRUE;
    }

    // 3. Let's enumerate all active desktop components and make sure they are all off.
    BOOL fModifiedComp = FALSE;

    HRESULT hres = CActiveDesktop_InternalCreateInstance((LPUNKNOWN *)&pActiveDesk, IID_IActiveDesktop);
    if (SUCCEEDED(hres))
    {
        int iCount = 0;
        pActiveDesk->GetDesktopItemCount(&iCount, 0);

        for(int i = 0; i < iCount; i++)
        {
            COMPONENT Comp;
            Comp.dwSize = sizeof(Comp);
            if(SUCCEEDED(pActiveDesk->GetDesktopItem(i, &Comp, 0)))
            {
                if(Comp.fChecked)           //If this component is enabled.....
                {
                    Comp.fChecked = FALSE;  //...., then disable it!
                    if(SUCCEEDED(pActiveDesk->ModifyDesktopItem(&Comp, COMP_ELEM_CHECKED)))
                        fModifiedComp = TRUE;
                }
            }
        }

        if(fModifiedComp)
            pActiveDesk->ApplyChanges(AD_APPLY_SAVE); //We just need to save the above changes.

        pActiveDesk ->Release();
    }

    //return whether we modified any setting.
    return (fModified || fModifiedComp);
}



// Little helper function used to change the safemode state
void SetSafeMode(DWORD dwFlags)
{
    IActiveDesktopP * piadp;

    if (SUCCEEDED(CActiveDesktop_InternalCreateInstance((LPUNKNOWN *)&piadp, IID_IActiveDesktopP)))
    {
        piadp->SetSafeMode(dwFlags);
        piadp->Release();
    }
}

/****************************************************************************
 *
 *  RefreshWebViewDesktop - regenerates desktop HTML from registry and updates
 *                          the screen
 *
 *  ENTRY:
 *      none
 *
 *  RETURNS:
 *      TRUE on success
 *      
 ****************************************************************************/
BOOL PokeWebViewDesktop(DWORD dwFlags)
{
    IActiveDesktop *pad;
    HRESULT     hres;
    BOOL        fRet = FALSE;

    hres = CActiveDesktop_InternalCreateInstance((LPUNKNOWN *)&pad, IID_IActiveDesktop);

    if (SUCCEEDED(hres))
    {
        pad->ApplyChanges(dwFlags);
        pad->Release();

        fRet = TRUE;
    }

    return (fRet);
}

#define CCH_NONE 20 //big enough for "(None)" in german
TCHAR g_szNone[CCH_NONE] = {0};

void InitDeskHtmlGlobals(void)
{
    static fGlobalsInited = FALSE;

    if (fGlobalsInited == FALSE)
    {
        LoadString(HINST_THISDLL, IDS_WPNONE, g_szNone, ARRAYSIZE(g_szNone));

        fGlobalsInited = TRUE;
    }
}

//
// Loads the preview bitmap for property sheet pages.
//
HBITMAP LoadMonitorBitmap(void)
{
    HBITMAP hbm,hbmT;
    BITMAP bm;
    HBRUSH hbrT;
    HDC hdc;
    COLORREF c3df = GetSysColor(COLOR_3DFACE);

    hbm = LoadBitmap(HINST_THISDLL, MAKEINTRESOURCE(IDB_MONITOR));
    if (hbm == NULL)
    {
        return NULL;
    }

    //
    // Convert the "base" of the monitor to the right color.
    //
    // The lower left of the bitmap has a transparent color
    // we fixup using FloodFill
    //
    hdc = CreateCompatibleDC(NULL);
    hbmT = (HBITMAP)SelectObject(hdc, hbm);
    hbrT = (HBRUSH)SelectObject(hdc, GetSysColorBrush(COLOR_3DFACE));

    GetObject(hbm, sizeof(bm), &bm);

    ExtFloodFill(hdc, 0, bm.bmHeight-1, GetPixel(hdc, 0, bm.bmHeight-1), FLOODFILLSURFACE);

    //
    // Round off the corners.
    // The bottom two were done by the floodfill above.
    // The top left is important since SS_CENTERIMAGE uses it to fill gaps.
    // The top right should be rounded because the other three are.
    //
    SetPixel( hdc, 0, 0, c3df );
    SetPixel( hdc, bm.bmWidth-1, 0, c3df );

    //
    // Fill in the desktop here.
    //
    HBRUSH hbrOld = (HBRUSH)SelectObject(hdc, GetSysColorBrush(COLOR_DESKTOP));
    PatBlt(hdc, MON_X, MON_Y, MON_DX, MON_DY, PATCOPY);
    SelectObject(hdc, hbrOld);

    //
    // Clean up after ourselves.
    //
    SelectObject(hdc, hbrT);
    SelectObject(hdc, hbmT);
    DeleteDC(hdc);

    return hbm;
}

STDAPI_(VOID) ActiveDesktop_ApplyChanges()
{
    IActiveDesktop* piad;
    if (SUCCEEDED(CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IActiveDesktop, &piad))))
    {        
        piad->ApplyChanges(AD_APPLY_ALL | AD_APPLY_DYNAMICREFRESH);
        piad->Release();
    }
    
}

STDAPI_(DWORD) GetDesktopFlags(void)
{
    DWORD dwFlags = 0, dwType, cbSize = SIZEOF(dwFlags);
    TCHAR lpszDeskcomp[MAX_PATH];

    GetRegLocation(lpszDeskcomp, SIZECHARS(lpszDeskcomp), REG_DESKCOMP_COMPONENTS, NULL);

    SHGetValue(HKEY_CURRENT_USER, lpszDeskcomp, REG_VAL_COMP_GENFLAGS, &dwType, &dwFlags, &cbSize);

    return dwFlags;
}

STDAPI_(BOOL) SetDesktopFlags(DWORD dwMask, DWORD dwNewFlags)
{
    BOOL  fRet = FALSE;
    HKEY  hkey;
    DWORD dwDisposition;
    TCHAR lpszDeskcomp[MAX_PATH];

    GetRegLocation(lpszDeskcomp, SIZECHARS(lpszDeskcomp), REG_DESKCOMP_COMPONENTS, NULL);

    if (RegCreateKeyEx(HKEY_CURRENT_USER, (LPCTSTR)lpszDeskcomp, 
                       0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &hkey,
                       &dwDisposition) == ERROR_SUCCESS)
    {
        DWORD dwFlags;
        DWORD cbSize = SIZEOF(dwFlags);
        DWORD dwType;

        if (SHQueryValueEx(hkey, REG_VAL_COMP_GENFLAGS, NULL, &dwType,
                            (LPBYTE)&dwFlags, &cbSize) != ERROR_SUCCESS)
        {
            dwFlags = 0;
        }

        dwFlags = (dwFlags & ~dwMask) | (dwNewFlags & dwMask);

        if (RegSetValueEx(hkey, REG_VAL_COMP_GENFLAGS, 0, REG_DWORD,
                          (LPBYTE)&dwFlags, sizeof(dwFlags)) == ERROR_SUCCESS)
        {
            fRet = TRUE;
        }
    
        RegCloseKey(hkey);
    }

    return fRet;
}

BOOL UpdateComponentFlags(LPCTSTR pszCompId, DWORD dwMask, DWORD dwNewFlags)
{
    BOOL fRet = FALSE;
    TCHAR szRegPath[MAX_PATH];
    HKEY hkey;

    GetRegLocation(szRegPath, SIZECHARS(szRegPath), REG_DESKCOMP_COMPONENTS, NULL);

    lstrcat(szRegPath, TEXT("\\"));
    lstrcat(szRegPath, pszCompId);

    //Don't use RegCreateKeyEx here. It will result in Null components to be added.
    if (RegOpenKeyEx(HKEY_CURRENT_USER, szRegPath, 0,
                       KEY_READ | KEY_WRITE, &hkey) == ERROR_SUCCESS)
    {
        DWORD dwType, dwFlags, dwDataLength;

        dwDataLength = sizeof(DWORD);
        if(SHQueryValueEx(hkey, REG_VAL_COMP_FLAGS, NULL, &dwType, (LPBYTE)&dwFlags, &dwDataLength) != ERROR_SUCCESS)
        {
            dwFlags = 0;
        }        

        dwNewFlags = (dwFlags & ~dwMask) | (dwNewFlags & dwMask);

        if (RegSetValueEx(hkey, REG_VAL_COMP_FLAGS, 0, REG_DWORD, (LPBYTE)&dwNewFlags,
                          SIZEOF(DWORD)) == ERROR_SUCCESS)
        {
            fRet = TRUE;
        }

        SetDesktopFlags(COMPONENTS_DIRTY, COMPONENTS_DIRTY);

        RegCloseKey(hkey);
    }
    else
    {
        TraceMsg(TF_WARNING, "DS: Unable to UpdateComponentFlags");
    }

    return fRet;
}

DWORD GetCurrentState(LPTSTR pszCompId)
{
    TCHAR szRegPath[MAX_PATH];
    DWORD cbSize, dwType, dwCurState;

    GetRegLocation(szRegPath, SIZECHARS(szRegPath), REG_DESKCOMP_COMPONENTS, NULL);
    lstrcat(szRegPath, TEXT("\\"));
    lstrcat(szRegPath, pszCompId);

    cbSize = sizeof(dwCurState);

    if (SHGetValue(HKEY_CURRENT_USER, szRegPath, REG_VAL_COMP_CURSTATE, &dwType, &dwCurState, &cbSize) != ERROR_SUCCESS)
        dwCurState = IS_NORMAL;

    return dwCurState;
}

BOOL GetSavedStateInfo(LPTSTR pszCompId, LPCOMPSTATEINFO    pCompState, BOOL  fRestoredState)
{
    BOOL fRet = FALSE;
    TCHAR szRegPath[MAX_PATH];
    HKEY hkey;
    LPTSTR  lpValName = (fRestoredState ? REG_VAL_COMP_RESTOREDSTATEINFO : REG_VAL_COMP_ORIGINALSTATEINFO);

    GetRegLocation(szRegPath, SIZECHARS(szRegPath), REG_DESKCOMP_COMPONENTS, NULL);
    lstrcat(szRegPath, TEXT("\\"));
    lstrcat(szRegPath, pszCompId);

    //No need to use RegCreateKeyEx here. Use RegOpenKeyEx instead.
    if (RegOpenKeyEx(HKEY_CURRENT_USER, szRegPath, 0,
                       KEY_READ, &hkey) == ERROR_SUCCESS)
    {
        DWORD   cbSize, dwType;
        
        cbSize = SIZEOF(*pCompState);
        dwType = REG_BINARY;
        
        if (SHQueryValueEx(hkey, lpValName, NULL, &dwType, (LPBYTE)pCompState, &cbSize) != ERROR_SUCCESS)
        {
            //If the item state is missing, read the item current position and
            // and return that as the saved state.
            COMPPOS cpPos;

            cbSize = SIZEOF(cpPos);
            dwType = REG_BINARY;
            if (SHQueryValueEx(hkey, REG_VAL_COMP_POSITION, NULL, &dwType, (LPBYTE)&cpPos, &cbSize) != ERROR_SUCCESS)
            {
                ZeroMemory(&cpPos, SIZEOF(cpPos));
            }            
            SetStateInfo(pCompState, &cpPos, IS_NORMAL);
        }

        RegCloseKey(hkey);
    }
    else
        TraceMsg(TF_WARNING, "DS: Unable to get SavedStateInfo()");

    return fRet;
 }


BOOL UpdateDesktopPosition(LPTSTR pszCompId, int iLeft, int iTop, DWORD dwWidth, DWORD dwHeight, int izIndex,
                            BOOL    fSaveRestorePos, BOOL fSaveOriginal, DWORD dwCurState)
{
    BOOL fRet = FALSE;
    TCHAR szRegPath[MAX_PATH];
    HKEY hkey;

    GetRegLocation(szRegPath, SIZECHARS(szRegPath), REG_DESKCOMP_COMPONENTS, NULL);
    lstrcat(szRegPath, TEXT("\\"));
    lstrcat(szRegPath, pszCompId);

    //Don't use RegCreateKeyEx here; It will result in a NULL component being added.
    if (RegOpenKeyEx(HKEY_CURRENT_USER, szRegPath, 0,
                       KEY_READ | KEY_WRITE, &hkey) == ERROR_SUCCESS)
    {
        COMPPOS         cp;
        DWORD           dwType;
        DWORD           dwDataLength;
        COMPSTATEINFO   csi;

        dwType = REG_BINARY;
        dwDataLength = sizeof(COMPPOS);

        if(SHQueryValueEx(hkey, REG_VAL_COMP_POSITION, NULL, &dwType, (LPBYTE)&cp, &dwDataLength) != ERROR_SUCCESS)
        {
            cp.fCanResize = cp.fCanResizeX = cp.fCanResizeY = TRUE;
            cp.iPreferredLeftPercent = cp.iPreferredTopPercent = 0;
        }

        //Read the current State
        dwType = REG_DWORD;
        dwDataLength = SIZEOF(csi.dwItemState);
        if (SHQueryValueEx(hkey, REG_VAL_COMP_CURSTATE, NULL, &dwType, (LPBYTE)&csi.dwItemState, &dwDataLength) != ERROR_SUCCESS)
        {
            csi.dwItemState = IS_NORMAL;
        }

        if(fSaveRestorePos)
        {
            //We have just read the current position; Let's save it as the restore position.
            SetStateInfo(&csi, &cp, csi.dwItemState);

            //Now that we know the complete current state, save it as the restore state!
            RegSetValueEx(hkey, REG_VAL_COMP_RESTOREDSTATEINFO, 0, REG_BINARY, (LPBYTE)&csi, SIZEOF(csi));
        }

        //Save the current state too!
        if(dwCurState)
            RegSetValueEx(hkey, REG_VAL_COMP_CURSTATE, 0, REG_DWORD, (LPBYTE)&dwCurState, SIZEOF(dwCurState));
            
        cp.dwSize = sizeof(COMPPOS);
        cp.iLeft = iLeft;
        cp.iTop = iTop;
        cp.dwWidth = dwWidth;
        cp.dwHeight = dwHeight;
        cp.izIndex = izIndex;

        if (fSaveOriginal) {
            SetStateInfo(&csi, &cp, csi.dwItemState);
            RegSetValueEx(hkey, REG_VAL_COMP_ORIGINALSTATEINFO, 0, REG_BINARY, (LPBYTE)&csi, SIZEOF(csi));
        }

        if (RegSetValueEx(hkey, REG_VAL_COMP_POSITION, 0, REG_BINARY, (LPBYTE)&cp,
                          SIZEOF(cp)) == ERROR_SUCCESS)
        {
            fRet = TRUE;
        }

        // Don't need to mark as dirty if we're just saving the original pos
        if (!fSaveOriginal)
            SetDesktopFlags(COMPONENTS_DIRTY, COMPONENTS_DIRTY);

        RegCloseKey(hkey);

    }
    else
    {
        TraceMsg(TF_WARNING, "DS: Unable to UpdateDesktopPosition");
    }

    return fRet;
}


HRESULT EnsureFilePathIsPresent(LPCTSTR pszFilePath)
//  Recursively process (in reverse) a path and create the first
//  directory that does not exist and when unwinding the calling
//  chain create each subsequent directory in the path until the
//  whole original path is created.
{
    HRESULT hr = E_INVALIDARG;

    if (pszFilePath && pszFilePath[0])
    {
        TCHAR   szDesktopFileDirectory[MAX_PATH];

        hr = S_OK;
        lstrcpy(szDesktopFileDirectory, pszFilePath);
        ASSERT(lstrlen(szDesktopFileDirectory) > lstrlen(TEXT("C:\\")));      // something wrong if the root directory is hit
        if ((PathRemoveFileSpec(szDesktopFileDirectory) != 0) && (GetFileAttributes(szDesktopFileDirectory) == 0xFFFFFFFF) && (CreateDirectory(szDesktopFileDirectory, NULL) == 0))
        {
            hr = EnsureFilePathIsPresent(szDesktopFileDirectory);
            TBOOL(CreateDirectory(szDesktopFileDirectory, NULL));
        }
    }

    return hr;
}


HRESULT GetPerUserFileName(LPTSTR pszOutputFileName, DWORD dwSize, LPTSTR pszPartialFileName)
{
    LPITEMIDLIST    pidlAppData;

    *pszOutputFileName = TEXT('\0');

    if(dwSize < MAX_PATH)
    {
        ASSERT(FALSE);
        return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
    }

    HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, &pidlAppData);
    if (SUCCEEDED(hr))
    {
        SHGetPathFromIDList(pidlAppData, pszOutputFileName);
        PathAppend(pszOutputFileName, pszPartialFileName);
        ILFree(pidlAppData);

        hr = EnsureFilePathIsPresent(pszOutputFileName);
    }

    return hr;
}


void GetRegLocation(LPTSTR lpszResult, DWORD cchResult, LPCTSTR lpszKey, LPCTSTR lpszScheme)
{
    TCHAR szSubkey[MAX_PATH];
    DWORD dwDataLength = sizeof(szSubkey) - 2 * sizeof(TCHAR);
    DWORD dwType;

    lstrcpy(szSubkey, TEXT("\\"));

    //  use what was given or get it from the registry
    if (lpszScheme)
        StrCatBuff(szSubkey, lpszScheme, SIZECHARS(szSubkey));
    else
        SHGetValue(HKEY_CURRENT_USER, REG_DESKCOMP_SCHEME, REG_VAL_SCHEME_DISPLAY, &dwType,
            (LPBYTE)(szSubkey) + sizeof(TCHAR), &dwDataLength);

    if (szSubkey[1])
        StrCatBuff(szSubkey, TEXT("\\"), SIZECHARS(szSubkey));

    wnsprintf(lpszResult, cchResult, lpszKey, szSubkey);
}

BOOL ValidateFileName(HWND hwnd, LPCTSTR pszFilename, int iTypeString)
{
    BOOL fRet = TRUE;

    DWORD dwAttributes = GetFileAttributes(pszFilename);
    if ((dwAttributes != 0xFFFFFFFF) &&
        (dwAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)))
    {
        TCHAR szType1[64];
        TCHAR szType2[64];

        LoadString(HINST_THISDLL, iTypeString, szType1, ARRAYSIZE(szType1));
        LoadString(HINST_THISDLL, iTypeString+1, szType2, ARRAYSIZE(szType2));
        if (ShellMessageBox(HINST_THISDLL, hwnd,
                            MAKEINTRESOURCE(IDS_VALIDFN_FMT),
                            MAKEINTRESOURCE(IDS_VALIDFN_TITLE),
                            MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2,
                            szType1, szType2) == IDNO)
        {
            fRet = FALSE;
        }
    }

    return fRet;
}

void GetWallpaperDirName(LPTSTR lpszWallPaperDir, int iBuffSize)
{
    TCHAR szExp[MAX_PATH];

    //Compute the default wallpaper name.
    if (GetWindowsDirectory(lpszWallPaperDir, iBuffSize) != 0)
    {
        lstrcat(lpszWallPaperDir, DESKTOPHTML_WEB_DIR);

        //Read it from the registry key, if it is set!
        DWORD dwType;
        DWORD cbData = (DWORD)iBuffSize;
        SHGetValue(HKEY_LOCAL_MACHINE, REGSTR_PATH_SETUP, c_szWallPaperDir, &dwType, (LPVOID)lpszWallPaperDir, &cbData);

        // Maybe we should check if it is REG_EXPAND_SZ, to save a few CPU cycle?
        SHExpandEnvironmentStrings(lpszWallPaperDir, szExp, ARRAYSIZE(szExp));
        lstrcpyn(lpszWallPaperDir, szExp, iBuffSize);
    }
}

BOOL CALLBACK MultiMonEnumAreaCallBack(HMONITOR hMonitor, HDC hdc, LPRECT lprc, LPARAM lData)
{
    EnumMonitorsArea* pEMA = (EnumMonitorsArea*)lData;
    
    if (pEMA->iMonitors > LV_MAX_WORKAREAS - 1)
    {
        //ignore the other monitors because we can only handle up to LV_MAX_WORKAREAS
        //REARCHITECT: should we dynamically allocate this?
        return FALSE;
    }
    GetMonitorRect(hMonitor, &pEMA->rcMonitor[pEMA->iMonitors]);
    GetMonitorWorkArea(hMonitor, &pEMA->rcWorkArea[pEMA->iMonitors]);
    if(pEMA->iMonitors == 0)
    {
        pEMA->rcVirtualMonitor.left = pEMA->rcMonitor[0].left;
        pEMA->rcVirtualMonitor.top = pEMA->rcMonitor[0].top;
        pEMA->rcVirtualMonitor.right = pEMA->rcMonitor[0].right;
        pEMA->rcVirtualMonitor.bottom = pEMA->rcMonitor[0].bottom;

        pEMA->rcVirtualWorkArea.left = pEMA->rcWorkArea[0].left;
        pEMA->rcVirtualWorkArea.top = pEMA->rcWorkArea[0].top;
        pEMA->rcVirtualWorkArea.right = pEMA->rcWorkArea[0].right;
        pEMA->rcVirtualWorkArea.bottom = pEMA->rcWorkArea[0].bottom;
    }
    else
    {
        if(pEMA->rcMonitor[pEMA->iMonitors].left < pEMA->rcVirtualMonitor.left)
        {
            pEMA->rcVirtualMonitor.left = pEMA->rcMonitor[pEMA->iMonitors].left;
        }
        if(pEMA->rcMonitor[pEMA->iMonitors].top < pEMA->rcVirtualMonitor.top)
        {
            pEMA->rcVirtualMonitor.top = pEMA->rcMonitor[pEMA->iMonitors].top;
        }
        if(pEMA->rcMonitor[pEMA->iMonitors].right > pEMA->rcVirtualMonitor.right)
        {
            pEMA->rcVirtualMonitor.right = pEMA->rcMonitor[pEMA->iMonitors].right;
        }
        if(pEMA->rcMonitor[pEMA->iMonitors].bottom > pEMA->rcVirtualMonitor.bottom)
        {
            pEMA->rcVirtualMonitor.bottom = pEMA->rcMonitor[pEMA->iMonitors].bottom;
        }

        if(pEMA->rcWorkArea[pEMA->iMonitors].left < pEMA->rcVirtualWorkArea.left)
        {
            pEMA->rcVirtualWorkArea.left = pEMA->rcWorkArea[pEMA->iMonitors].left;
        }
        if(pEMA->rcWorkArea[pEMA->iMonitors].top < pEMA->rcVirtualWorkArea.top)
        {
            pEMA->rcVirtualWorkArea.top = pEMA->rcWorkArea[pEMA->iMonitors].top;
        }
        if(pEMA->rcWorkArea[pEMA->iMonitors].right > pEMA->rcVirtualWorkArea.right)
        {
            pEMA->rcVirtualWorkArea.right = pEMA->rcWorkArea[pEMA->iMonitors].right;
        }
        if(pEMA->rcWorkArea[pEMA->iMonitors].bottom > pEMA->rcVirtualWorkArea.bottom)
        {
            pEMA->rcVirtualWorkArea.bottom = pEMA->rcWorkArea[pEMA->iMonitors].bottom;
        }
    }
    pEMA->iMonitors++;
    return TRUE;
}

void GetMonitorSettings(EnumMonitorsArea* ema)
{
    ema->iMonitors = 0;

    ema->rcVirtualMonitor.left = 0;
    ema->rcVirtualMonitor.top = 0;
    ema->rcVirtualMonitor.right = 0;
    ema->rcVirtualMonitor.bottom = 0;

    ema->rcVirtualWorkArea.left = 0;
    ema->rcVirtualWorkArea.top = 0;
    ema->rcVirtualWorkArea.right = 0;
    ema->rcVirtualWorkArea.bottom = 0;

    EnumDisplayMonitors(NULL, NULL, MultiMonEnumAreaCallBack, (LPARAM)ema);
}

int _GetWorkAreaIndexWorker(POINT pt, LPCRECT prect, int crect)
{
    int iIndex;

    for (iIndex = 0; iIndex < crect; iIndex++)
    {
        if (PtInRect(&prect[iIndex], pt))
        {
            return iIndex;
        }
    }

    return -1;
}

int GetWorkAreaIndexFromPoint(POINT pt, LPCRECT prect, int crect)
{
    ASSERT(crect);

    // Map to correct coords...
    pt.x += prect[0].left;
    pt.y += prect[0].top;

    return _GetWorkAreaIndexWorker(pt, prect, crect);
}

int GetWorkAreaIndex(COMPPOS *pcp, LPCRECT prect, int crect, LPPOINT lpptVirtualTopLeft)
{
    POINT ptComp;
    ptComp.x = pcp->iLeft + lpptVirtualTopLeft->x;
    ptComp.y = pcp->iTop + lpptVirtualTopLeft->y;

    return _GetWorkAreaIndexWorker(ptComp, prect, crect);
}

// Prepends the Web wallpaper directory or the system directory to szWallpaper, if necessary
// (i.e., if the path is not specified). The return value is in szWallpaperWithPath, which is iBufSize
// bytes long
void GetWallpaperWithPath(LPCTSTR szWallpaper, LPTSTR szWallpaperWithPath, int iBufSize)
{
    if (szWallpaper[0] && lstrcmpi(szWallpaper, g_szNone) != 0 && !StrChr(szWallpaper, TEXT('\\'))
            && !StrChr(szWallpaper, TEXT(':'))) // The file could be d:foo.bmp
    {
        // If the file is a normal wallpaper, we prepend the windows directory to the filename
        if (IsNormalWallpaper(szWallpaper))
        {
            GetWindowsDirectory(szWallpaperWithPath, iBufSize);
        }
        // else we prepend the wallpaper directory to the filename
        else
        {
            GetWallpaperDirName(szWallpaperWithPath, iBufSize);
        }
        PathAppend(szWallpaperWithPath, szWallpaper);
    }
    else
    {
        lstrcpyn(szWallpaperWithPath, szWallpaper, iBufSize);
    }
}

BOOL GetViewAreas(LPRECT lprcViewAreas, int* pnViewAreas)
{
    BOOL bRet = FALSE;
    HWND hwndDesktop = GetShellWindow();    // This is the "normal" desktop
    
    if (hwndDesktop && IsWindow(hwndDesktop))
    {
        DWORD dwProcID, dwCurrentProcID;
        
        GetWindowThreadProcessId(hwndDesktop, &dwProcID);
        dwCurrentProcID = GetCurrentProcessId();
        if (dwCurrentProcID == dwProcID) {
            SendMessage(hwndDesktop, DTM_GETVIEWAREAS, (WPARAM)pnViewAreas, (LPARAM)lprcViewAreas);
            if (*pnViewAreas <= 0)
            {
                bRet = FALSE;
            }
            else
            {
                bRet = TRUE;
            }
        }
        else
        {
            bRet = FALSE;
        }
    }
    return bRet;
}

// We need to enforce a minimum size for the deskmovr caption since it doesn't look
// right drawn any smaller
int GetcyCaption()
{
    int cyCaption = GetSystemMetrics(SM_CYSMCAPTION);

    if (cyCaption < 15)
        cyCaption = 15;

    cyCaption -= GetSystemMetrics(SM_CYBORDER);

    return cyCaption;
}



void PathUnExpandEnvStringsWrap(LPTSTR pszString, DWORD cchSize)
{
    TCHAR szTemp[MAX_PATH];

    StrCpyN(szTemp, pszString, ARRAYSIZE(szTemp));
    if (!PathUnExpandEnvStrings(szTemp, pszString, cchSize))
    {
        StrCpyN(pszString, szTemp, cchSize);
    }
}



void PathExpandEnvStringsWrap(LPTSTR pszString, DWORD cchSize)
{
    TCHAR szTemp[MAX_PATH];

    StrCpyN(szTemp, pszString, ARRAYSIZE(szTemp));
    if (0 == SHExpandEnvironmentStrings(szTemp, pszString, cchSize))
    {
        StrCpyN(pszString, szTemp, cchSize);
    }
}