2444 lines
78 KiB
C++
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, ®DataSize));
|
|
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, ®DataSize));
|
|
++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);
|
|
}
|
|
}
|