/***************************************************************************\ * * DLGMGR.C - * * Copyright (c) 1985 - 1999, Microsoft Corporation * * Dialog Box Manager Routines * * ??-???-???? mikeke Ported from Win 3.0 sources * 12-Feb-1991 mikeke Added Revalidation code * 19-Feb-1991 JimA Added access checks \***************************************************************************/ #include "precomp.h" #pragma hdrstop #define UNICODE_MINUS_SIGN 0x2212 LOOKASIDE DialogLookaside; BOOL ValidateCallback(HANDLE h); #define IsInForegroundQueue(hwnd) \ (NtUserQueryWindow(hwnd, WindowIsForegroundThread) != NULL) #define IsCurrentThreadForeground() \ ((BOOL)NtUserGetThreadState(UserThreadStateIsForeground)) /***************************************************************************\ * * GetParentDialog() * * Gets top level window, not a control parent. If not a dialog, then use * "highest" control parent guy. * * BOGUS * Need a way to mark a window as a dialog. If it ever comes into * DefDlgProc(), set an internal flag. Will be used by thunking and * CallDlgProc() optimizations also! * \***************************************************************************/ PWND GetParentDialog(PWND pwndDialog) { PWND pwndParent; pwndParent = pwndDialog; // // Walk up the parent chain. We're looking for the top-most dialog // window. Most cases, the window is a top level one. But in case of // backup app, the window will be a child of some other window. // for (; pwndDialog; pwndDialog = REBASEPWND(pwndDialog, spwndParent)) { if (TestWF(pwndDialog, WFDIALOGWINDOW)) { // // For old guys: If not DS_RECURSE, then stop here. // that way old apps which try to do the nested dialog // stuff in their old limited way don't die. // if (TestWF(pwndDialog, WEFCONTROLPARENT)) pwndParent = pwndDialog; else if (!TestWF(pwndDialog, DFCONTROL)) break; } if (!TestWF(pwndDialog, WFCHILD)) break; } return(pwndParent); } /***************************************************************************\ * xxxSaveDlgFocus * * History: * 02-18-92 JimA Ported from Win31 sources \***************************************************************************/ BOOL xxxSaveDlgFocus( PWND pwnd) { HWND hwndFocus = GetFocus(); CheckLock(pwnd); if (hwndFocus != NULL && IsChild(HWq(pwnd), hwndFocus) && PDLG(pwnd)->hwndFocusSave == NULL) { PDLG(pwnd)->hwndFocusSave = hwndFocus; xxxRemoveDefaultButton(pwnd, ValidateHwnd(hwndFocus)); return TRUE; } return FALSE; } /***************************************************************************\ * xxxRestoreDlgFocus * * History: * 02-18-92 JimA Ported from Win31 sources * 01-01-2001 Mohamed Need to re-validate the window before cleanup. \***************************************************************************/ // LATER // 21-Mar-1992 mikeke // does pwndFocusSave need to be unlocked when the dialog is destroyed? BOOL xxxRestoreDlgFocus( PWND pwnd) { HWND hwndFocus; HWND hwndFocusSave; BOOL fRestored = FALSE; CheckLock(pwnd); if (PDLG(pwnd)->hwndFocusSave && !TestWF(pwnd, WFMINIMIZED)) { hwndFocus = GetFocus(); hwndFocusSave = KHWND_TO_HWND(PDLG(pwnd)->hwndFocusSave); if (IsWindow(hwndFocusSave)) { xxxCheckDefPushButton(pwnd, hwndFocus, hwndFocusSave); fRestored = (NtUserSetFocus(hwndFocusSave) != NULL); } // // After calling SetFocus(), we need to re-validate // the window. PDLG(pwnd) might be NULL. // if (ValidateDialogPwnd(pwnd)) { PDLG(pwnd)->hwndFocusSave = NULL; } } return fRestored; } /***************************************************************************\ * DlgSetFocus * * History: \***************************************************************************/ void DlgSetFocus( HWND hwnd) { if (((UINT)SendMessage(hwnd, WM_GETDLGCODE, 0, 0)) & DLGC_HASSETSEL) { SendMessage(hwnd, EM_SETSEL, 0, MAXLONG); } NtUserSetFocus(hwnd); } FUNCLOG1(LOG_GENERAL, int, DUMMYCALLINGTYPE, GetDlgCtrlID, HWND, hwnd) int GetDlgCtrlID( HWND hwnd) { PWND pwnd; pwnd = ValidateHwnd(hwnd); if (pwnd == NULL) return 0; return PtrToLong(pwnd->spmenu); } /***************************************************************************\ * ValidateDialogPwnd * * Under Win3, DLGWINDOWEXTRA is 30 bytes. We cannot change that for 16 bit * compatibility reasons. Problem is there is no way to tell if a given * 16 bit window depends on byte count. If there was, this would be easy. * The only way to tell is when a window is about to be used as a dialog * window. This window may be of the class DIALOGCLASS, but again it may * not!! So we keep dialog window words at 30 bytes, and allocate another * structure for the real dialog structure fields. Problem is that this * structure has to be created lazily! And that's what we're doing here. * * 05-21-91 ScottLu Created. \***************************************************************************/ BOOL ValidateDialogPwnd( PWND pwnd) { static BOOL sfInit = TRUE; PDLG pdlg; /* * This bit is set if we've already run through this initialization and * have identified this window as a dialog window (able to withstand * peeks into window words at random moments in time). */ if (TestWF(pwnd, WFDIALOGWINDOW)) return TRUE; if (pwnd->cbwndExtra < DLGWINDOWEXTRA) { RIPERR0(ERROR_WINDOW_NOT_DIALOG, RIP_VERBOSE, ""); return FALSE; } /* * See if the pdlg was destroyed and this is a rogue message to be ignored */ if (pwnd->fnid & FNID_STATUS_BITS) { return FALSE; } /* * If the lookaside buffer has not been initialized, do it now. */ if (sfInit) { if (!NT_SUCCESS(InitLookaside(&DialogLookaside, sizeof(DLG), 2))) { return FALSE; } sfInit = FALSE; } if ((pdlg = (PDLG)AllocLookasideEntry(&DialogLookaside)) == NULL) { return FALSE; } NtUserCallHwndParam(HWq(pwnd), (ULONG_PTR)pdlg, SFI_SETDIALOGPOINTER); return TRUE; } /***************************************************************************\ * CvtDec * * LATER!!! convert to itoa? * * History: \***************************************************************************/ void CvtDec( int u, LPWSTR *lplpch) { if (u >= 10) { CvtDec(u / 10, lplpch); u %= 10; } *(*lplpch)++ = (WCHAR)(u + '0'); } /***************************************************************************\ * SetDlgItemInt * * History: \***************************************************************************/ FUNCLOG4(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, SetDlgItemInt, HWND, hwnd, int, item, UINT, u, BOOL, fSigned) BOOL SetDlgItemInt( HWND hwnd, int item, UINT u, BOOL fSigned) { LPWSTR lpch; WCHAR rgch[16]; lpch = rgch; if (fSigned) { if ((int)u < 0) { *lpch++ = TEXT('-'); u = (UINT)(-(int)u); } } else { if (u & 0x80000000) { CvtDec(u / 10, (LPWSTR FAR *)&lpch); u = u % 10; } } CvtDec(u, (LPWSTR FAR *)&lpch); *lpch = 0; return SetDlgItemTextW(hwnd, item, rgch); } /***************************************************************************\ * CheckDlgButton * * History: \***************************************************************************/ FUNCLOG3(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, CheckDlgButton, HWND, hwnd, int, id, UINT, cmdCheck) BOOL CheckDlgButton( HWND hwnd, int id, UINT cmdCheck) { if ((hwnd = GetDlgItem(hwnd, id)) == NULL) { return FALSE; } SendMessage(hwnd, BM_SETCHECK, cmdCheck, 0); return TRUE; } /***************************************************************************\ * GetDlgItemInt * * History: \***************************************************************************/ UINT GetDlgItemInt( HWND hwnd, int item, BOOL FAR *lpfValOK, BOOL fSigned) { int i, digit, ch; int maxTens, maxUnits; BOOL fOk, fNeg; LPWSTR lpch; WCHAR rgch[48]; WCHAR rgchDigits[48]; fOk = FALSE; if (lpfValOK != NULL) *lpfValOK = FALSE; if (!GetDlgItemTextW(hwnd, item, rgch, sizeof(rgch)/sizeof(WCHAR) - 1)) return 0; lpch = rgch; /* * Skip leading white space. */ while (*lpch == TEXT(' ')) lpch++; fNeg = FALSE; while (fSigned && ((*lpch == L'-') || (*lpch == UNICODE_MINUS_SIGN))) { lpch++; fNeg ^= TRUE; } if (fSigned) { maxTens = INT_MAX/10; maxUnits = INT_MAX%10; } else { maxTens = UINT_MAX/10; maxUnits = UINT_MAX%10; } /* * Convert all decimal digits to ASCII Unicode digits 0x0030 - 0x0039 */ FoldStringW(MAP_FOLDDIGITS, lpch, -1, rgchDigits, sizeof(rgchDigits)/sizeof(rgchDigits[0])); lpch = rgchDigits; i = 0; while (ch = *lpch++) { digit = ch - TEXT('0'); if (digit < 0 || digit > 9) { break; } if ((UINT)i >= (UINT)maxTens) { /* * We need to special case INT_MIN as the i = -i * would damage it */ if (i == maxTens) { if (digit == maxUnits+1 && fNeg && (*lpch) == 0) { i = INT_MIN; goto HaveResult; } else if (digit > maxUnits) { return 0; } } else { return 0; } } fOk = TRUE; i = ((UINT)i * 10) + digit; } if (fNeg) i = -i; HaveResult: if (lpfValOK != NULL) *lpfValOK = ((ch == 0) && fOk); return (UINT)i; } /***************************************************************************\ * CheckRadioButton * * History: \***************************************************************************/ FUNCLOG4(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, CheckRadioButton, HWND, hwnd, int, idFirst, int, idLast, int, id) BOOL CheckRadioButton( HWND hwnd, int idFirst, int idLast, int id) { PWND pwnd, pwndDialog; BOOL fCheckOn; pwndDialog = ValidateHwnd(hwnd); if (pwndDialog == NULL) return 0; for (pwnd = REBASE(pwndDialog, spwndChild); pwnd; pwnd = REBASE(pwnd, spwndNext)) { if ((PtrToLong(pwnd->spmenu) >= idFirst) && (PtrToLong(pwnd->spmenu) <= idLast)) { fCheckOn = (PtrToLong(pwnd->spmenu) == id); SendMessage(PtoHq(pwnd), BM_SETCHECK, fCheckOn, 0L); } } return TRUE; } /***************************************************************************\ * IsDlgButtonChecked * * History: \***************************************************************************/ FUNCLOG2(LOG_GENERAL, UINT, DUMMYCALLINGTYPE, IsDlgButtonChecked, HWND, hwnd, int, id) UINT IsDlgButtonChecked( HWND hwnd, int id) { if ((hwnd = GetDlgItem(hwnd, id)) != NULL) { return (UINT)SendMessage(hwnd, BM_GETCHECK, 0, 0); } return FALSE; } /***************************************************************************\ * DefDlgProc * * History: \***************************************************************************/ LRESULT DefDlgProcWorker( PWND pwnd, UINT message, WPARAM wParam, LPARAM lParam, DWORD fAnsi) { HWND hwnd = HWq(pwnd); TL tlpwndT1, tlpwndT2, tlpwndT3, tlpwndTop; PWND pwndT; PWND pwndT1, pwndT2, pwndT3, pwndTop; HWND hwndT1; LRESULT result; BOOL fSetBit; DLGPROC pfn; CheckLock(pwnd); /* * use the Win 3.1 documented size */ VALIDATECLASSANDSIZE(pwnd, FNID_DIALOG); /* * Must do special validation here to make sure pwnd is a dialog window. */ if (!ValidateDialogPwnd(pwnd)) return 0; if (((PDIALOG)pwnd)->resultWP != 0) NtUserSetWindowLongPtr(hwnd, DWLP_MSGRESULT, 0, FALSE); result = 0; // no dialog proc if (message == WM_FINALDESTROY) { goto DoCleanup; } if ((pfn = PDLG(pwnd)->lpfnDlg) != NULL) { /* Bug 234292 - joejo * Since the called window/dialog proc may have a different calling * convention, we must wrap the call and, check esp and replace with * a good esp when the call returns. This is what UserCallWinProc* does. */ if (UserCallDlgProcCheckWow(pwnd->pActCtx, pfn, hwnd, message, wParam, lParam, &(pwnd->state), &result)) { return result; } /* * Get out if the window was destroyed in the dialog proc. */ if ((RevalidateHwnd(hwnd)==NULL) || (pwnd->fnid & FNID_STATUS_BITS)) return result; } /* * SPECIAL CASED ... and DOCUMENTED that way !!! * These 6, and ONLY these 6, should be hacked in this fashion. * Anybody who needs the REAL return value to a message should * use SetDlgMsgResult in WINDOWSX.H */ switch (message) { case WM_COMPAREITEM: case WM_VKEYTOITEM: case WM_CHARTOITEM: case WM_INITDIALOG: case WM_QUERYDRAGICON: return ((LRESULT)(DWORD)result); case WM_CTLCOLOR: case WM_CTLCOLORMSGBOX: case WM_CTLCOLOREDIT: case WM_CTLCOLORLISTBOX: case WM_CTLCOLORBTN: case WM_CTLCOLORDLG: case WM_CTLCOLORSCROLLBAR: case WM_CTLCOLORSTATIC: // QuarkXPress doesn't like finding the WM_CTLCOLOR result in // resultWP -- we should never be setting resultWP -- that's meant // as a pass-thru return value -- so let's go back to doing it the // old way -- Win95B B#21269 -- 03/13/95 -- tracysh (cr: jeffbog) if (result) return result; break; } if (!result) { /* * Save the result value in case our private memory is freed * before we return */ // result = PDLG(pwnd)->resultWP; switch (message) { case WM_CTLCOLOR: case WM_CTLCOLORMSGBOX: case WM_CTLCOLOREDIT: case WM_CTLCOLORLISTBOX: case WM_CTLCOLORBTN: case WM_CTLCOLORDLG: case WM_CTLCOLORSCROLLBAR: case WM_CTLCOLORSTATIC: { // // HACK OF DEATH: // To get 3D colors for non 4.0 apps who use 3DLOOK, // we temporarily add on the 4.0 compat bit, pass this // down to DWP, and clear it. // // Use "result" var for bool saying we have to add/clear 4.0 // compat bit. fSetBit = (TestWF(pwnd, DF3DLOOK)!= 0) && (TestWF(pwnd, WFWIN40COMPAT) == 0); if (fSetBit) SetWindowState(pwnd, WFWIN40COMPAT); result = DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi); if (fSetBit) ClearWindowState(pwnd, WFWIN40COMPAT); return result; } case WM_ERASEBKGND: FillWindow(hwnd, hwnd, (HDC)wParam, (HBRUSH)CTLCOLOR_DLG); return TRUE; case WM_SHOWWINDOW: /* * If hiding the window, save the focus. If showing the window * by means of a SW_* command and the fEnd bit is set, do not * pass to DWP so it won't get shown. */ if (GetParentDialog(pwnd) == pwnd) { if (!wParam) { xxxSaveDlgFocus(pwnd); } else { if (LOWORD(lParam) != 0 && PDLG(pwnd)->fEnd) break; /* * Snap the cursor to the center of the default button. * Only do this if the current thread is in the foreground. * The _ShowCursor() code is added to work around a * problem with hardware cursors. If change is done * in the same refresh cycle, the display of the cursor * would not reflect the new position. */ if (TEST_PUSIF(PUSIF_SNAPTO) && IsInForegroundQueue(hwnd)) { hwndT1 = GetDlgItem(hwnd, (int)PDLG(pwnd)->result); if (hwndT1) { RECT rc; NtUserShowCursor(FALSE); GetWindowRect(hwndT1, &rc); NtUserSetCursorPos(rc.left + ((rc.right - rc.left)/2), rc.top + ((rc.bottom - rc.top)/2)); NtUserShowCursor(TRUE); } } } } goto CallDWP; case WM_SYSCOMMAND: if (GetParentDialog(pwnd) == pwnd) { /* * If hiding the window, save the focus. If showing the window * by means of a SW_* command and the fEnd bit is set, do not * pass to DWP so it won't get shown. */ if ((int)wParam == SC_MINIMIZE) xxxSaveDlgFocus(pwnd); } goto CallDWP; case WM_ACTIVATE: pwndT1 = GetParentDialog(pwnd); if ( pwndT1 != pwnd) { /* * This random bit is used during key processing - bit * 08000000 of WM_CHAR messages is set if a dialog is currently * active. */ NtUserSetThreadState(wParam ? QF_DIALOGACTIVE : 0, QF_DIALOGACTIVE); } ThreadLock(pwndT1, &tlpwndT1); if (wParam != 0) xxxRestoreDlgFocus(pwndT1); else xxxSaveDlgFocus(pwndT1); ThreadUnlock(&tlpwndT1); break; case WM_SETFOCUS: pwndT1 = GetParentDialog(pwnd); if (!PDLG(pwndT1)->fEnd && !xxxRestoreDlgFocus(pwndT1)) { pwndT = _GetNextDlgTabItem(pwndT1, NULL, FALSE); DlgSetFocus(HW(pwndT)); } break; case WM_CLOSE: /* * Make sure cancel button is not disabled before sending the * IDCANCEL. Note that we need to do this as a message instead * of directly calling the dlg proc so that any dialog box * filters get this. */ pwndT1 = _GetDlgItem(pwnd, IDCANCEL); if (pwndT1 && TestWF(pwndT1, WFDISABLED)) NtUserMessageBeep(0); else PostMessage(hwnd, WM_COMMAND, MAKELONG(IDCANCEL, BN_CLICKED), (LPARAM)HW(pwndT1)); break; case WM_NCDESTROY: case WM_FINALDESTROY: DoCleanup: NtUserSetThreadState(0, QF_DIALOGACTIVE); if (!(pwnd->style & DS_LOCALEDIT)) { if (PDLG(pwnd)->hData) { ReleaseEditDS(KHANDLE_TO_HANDLE(PDLG(pwnd)->hData)); PDLG(pwnd)->hData = NULL; } } /* * Delete the user defined font if any */ if (PDLG(pwnd)->hUserFont) { DeleteObject(KHFONT_TO_HFONT(PDLG(pwnd)->hUserFont)); PDLG(pwnd)->hUserFont = NULL; } /* * Free the dialog memory and mark this as a non-dialog window */ FreeLookasideEntry(&DialogLookaside, KPVOID_TO_PVOID(PDLG(pwnd))); NtUserCallHwndParam(hwnd, 0, SFI_SETDIALOGPOINTER); break; case DM_REPOSITION: { RECT rc; PMONITOR pMonitor; // DAT recorder APP sends it's own private message 0x402 // through and we mistake it to be DM_REPOSITION. To avoid // this confusion, we do the following check. // Fix for Bug#25747 -- 9/29/94 -- if (!TestWF(pwnd, WEFCONTROLPARENT) || (GETFNID(pwnd) != FNID_DESKTOP && GETFNID(REBASEPWND(pwnd, spwndParent)) != FNID_DESKTOP)) { goto CallDWP; } CopyRect(&rc, KPRECT_TO_PRECT(&pwnd->rcWindow)); pMonitor = _MonitorFromRect(&rc, MONITOR_DEFAULTTOPRIMARY); RepositionRect(pMonitor, &rc, pwnd->style, pwnd->ExStyle); NtUserSetWindowPos(hwnd, HWND_TOP, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); } break; case DM_SETDEFID: pwndT1 = GetParentDialog(pwnd); ThreadLock(pwndT1, &tlpwndT1); if (!(PDLG(pwndT1)->fEnd)) { pwndT2 = NULL; if (PDLG(pwndT1)->result != 0) pwndT2 = _FindDlgItem(pwndT1, (int)PDLG(pwndT1)->result); pwndT3 = NULL; if (wParam != 0) { pwndT3 = _GetDlgItem(pwnd, (UINT)wParam); } ThreadLock(pwndT2, &tlpwndT2); ThreadLock(pwndT3, &tlpwndT3); xxxCheckDefPushButton(pwndT1, HW(pwndT2), HW(pwndT3)); ThreadUnlock(&tlpwndT3); ThreadUnlock(&tlpwndT2); PDLG(pwndT1)->result = (UINT)wParam; // if (PDLG(pwnd)->spwndFocusSave) { // Lock(&(PDLG(pwnd)->spwndFocusSave), pwndT2); // } NotifyWinEvent(EVENT_OBJECT_DEFACTIONCHANGE, HW(pwndT1), OBJID_CLIENT, INDEXID_CONTAINER); } ThreadUnlock(&tlpwndT1); return TRUE; case DM_GETDEFID: pwndT1 = GetParentDialog(pwnd); if (!PDLG(pwndT1)->fEnd && PDLG(pwndT1)->result) return(MAKELONG(PDLG(pwndT1)->result, DC_HASDEFID)); else return 0; break; /* * This message was added so that user defined controls that want * tab keys can pass the tab off to the next/previous control in the * dialog box. Without this, all they could do was set the focus * which didn't do the default button stuff. */ case WM_NEXTDLGCTL: pwndTop = GetParentDialog(pwnd); ThreadLock(pwndTop, &tlpwndTop); hwndT1 = GetFocus(); pwndT2 = ValidateHwndNoRip(hwndT1); if (LOWORD(lParam)) { if (pwndT2 == NULL) pwndT2 = pwndTop; /* * wParam contains the pwnd of the ctl to set focus to. */ if ((pwndT1 = ValidateHwnd((HWND)wParam)) == NULL) { ThreadUnlock(&tlpwndTop); return TRUE; } } else { if (pwndT2 == NULL) { /* * Set focus to the first tab item. */ pwndT1 = _GetNextDlgTabItem(pwndTop, NULL, FALSE); pwndT2 = pwndTop; } else { /* * If window with focus not a dlg ctl, ignore message. */ if (!_IsChild(pwndTop, pwndT2)) { ThreadUnlock(&tlpwndTop); return TRUE; } /* * wParam = TRUE for previous, FALSE for next */ pwndT1 = _GetNextDlgTabItem(pwndTop, pwndT2, (wParam != 0)); /* * If there is no next item, ignore the message. */ if (pwndT1 == NULL) { ThreadUnlock(&tlpwndTop); return TRUE; } } } ThreadLock(pwndT1, &tlpwndT1); ThreadLock(pwndT2, &tlpwndT2); DlgSetFocus(HW(pwndT1)); xxxCheckDefPushButton(pwndTop, HW(pwndT2), HW(pwndT1)); ThreadUnlock(&tlpwndT2); ThreadUnlock(&tlpwndT1); ThreadUnlock(&tlpwndTop); return TRUE; case WM_ENTERMENULOOP: /* * We need to pop up the combo box window if the user brings * down a menu. * * ... FALL THROUGH... */ case WM_LBUTTONDOWN: case WM_NCLBUTTONDOWN: hwndT1 = GetFocus(); if (hwndT1 != NULL) { pwndT1 = ValidateHwndNoRip(hwndT1); if (GETFNID(pwndT1) == FNID_COMBOBOX) { /* * If user clicks anywhere in dialog box and a combo box (or * the editcontrol of a combo box) has the focus, then hide * it's listbox. */ ThreadLockAlways(pwndT1, &tlpwndT1); SendMessage(HWq(pwndT1), CB_SHOWDROPDOWN, FALSE, 0); ThreadUnlock(&tlpwndT1); } else { PWND pwndParent; /* * It's a subclassed combo box. See if the listbox and edit * boxes exist (this is a very cheezy evaluation - what if * these controls are subclassed too? NOTE: Not checking * for EditWndProc: it's a client proc address. */ pwndParent = REBASEPWND(pwndT1, spwndParent); if (GETFNID(pwndParent) == FNID_COMBOBOX) { pwndT1 = pwndParent; ThreadLock(pwndT1, &tlpwndT1); SendMessage(HWq(pwndT1), CB_SHOWDROPDOWN, FALSE, 0); ThreadUnlock(&tlpwndT1); } } } /* * Always send the message off to DefWndProc */ goto CallDWP; case WM_GETFONT: return (LRESULT)PDLG(pwnd)->hUserFont; case WM_VKEYTOITEM: case WM_COMPAREITEM: case WM_CHARTOITEM: case WM_INITDIALOG: /* * We need to return the 0 the app may have returned for these * items instead of calling defwindow proc. */ return result; case WM_NOTIFYFORMAT: if (lParam == NF_QUERY) return((PDLG(pwnd)->flags & DLGF_ANSI ) ? NFR_ANSI : NFR_UNICODE); return result; case WM_INPUTLANGCHANGEREQUEST: if (IS_IME_ENABLED()) { /* * #115190 * For dialogbox itself, buttons/static controls on top of * dialogbox, we'll simply discard this message. B#3843-win95c */ break; } if (PDLG(pwnd)->lpfnDlg == MB_DlgProc) { break; } goto CallDWP; default: CallDWP: return DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi); } } else if ((message == WM_SHOWWINDOW) && result) { /* * For a visible-case we want to snap the cursor regardless of * what was returned from the dialog-handler on the client. If * we're going visible, snap the cursor to the dialog-button. */ if (GetParentDialog(pwnd) == pwnd) { if (wParam && ((LOWORD(lParam) == 0) || !PDLG(pwnd)->fEnd)) { /* * Snap the cursor to the center of the default button. * Only do this if the current thread is in the foreground. * The _ShowCursor() code is added to work around a * problem with hardware cursors. If change is done * in the same refresh cycle, the display of the cursor * would not reflect the new position. */ if (TEST_PUSIF(PUSIF_SNAPTO) && IsInForegroundQueue(hwnd)) { hwndT1 = GetDlgItem(hwnd, (int)PDLG(pwnd)->result); if (hwndT1) { RECT rc; NtUserShowCursor(FALSE); GetWindowRect(hwndT1, &rc); NtUserSetCursorPos(rc.left + ((rc.right - rc.left)/2), rc.top + ((rc.bottom - rc.top)/2)); NtUserShowCursor(TRUE); } } } } } /* * If this is still marked as a dialog window then return the real * result. Otherwise, we've already processed the WM_NCDESTROY message * and freed our private memory so return the stored value. */ if (TestWF(pwnd, WFDIALOGWINDOW)) return KERNEL_LRESULT_TO_LRESULT(((PDIALOG)pwnd)->resultWP); else return result; } /***************************************************************************\ * DefDlgProc * * Translates the message, calls DefDlgProc on server side. DefDlgProc * is the default WindowProc for dialogs (NOT the dialog's dialog proc) * * 04-11-91 ScottLu Created. \***************************************************************************/ FUNCLOG4(LOG_GENERAL, LRESULT, WINAPI, DefDlgProcW, HWND, hwnd, UINT, message, WPARAM, wParam, LPARAM, lParam) LRESULT WINAPI DefDlgProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { PWND pwnd; if ((pwnd = ValidateHwnd(hwnd)) == NULL) { return (0L); } return DefDlgProcWorker(pwnd, message, wParam, lParam, FALSE); } FUNCLOG4(LOG_GENERAL, LRESULT, WINAPI, DefDlgProcA, HWND, hwnd, UINT, message, WPARAM, wParam, LPARAM, lParam) LRESULT WINAPI DefDlgProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { PWND pwnd; if ((pwnd = ValidateHwnd(hwnd)) == NULL) { return (0L); } return DefDlgProcWorker(pwnd, message, wParam, lParam, TRUE); } /***************************************************************************\ * DialogBox2 * * History: \***************************************************************************/ INT_PTR DialogBox2( HWND hwnd, HWND hwndOwner, BOOL fDisabled, BOOL fOwnerIsActiveWindow) { MSG msg; INT_PTR result; BOOL fShown; BOOL fWantIdleMsgs; BOOL fSentIdleMessage = FALSE; HWND hwndCapture; PWND pwnd; if (hwnd) { pwnd = ValidateHwnd(hwnd); } else { pwnd = NULL; } CheckLock(pwnd); if (pwnd == NULL) { if ((hwndOwner != NULL) && !fDisabled && IsWindow(hwndOwner)) { NtUserEnableWindow(hwndOwner, TRUE); if (fOwnerIsActiveWindow) { /* * The dialog box failed but we disabled the owner in * xxxDialogBoxIndirectParam and if it had the focus, the * focus was set to NULL. Now, when we enable the window, it * doesn't get the focus back if it had it previously so we * need to correct this. */ NtUserSetFocus(hwndOwner); } } return -1; } hwndCapture = GetCapture(); if (hwndCapture != NULL) { SendMessage(hwndCapture, WM_CANCELMODE, 0, 0); } /* * Set the 'parent disabled' flag for EndDialog(). * convert BOOL to definite bit 0 or 1 */ PDLG(pwnd)->fDisabled = !!fDisabled; fShown = TestWF(pwnd, WFVISIBLE); /* * Should the WM_ENTERIDLE messages be sent? */ fWantIdleMsgs = !(pwnd->style & DS_NOIDLEMSG); if ((SYSMET(SLOWMACHINE) & 1) && !fShown && !PDLG(pwnd)->fEnd) goto ShowIt; while (PDLG(pwnd) && (!PDLG(pwnd)->fEnd)) { if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { ShowIt: if (!fShown) { fShown = TRUE; #ifdef SYSMODALWINDOWS if (pwnd == gspwndSysModal) { /* * Make this a topmost window */ NtUserSetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOACTIVATE); } #endif NtUserShowWindow(hwnd, SHOW_OPENWINDOW); UpdateWindow(hwnd); NotifyWinEvent(EVENT_SYSTEM_DIALOGSTART, hwnd, OBJID_WINDOW, INDEXID_CONTAINER); } else { /* * Make sure window still exists */ if (hwndOwner && !IsWindow(hwndOwner)) hwndOwner = NULL; if (hwndOwner && fWantIdleMsgs && !fSentIdleMessage) { fSentIdleMessage = TRUE; SendMessage(hwndOwner, WM_ENTERIDLE, MSGF_DIALOGBOX, (LPARAM)hwnd); } else { if ((RevalidateHwnd(hwnd)==NULL) || (pwnd->fnid & FNID_STATUS_BITS)) break; NtUserWaitMessage(); } } } else { /* * We got a real message. Reset fSentIdleMessage so that we send * one next time things are calm. */ fSentIdleMessage = FALSE; if (msg.message == WM_QUIT) { PostQuitMessage((int)msg.wParam); break; } /* * If pwnd is a message box, allow Ctrl-C and Ctrl-Ins * to copy its content to the clipboard. * Fall through in case hooking apps look for these keys. */ if (TestWF(pwnd, WFMSGBOX)) { if ( (msg.message == WM_CHAR && LOBYTE(msg.wParam) == 3) || (msg.message == WM_KEYDOWN && LOBYTE(msg.wParam) == VK_INSERT && GetKeyState(VK_CONTROL) < 0)) { /* * Send the WM_COPY message and let the original message fall through * as some apps might want it */ SendMessage(hwnd, WM_COPY, 0, 0); } } /* * Moved the msg filter hook call to IsDialogMessage to allow * messages to be hooked for both modal and modeless dialog * boxes. */ if (!IsDialogMessage(hwnd, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } /* * If we get a timer message, go ahead and show the window. * We may continuously get timer msgs if there are zillions of * apps running. * * If we get a syskeydown message, show the dialog box because * the user may be bringing down a menu and we want the dialog * box to become visible. */ if (!fShown && (msg.message == WM_TIMER || msg.message == WM_SYSTIMER || msg.message == WM_SYSKEYDOWN)) goto ShowIt; } if (!RevalidateHwnd(hwnd)) { /* * Bogus case - we've already been destroyed somehow (by app, * GP, etc.) */ RIPMSG0(RIP_WARNING, "Dialog should be dismissed with EndDialog, not DestroyWindow"); break; } } NotifyWinEvent(EVENT_SYSTEM_DIALOGEND, hwnd, OBJID_WINDOW, INDEXID_CONTAINER); /* * Make sure the window still exists */ if (!RevalidateHwnd(hwnd)) { return 0; } if (PDLG(pwnd)) result = KERNEL_INT_PTR_TO_INT_PTR(PDLG(pwnd)->result); else result = 0; NtUserDestroyWindow(hwnd); /* * If the owner window belongs to another thread, the reactivation * of the owner may have failed within DestroyWindow(). Therefore, * if the current thread is in the foreground and the owner is not * in the foreground we can safely set the foreground back * to the owner. */ if (hwndOwner != NULL) { if (IsCurrentThreadForeground() && !IsInForegroundQueue(hwndOwner)) { NtUserSetForegroundWindow(hwndOwner); } } return result; } /***************************************************************************\ * InternalDialogBox * * Server portion of DialogBoxIndirectParam. * * 04-05-91 ScottLu Created. \***************************************************************************/ INT_PTR InternalDialogBox( HANDLE hModule, LPDLGTEMPLATE lpdt, HWND hwndOwner, DLGPROC pfnDialog, LPARAM lParam, UINT fSCDLGFlags) { INT_PTR i; BOOL fDisabled = FALSE; HWND hwnd; PWND pwndOwner; BOOL fOwnerIsActiveWindow = FALSE; TL tlpwndOwner; BOOL fUnlockOwner; UserAssert(!(fSCDLGFlags & ~(SCDLG_CLIENT|SCDLG_ANSI|SCDLG_16BIT))); // These are the only valid flags /* * If hwndOwner == HWNDESKTOP, change it to NULL. This way the desktop * (and all its children) won't be disabled if the dialog is modal. */ if (hwndOwner && SAMEWOWHANDLE(hwndOwner, GetDesktopWindow())) hwndOwner = NULL; /* * We return 0 if the ValidateHwnd fails in order to match Win 3.1 * validation layer which always returns 0 for invalid hwnds even * if the function is spec'ed to return -1. Autocad setup bug #3615 */ if (hwndOwner) { if ((pwndOwner = ValidateHwnd(hwndOwner)) == NULL) { return (0L); } } else { pwndOwner = NULL; } CheckLock(pwndOwner); fUnlockOwner = FALSE; if (pwndOwner != NULL) { /* The following fixes an AV in Corel Photo-Paint 6.0. It passes a * 16-bit HWND in, and croaks at some point when it gets 16-bit hwnds * back in send messages. FritzS -- fixing bug 12531 */ hwndOwner = PtoHq(pwndOwner); /* * Make sure the owner is a top level window. */ if (TestwndChild(pwndOwner)) { pwndOwner = GetTopLevelWindow(pwndOwner); hwndOwner = HWq(pwndOwner); ThreadLock(pwndOwner, &tlpwndOwner); fUnlockOwner = TRUE; } /* * Remember if window was originally disabled (so we can set * the correct state when the dialog goes away. */ fDisabled = TestWF(pwndOwner, WFDISABLED); fOwnerIsActiveWindow = (SAMEWOWHANDLE(hwndOwner, GetActiveWindow())); /* * Disable the window. */ NtUserEnableWindow(hwndOwner, FALSE); } /* * Don't show cursors on a mouseless system. Put up an hour glass while * the dialog comes up. */ if (SYSMET(MOUSEPRESENT)) { NtUserSetCursor(LoadCursor(NULL, IDC_WAIT)); } /* * Creates the dialog. Frees the menu if this routine fails. */ hwnd = InternalCreateDialog(hModule, lpdt, 0, hwndOwner, pfnDialog, lParam, fSCDLGFlags); if (hwnd == NULL) { /* * The dialog creation failed. Re-enable the window, destroy the * menu, ie., fail gracefully. */ if (!fDisabled && hwndOwner != NULL) NtUserEnableWindow(hwndOwner, TRUE); if (fUnlockOwner) ThreadUnlock(&tlpwndOwner); return -1; } i = DialogBox2(hwnd, hwndOwner, fDisabled, fOwnerIsActiveWindow); if (fUnlockOwner) ThreadUnlock(&tlpwndOwner); return i; } /***************************************************************************\ ** ** RepositionRect() ** ** Used to ensure that toplevel dialogs are still visible within the ** desktop area after they've resized. ** \***************************************************************************/ void RepositionRect( PMONITOR pMonitor, LPRECT lprc, DWORD dwStyle, DWORD dwExStyle) { LPRECT lprcClip; int y; UserAssert(lprc); UserAssert(pMonitor); if (dwStyle & WS_CHILD) { if (dwExStyle & WS_EX_CONTROLPARENT) return; /* * Old style 3.1 child dialogs--do this nonsense anyway. Keeps * FedEx happy. */ pMonitor = GetPrimaryMonitor(); lprcClip = KPRECT_TO_PRECT(&pMonitor->rcMonitor); } else if (dwExStyle & WS_EX_TOOLWINDOW) { lprcClip = KPRECT_TO_PRECT(&pMonitor->rcMonitor); } else { lprcClip = KPRECT_TO_PRECT(&pMonitor->rcWork); } UserAssert(lprc); y = lprcClip->bottom - (SYSMET(CYEDGE) * 2 + SYSMET(CYKANJIWINDOW)); if (lprc->bottom > y) { OffsetRect(lprc, 0, y - lprc->bottom); } if (lprc->top < lprcClip->top) { OffsetRect(lprc, 0, lprcClip->top - lprc->top); } if (lprc->right > lprcClip->right) { OffsetRect(lprc, lprcClip->right - lprc->right, 0); } if (lprc->left < lprcClip->left) { OffsetRect(lprc, lprcClip->left - lprc->left, 0); } } /***************************************************************************\ * MapDialogRect * * History: \***************************************************************************/ FUNCLOG2(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, MapDialogRect, HWND, hwnd, LPRECT, lprc) BOOL MapDialogRect( HWND hwnd, LPRECT lprc) { PWND pwnd; if ((pwnd = ValidateHwnd(hwnd)) == NULL) { return FALSE; } /* * Must do special validation here to make sure pwnd is a dialog window. */ if (!ValidateDialogPwnd(pwnd)) return FALSE; lprc->left = XPixFromXDU(lprc->left, PDLG(pwnd)->cxChar); lprc->right = XPixFromXDU(lprc->right, PDLG(pwnd)->cxChar); lprc->top = YPixFromYDU(lprc->top, PDLG(pwnd)->cyChar); lprc->bottom = YPixFromYDU(lprc->bottom, PDLG(pwnd)->cyChar); return TRUE; }