You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
479 lines
15 KiB
479 lines
15 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: class.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* This module contains RegisterClass and the related window class management
|
|
* functions.
|
|
*
|
|
* History:
|
|
* 12-20-94 FritzS
|
|
*
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
BOOL VisWindow(PWND,DWORD);
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxSetClassIconEnum
|
|
\***************************************************************************/
|
|
BOOL xxxSetClassIconEnum(
|
|
PWND pwnd,
|
|
LPARAM lParam)
|
|
{
|
|
CheckLock(pwnd);
|
|
|
|
if (pwnd->pcls == (PCLS)lParam) {
|
|
/*
|
|
* If the window doesn't have a small icon or it comes from
|
|
* WM_QUERYDRAGICON, redraw the title. In the WM_QUERYDRAGICON
|
|
* case, get rid of the small icon so redrawing the title will
|
|
* create it if necessary.
|
|
*/
|
|
if (TestWF(pwnd, WFSMQUERYDRAGICON)) {
|
|
DestroyWindowSmIcon(pwnd);
|
|
}
|
|
|
|
if (!_GetProp(pwnd, MAKEINTATOM(gpsi->atomIconSmProp),PROPF_INTERNAL)) {
|
|
xxxRedrawTitle(pwnd, DC_ICON);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* SetClassIcon
|
|
*
|
|
* Changes the big/small icon of a class. Called from SetClassWord().
|
|
\***************************************************************************/
|
|
PCURSOR xxxSetClassIcon(
|
|
PWND pwnd,
|
|
PCLS pcls,
|
|
PCURSOR pCursor,
|
|
int gcw)
|
|
{
|
|
PTHREADINFO pti = PtiCurrent();
|
|
PCURSOR pCursorOld;
|
|
HCURSOR hCursorOld;
|
|
TL tlpwndChild;
|
|
BOOL fRedraw;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
/*
|
|
* Save old icon.
|
|
*/
|
|
pCursorOld = ((gcw == GCLP_HICON) ? pcls->spicn : pcls->spicnSm);
|
|
if (pCursorOld != pCursor) {
|
|
fRedraw = TRUE;
|
|
|
|
hCursorOld = PtoH(pCursorOld);
|
|
|
|
/*
|
|
* Set new icon.
|
|
*/
|
|
if (gcw == GCLP_HICON) {
|
|
/*
|
|
* Destroy private cached small icon first.
|
|
*/
|
|
if (pcls->spicnSm && !DestroyClassSmIcon(pcls)) {
|
|
fRedraw = FALSE;
|
|
}
|
|
|
|
Lock(&(pcls->spicn), pCursor);
|
|
|
|
} else {
|
|
|
|
/*
|
|
* We don't allow apps to see the small icons we create from
|
|
* their big icons. They can see their own. Saves memory
|
|
* leak problems and is easier.
|
|
*/
|
|
if (pcls->CSF_flags & CSF_CACHEDSMICON) {
|
|
DestroyClassSmIcon(pcls);
|
|
hCursorOld = NULL;
|
|
}
|
|
|
|
Lock(&(pcls->spicnSm), pCursor);
|
|
}
|
|
|
|
if (pcls->spicn && !pcls->spicnSm) {
|
|
xxxCreateClassSmIcon(pcls);
|
|
}
|
|
|
|
if (fRedraw) {
|
|
if (pcls->cWndReferenceCount > 1) {
|
|
ThreadLock(pti->rpdesk->pDeskInfo->spwnd->spwndChild, &tlpwndChild);
|
|
xxxInternalEnumWindow(pti->rpdesk->pDeskInfo->spwnd->spwndChild,
|
|
xxxSetClassIconEnum,
|
|
(LPARAM)pcls,
|
|
BWL_ENUMLIST);
|
|
ThreadUnlock(&tlpwndChild);
|
|
} else {
|
|
xxxSetClassIconEnum(pwnd, (LPARAM)pcls);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Revalidate the old cursor
|
|
*/
|
|
if (hCursorOld != NULL) {
|
|
pCursorOld = HMRevalidateHandleNoRip(hCursorOld);
|
|
} else {
|
|
pCursorOld = NULL;
|
|
}
|
|
}
|
|
|
|
return pCursorOld;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DestroyClassSmIcon
|
|
*
|
|
* Destroys the small icon of a class if we've created a cached one.
|
|
\***************************************************************************/
|
|
BOOL DestroyClassSmIcon(
|
|
PCLS pcls)
|
|
{
|
|
/*
|
|
* If we don't have a cached icon, then no work.
|
|
*/
|
|
if (pcls->CSF_flags & CSF_CACHEDSMICON) {
|
|
if (pcls->spicnSm) {
|
|
_DestroyCursor(pcls->spicnSm, CURSOR_ALWAYSDESTROY);
|
|
Unlock(&pcls->spicnSm);
|
|
}
|
|
pcls->CSF_flags &= ~CSF_CACHEDSMICON;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxCreateClassSmIcon
|
|
*
|
|
* Creates a cached class small icon from a class big icon.
|
|
\***************************************************************************/
|
|
VOID xxxCreateClassSmIcon(
|
|
PCLS pcls)
|
|
{
|
|
PCURSOR pcur;
|
|
|
|
UserAssert(pcls->cWndReferenceCount > 0);
|
|
UserAssert(pcls->spicn);
|
|
UserAssert(!pcls->spicnSm);
|
|
|
|
pcur = xxxClientCopyImage(PtoH(pcls->spicn),
|
|
pcls->spicn->rt == PTR_TO_ID(RT_ICON) ? IMAGE_ICON : IMAGE_CURSOR,
|
|
SYSMET(CXSMICON),
|
|
SYSMET(CYSMICON),
|
|
LR_DEFAULTCOLOR | LR_COPYFROMRESOURCE);
|
|
|
|
Lock(&pcls->spicnSm, pcur);
|
|
if (pcls->spicnSm) {
|
|
pcls->CSF_flags |= CSF_CACHEDSMICON;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxSetWindowStyle
|
|
*
|
|
* Changes the style bits of a window. Called from SetWindowLong(). This
|
|
* sends two messages, a changing and a changed. Upon receipt of a
|
|
* WM_STYLECHANGING message, a window can muck with the style bits for
|
|
* validation purposes. The WM_STYLECHANGED message is simply after the
|
|
* fact.
|
|
\***************************************************************************/
|
|
LONG xxxSetWindowStyle(
|
|
PWND pwnd,
|
|
int gwl,
|
|
DWORD styleNew)
|
|
{
|
|
STYLESTRUCT sty;
|
|
BOOL fWasChild, fIsChild, fBefore, fAfter;
|
|
|
|
CheckLock(pwnd);
|
|
UserAssert(IsWinEventNotifyDeferredOK());
|
|
|
|
sty.styleOld = ((gwl == GWL_STYLE) ? pwnd->style : pwnd->ExStyle);
|
|
sty.styleNew = styleNew;
|
|
|
|
/*
|
|
* Note that we don't do validation before _and_ after. It is sufficient
|
|
* to do our stuff at the end.
|
|
*/
|
|
|
|
/*
|
|
* We break Quicken 2.0 if we send the messages. That's why we version
|
|
* switch them.
|
|
*
|
|
* Send a WM_STYLECHANGING message to the window, so it can muck with
|
|
* the style bits. Like validate some stuff.
|
|
*/
|
|
if (TestWF(pwnd, WFWIN40COMPAT)) {
|
|
xxxSendMessage(pwnd, WM_STYLECHANGING, gwl, (LPARAM)(LPSTYLESTRUCT)&sty);
|
|
}
|
|
|
|
/*
|
|
* Now do our own validation.
|
|
*/
|
|
if (gwl == GWL_STYLE) {
|
|
BOOL fWasVisWindow;
|
|
|
|
/*
|
|
* If this is an edit control that has ES_PASSWORD set and
|
|
* the caller does not own it and is trying to reset it,
|
|
* fail the call.
|
|
*/
|
|
if (PpiCurrent() != GETPTI(pwnd)->ppi && IS_EDIT(pwnd) &&
|
|
(sty.styleOld & ES_PASSWORD) && !(sty.styleNew & ES_PASSWORD)) {
|
|
RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "Access denied in xxxSetWindowStyle");
|
|
return 0;
|
|
}
|
|
|
|
/* Listbox ownerdraw style check was moved to the client side (client\ntstubs.c) */
|
|
|
|
/*
|
|
* Do proper validation on style bits.
|
|
*/
|
|
if (pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
|
|
sty.styleNew |= WS_CLIPSIBLINGS;
|
|
}
|
|
|
|
/*
|
|
* If the clipping-ness is changing, invalidate the dc cache.
|
|
*/
|
|
if ((sty.styleNew & (WS_CLIPCHILDREN | WS_CLIPSIBLINGS)) !=
|
|
(sty.styleOld & (WS_CLIPCHILDREN | WS_CLIPSIBLINGS))) {
|
|
|
|
/*
|
|
* No need to DeferWinEventNotify() - pwnd is locked
|
|
*/
|
|
zzzInvalidateDCCache(pwnd, IDC_DEFAULT);
|
|
}
|
|
|
|
/*
|
|
* This breaks all Paradox dialogs 1.0-5.0 that have combos. They
|
|
* enumerate all child windows, add on minimized, then sit in a peek
|
|
* loop. After that they enumerate all child windows and remove
|
|
* WS_MINIMIZE--except the code below won't let them.
|
|
*
|
|
* Result is weird painting and an inability to use the dialog any
|
|
* more short of dismissing it
|
|
*
|
|
* Temp fix: Check for child window first.
|
|
*/
|
|
|
|
/*
|
|
* if this window is REALLY minimized (minimized bit is set and caption
|
|
* present bit is removed), then don't allow app to remove the minimize
|
|
* bit -- this fixes FoxBlow's attempt at being the OS -- jeffbog
|
|
*/
|
|
if (!TestWF(pwnd, WFCHILD) &&
|
|
TestWF(pwnd, WFMINIMIZED) &&
|
|
!TestWF(pwnd, WFCPRESENT) &&
|
|
!(sty.styleNew & WS_MINIMIZE)) {
|
|
|
|
sty.styleNew |= WS_MINIMIZE;
|
|
}
|
|
|
|
/*
|
|
* If we're changing the child bit, deal with spmenu appropriately.
|
|
* If we're turning into a child, change spmenu to an id. If we're
|
|
* turning into a top level window, turn spmenu into a menu.
|
|
*/
|
|
fWasChild = TestwndChild(pwnd);
|
|
|
|
pwnd->style = sty.styleNew;
|
|
|
|
fIsChild = TestwndChild(pwnd);
|
|
|
|
/*
|
|
* If we turned into a top level window, change spmenu to NULL.
|
|
* If we turned into a child from a top level window, unlock spmenu.
|
|
*/
|
|
if (fWasChild && !fIsChild) {
|
|
pwnd->spmenu = NULL;
|
|
}
|
|
|
|
if (!fWasChild && fIsChild) {
|
|
ClrWF(pwnd, WFMPRESENT);
|
|
UnlockWndMenu(pwnd, &pwnd->spmenu);
|
|
}
|
|
|
|
/*
|
|
* If the visible, child, or minimized style is changing,
|
|
* then update the cVisWindows count
|
|
*/
|
|
fWasVisWindow = VisWindow(pwnd, sty.styleOld);
|
|
if (fWasVisWindow != VisWindow(pwnd, sty.styleNew)) {
|
|
|
|
if (fWasVisWindow) {
|
|
DecVisWindows(pwnd);
|
|
} else {
|
|
IncVisWindows(pwnd);
|
|
}
|
|
}
|
|
} else {
|
|
/*
|
|
* First, see if the app might be setting bits that it really
|
|
* doesn't know about. If so, replace those bits with the
|
|
* current values.
|
|
*/
|
|
if (GetAppCompatFlags2(VER40) & GACF2_NO50EXSTYLEBITS) {
|
|
sty.styleNew &= WS_EX_VALID40;
|
|
} else {
|
|
/*
|
|
* Don't let aplications set unused extended bits.
|
|
*/
|
|
if (sty.styleNew & ~WS_EX_ALLVALID) {
|
|
RIPMSGF1(RIP_WARNING,
|
|
"Trying to set reserved exStyle bits 0x%x",
|
|
sty.styleNew);
|
|
}
|
|
|
|
sty.styleNew &= WS_EX_ALLVALID;
|
|
}
|
|
|
|
/*
|
|
* Is someone trying to toggle the WS_EX_TOPMOST style bit?
|
|
*/
|
|
if ((sty.styleOld & WS_EX_TOPMOST) != (sty.styleNew & WS_EX_TOPMOST)) {
|
|
RIPMSG0(RIP_WARNING, "Can't change WS_EX_TOPMOST with SetWindowLong");
|
|
|
|
/*
|
|
* BACKWARDS COMPATIBILITY HACK
|
|
* If stuff is getting stored in the high word, then it must be
|
|
* Lotus 123-W sticking a FAR pointer in this field. So don't
|
|
* modify it.
|
|
*/
|
|
if (TestWF(pwnd, WFWIN40COMPAT) || !HIWORD(sty.styleNew)) {
|
|
/*
|
|
* Don't let the bit be flipped.
|
|
*/
|
|
sty.styleNew &= ~WS_EX_TOPMOST;
|
|
sty.styleNew |= (sty.styleOld & WS_EX_TOPMOST);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check pwnd->ExStyle directly since sty.styleOld can now be
|
|
* different from the real state of the window, because of the
|
|
* callbacks in this function from the time sty.styleOld was
|
|
* remembered and up to now. We must call the layering functions
|
|
* based on the real state of the layering bit.
|
|
*/
|
|
fBefore = (pwnd->ExStyle & WS_EX_LAYERED);
|
|
fAfter = (sty.styleNew & WS_EX_LAYERED);
|
|
|
|
if (fBefore && !fAfter) {
|
|
UnsetLayeredWindow(pwnd);
|
|
} else if (!fBefore && fAfter) {
|
|
if (!xxxSetLayeredWindow(pwnd, TRUE)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
fBefore = (pwnd->ExStyle & WS_EX_COMPOSITED);
|
|
fAfter = (sty.styleNew & WS_EX_COMPOSITED);
|
|
|
|
if (!fBefore && fAfter) {
|
|
/*
|
|
* If we are turning WS_EX_COMPOSITED on, none of our parents
|
|
* should already have WS_EX_COMPOSITED turned on. If any do,
|
|
* since we were explicitely trying to turn this style on, fail
|
|
* the call.
|
|
*/
|
|
|
|
if (GetStyleWindow(pwnd->spwndParent, WEFCOMPOSITED) != NULL) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (fBefore && !fAfter) {
|
|
UnsetRedirectedWindow(pwnd, REDIRECT_COMPOSITED);
|
|
} else if (!fBefore && fAfter) {
|
|
if (!SetRedirectedWindow(pwnd, REDIRECT_COMPOSITED)) {
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* We have successfully turned WS_EX_COMPOSITED on for ourself, so
|
|
* need to ensure that none of our child have WS_EX_COMPOSITED also
|
|
* turned on.
|
|
*/
|
|
|
|
xxxTurnOffCompositing(pwnd, TRUE);
|
|
}
|
|
|
|
#ifdef REDIRECTION
|
|
{
|
|
|
|
BOOL fVisrgnChange = FALSE;
|
|
fBefore = (pwnd->ExStyle & WS_EX_EXTREDIRECTED);
|
|
fAfter = (sty.styleNew & WS_EX_EXTREDIRECTED);
|
|
|
|
if (fBefore && !fAfter) {
|
|
UnsetRedirectedWindow(pwnd, REDIRECT_EXTREDIRECTED);
|
|
fVisrgnChange = TRUE;
|
|
} else if (!fBefore && fAfter) {
|
|
if (!SetRedirectedWindow(pwnd, REDIRECT_EXTREDIRECTED)) {
|
|
return 0;
|
|
}
|
|
fVisrgnChange = TRUE;
|
|
}
|
|
|
|
if (fVisrgnChange) {
|
|
BEGINATOMICCHECK();
|
|
zzzInvalidateDCCache(pwnd, IDC_DEFAULT | IDC_NOMOUSE);
|
|
ENDATOMICCHECK();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* The bits we use internally should be preserved
|
|
*/
|
|
pwnd->ExStyle = sty.styleNew | (pwnd->ExStyle & ~WS_EX_ALLVALID);
|
|
if ((sty.styleOld ^ sty.styleNew)
|
|
& (WS_EX_LEFTSCROLLBAR | WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LAYOUTRTL)) {
|
|
xxxRedrawFrame(pwnd);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* See if we still need the 3D edge since the window styles changed.
|
|
*/
|
|
if (NeedsWindowEdge(pwnd->style, pwnd->ExStyle, TestWF(pwnd, WFWIN40COMPAT))) {
|
|
SetWF(pwnd, WEFWINDOWEDGE);
|
|
} else {
|
|
ClrWF(pwnd, WEFWINDOWEDGE);
|
|
}
|
|
|
|
/*
|
|
* Send a WM_STYLECHANGED message.
|
|
*/
|
|
if (TestWF(pwnd, WFWIN40COMPAT)) {
|
|
xxxSendMessage(pwnd, WM_STYLECHANGED, gwl, (LPARAM)(LPSTYLESTRUCT)&sty);
|
|
}
|
|
|
|
return sty.styleOld;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* VisWindow
|
|
*
|
|
* Based on style, determines if this is considered to be "visible" by
|
|
* queue foreground styles.
|
|
\***************************************************************************/
|
|
BOOL VisWindow(
|
|
PWND pwnd,
|
|
DWORD style)
|
|
{
|
|
return (FTopLevel(pwnd) && !(style & WS_MINIMIZE) && (style & WS_VISIBLE));
|
|
}
|