windows-nt/Source/XPSP1/NT/shell/shell32/unicpp/dutil.cpp
2020-09-26 16:20:57 +08:00

2444 lines
78 KiB
C++

#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);
}
}