[USER32] Add support for navigating a group of radio buttons using a keyboard.
[reactos.git] / win32ss / user / user32 / windows / dialog.c
index 48304d1..4770169 100644 (file)
@@ -18,8 +18,8 @@
  */
 /*
  * PROJECT:         ReactOS user32.dll
- * FILE:            dll/win32/user32/windows/dialog.c
- * PURPOSE:         Input
+ * FILE:            win32ss/user/user32/windows/dialog.c
+ * PURPOSE:         Dialog Manager
  * PROGRAMMER:      Casper S. Hornstrup (chorns@users.sourceforge.net)
  *                  Thomas Weidenmueller (w3seek@users.sourceforge.net)
  *                  Steven Edwards (Steven_Ed4153@yahoo.com)
  *      09-05-2001  CSH  Created
  */
 
-/* INCLUDES ******************************************************************/
-
 #include <user32.h>
 
-#include <wine/debug.h>
 WINE_DEFAULT_DEBUG_CHANNEL(user32);
 
 /* MACROS/DEFINITIONS ********************************************************/
 
 #define DF_END  0x0001
-#define DF_OWNERENABLED 0x0002
 #define DF_DIALOGACTIVE 0x4000 // ReactOS
 #define DWLP_ROS_DIALOGINFO (DWLP_USER+sizeof(ULONG_PTR))
 #define GETDLGINFO(hwnd) DIALOG_get_info(hwnd, FALSE)
@@ -172,43 +168,6 @@ DIALOGINFO *DIALOG_get_info( HWND hWnd, BOOL create )
     return dlgInfo;
 }
 
-/***********************************************************************
- *           DIALOG_EnableOwner
- *
- * Helper function for modal dialogs to enable again the
- * owner of the dialog box.
- */
-void DIALOG_EnableOwner( HWND hOwner )
-{
-    /* Owner must be a top-level window */
-    if (hOwner)
-        hOwner = GetAncestor( hOwner, GA_ROOT );
-    if (!hOwner) return;
-    EnableWindow( hOwner, TRUE );
-}
-
-
-/***********************************************************************
- *           DIALOG_DisableOwner
- *
- * Helper function for modal dialogs to disable the
- * owner of the dialog box. Returns TRUE if owner was enabled.
- */
-BOOL DIALOG_DisableOwner( HWND hOwner )
-{
-    /* Owner must be a top-level window */
-    if (hOwner)
-        hOwner = GetAncestor( hOwner, GA_ROOT );
-    if (!hOwner) return FALSE;
-    if (IsWindowEnabled( hOwner ))
-    {
-        EnableWindow( hOwner, FALSE );
-        return TRUE;
-    }
-    else
-        return FALSE;
-}
-
 /***********************************************************************
  *           DIALOG_GetControl32
  *
@@ -308,7 +267,7 @@ static const WORD *DIALOG_GetControl32( const WORD *p, DLG_CONTROL_INFO *info,
 
     if (GET_WORD(p))
     {
-        info->data = p + 1;
+        info->data = p;
         p += GET_WORD(p) / sizeof(WORD);
     }
     else info->data = NULL;
@@ -546,7 +505,6 @@ INT DIALOG_DoDialogBox( HWND hwnd, HWND owner )
     DIALOGINFO * dlgInfo;
     MSG msg;
     INT retval;
-    HWND ownerMsg = GetAncestor( owner, GA_ROOT );
     BOOL bFirstEmpty;
     PWND pWnd;
 
@@ -571,7 +529,7 @@ INT DIALOG_DoDialogBox( HWND hwnd, HWND owner )
                 if (!(GetWindowLongPtrW( hwnd, GWL_STYLE ) & DS_NOIDLEMSG))
                {
                     /* No message present -> send ENTERIDLE and wait */
-                    if (ownerMsg) SendMessageW( ownerMsg, WM_ENTERIDLE, MSGF_DIALOGBOX, (LPARAM)hwnd );
+                    SendMessageW( owner, WM_ENTERIDLE, MSGF_DIALOGBOX, (LPARAM)hwnd );
                 }
                 GetMessageW( &msg, 0, 0, 0 );
             }
@@ -585,16 +543,16 @@ INT DIALOG_DoDialogBox( HWND hwnd, HWND owner )
 
             /*
              * If the user is pressing Ctrl+C, send a WM_COPY message.
-             * Guido Pola, Bug 5281, Is there another way to check if the Dialog it's a MessageBox?
+             * Guido Pola, CORE-4829, Is there another way to check if the Dialog is a MessageBox?
              */
-            ifmsg.message == WM_KEYDOWN &&
+            if (msg.message == WM_KEYDOWN &&
                 pWnd->state & WNDS_MSGBOX && // Yes!
-                GetForegroundWindow() == hwnd )
+                GetForegroundWindow() == hwnd)
             {
-                if( msg.wParam == L'C' && GetKeyState(VK_CONTROL) < 0 )
-                    SendMessageW( hwnd, WM_COPY, 0, 0);
+                if (msg.wParam == L'C' && GetKeyState(VK_CONTROL) < 0)
+                    SendMessageW(hwnd, WM_COPY, 0, 0);
             }
-            
+
             if (!IsWindow( hwnd )) return 0;
             if (!(dlgInfo->flags & DF_END) && !IsDialogMessageW( hwnd, &msg))
             {
@@ -611,7 +569,6 @@ INT DIALOG_DoDialogBox( HWND hwnd, HWND owner )
             }
         }
     }
-    if (dlgInfo->flags & DF_OWNERENABLED) DIALOG_EnableOwner( owner );
     retval = dlgInfo->idResult;
     DestroyWindow( hwnd );
     return retval;
@@ -802,8 +759,7 @@ static void DEFDLG_RestoreFocus( HWND hwnd, BOOL justActivate )
     else
         DEFDLG_SetFocus( infoPtr->hwndFocus );
 
-    /* This used to set infoPtr->hwndFocus to NULL for no apparent reason,
-       sometimes losing focus when receiving WM_SETFOCUS messages. */
+    infoPtr->hwndFocus = NULL;
 }
 
 /***********************************************************************
@@ -816,7 +772,7 @@ static void DEFDLG_RestoreFocus( HWND hwnd, BOOL justActivate )
  */
 static HWND DIALOG_CreateIndirect( HINSTANCE hInst, LPCVOID dlgTemplate,
                                    HWND owner, DLGPROC dlgProc, LPARAM param,
-                                   BOOL unicode, BOOL modal )
+                                   BOOL unicode, HWND *modal_owner )
 {
     HWND hwnd;
     RECT rect;
@@ -825,7 +781,7 @@ static HWND DIALOG_CreateIndirect( HINSTANCE hInst, LPCVOID dlgTemplate,
     DLG_TEMPLATE template;
     DIALOGINFO * dlgInfo = NULL;
     DWORD units = GetDialogBaseUnits();
-    BOOL ownerEnabled = TRUE;
+    HWND disabled_owner = NULL;
     HMENU hMenu = 0;
     HFONT hUserFont = 0;
     UINT flags = 0;
@@ -888,10 +844,7 @@ static HWND DIALOG_CreateIndirect( HINSTANCE hInst, LPCVOID dlgTemplate,
 
     /* Create dialog main window */
 
-    rect.left = rect.top = 0;
-    rect.right = MulDiv(template.cx, xBaseUnit, 4);
-    rect.bottom =  MulDiv(template.cy, yBaseUnit, 8);
-
+    SetRect(&rect, 0, 0, MulDiv(template.cx, xBaseUnit, 4), MulDiv(template.cy, yBaseUnit, 8));
     if (template.style & DS_CONTROL)
         template.style &= ~(WS_CAPTION|WS_SYSMENU);
     template.style |= DS_3DLOOK;
@@ -932,7 +885,10 @@ static HWND DIALOG_CreateIndirect( HINSTANCE hInst, LPCVOID dlgTemplate,
         {
             pos.x += MulDiv(template.x, xBaseUnit, 4);
             pos.y += MulDiv(template.y, yBaseUnit, 8);
-            if (!(template.style & (WS_CHILD|DS_ABSALIGN))) ClientToScreen( owner, &pos );
+            //
+            // REACTOS : Need an owner to be passed!!!
+            //
+            if (!(template.style & (WS_CHILD|DS_ABSALIGN)) && owner ) ClientToScreen( owner, &pos );
         }
         if ( !(template.style & WS_CHILD) )
         {
@@ -955,10 +911,35 @@ static HWND DIALOG_CreateIndirect( HINSTANCE hInst, LPCVOID dlgTemplate,
         }
     }
 
-    if (modal)
+    if (modal_owner && owner)
     {
-        ownerEnabled = DIALOG_DisableOwner( owner );
-        if (ownerEnabled) flags |= DF_OWNERENABLED;
+        HWND parent = NULL;
+        /*
+         * Owner needs to be top level window. We need to duplicate the logic from server,
+         * because we need to disable it before creating dialog window. Note that we do that
+         * even if dialog has WS_CHILD, but only for modal dialogs, which matched what
+         * Windows does.
+         */
+        while ((GetWindowLongW( owner, GWL_STYLE ) & (WS_POPUP|WS_CHILD)) == WS_CHILD)
+        {
+            parent = GetParent( owner );
+            if (!parent || parent == GetDesktopWindow()) break;
+            owner = parent;
+        }
+        ////// Wine'ie babies need to fix your code!!!! CORE-11633
+        if (!parent) parent = GetAncestor( owner, GA_ROOT );
+
+        if (parent)
+        {
+           owner = parent;
+
+           if (IsWindowEnabled( owner ))
+           {
+               disabled_owner = owner;
+               EnableWindow( disabled_owner, FALSE );
+           }
+        }
+        *modal_owner = owner;
     }
 
     if (unicode)
@@ -999,7 +980,7 @@ static HWND DIALOG_CreateIndirect( HINSTANCE hInst, LPCVOID dlgTemplate,
     {
         if (hUserFont) DeleteObject( hUserFont );
         if (hMenu) DestroyMenu( hMenu );
-        if (modal && (flags & DF_OWNERENABLED)) DIALOG_EnableOwner(owner);
+        if (disabled_owner) EnableWindow( disabled_owner, TRUE );
         return 0;
     }
 
@@ -1012,7 +993,7 @@ static HWND DIALOG_CreateIndirect( HINSTANCE hInst, LPCVOID dlgTemplate,
     {
         if (hUserFont) DeleteObject( hUserFont );
         if (hMenu) DestroyMenu( hMenu );
-        if (modal && (flags & DF_OWNERENABLED)) DIALOG_EnableOwner(owner);
+        if (disabled_owner) EnableWindow( disabled_owner, TRUE );
         return 0;
     }
     //
@@ -1047,13 +1028,18 @@ static HWND DIALOG_CreateIndirect( HINSTANCE hInst, LPCVOID dlgTemplate,
                 /* By returning TRUE, app has requested a default focus assignment.
                  * WM_INITDIALOG may have changed the tab order, so find the first
                  * tabstop control again. */
-                dlgInfo->hwndFocus = GetNextDlgTabItem( hwnd, 0, FALSE );
-                if (!dlgInfo->hwndFocus) dlgInfo->hwndFocus = GetNextDlgGroupItem( hwnd, 0, FALSE );
-                if( dlgInfo->hwndFocus )
-                    SetFocus( dlgInfo->hwndFocus );
+                focus = GetNextDlgTabItem( hwnd, 0, FALSE );
+                if (!focus) focus = GetNextDlgGroupItem( hwnd, 0, FALSE );
+                if (focus)
+                {
+                    if (SendMessageW( focus, WM_GETDLGCODE, 0, 0 ) & DLGC_HASSETSEL)
+                        SendMessageW( focus, EM_SETSEL, 0, MAXLONG );
+                    SetFocus( focus );
+                }
             }
-//// ReactOS
-            DEFDLG_SaveFocus( hwnd );
+//// ReactOS see 43396, Fixes setting focus on Open and Close dialogs to the FileName edit control in OpenOffice.
+//// This now breaks test_SaveRestoreFocus.
+            //DEFDLG_SaveFocus( hwnd );
 ////
         }
 //// ReactOS Rev 30613 & 30644
@@ -1063,11 +1049,12 @@ static HWND DIALOG_CreateIndirect( HINSTANCE hInst, LPCVOID dlgTemplate,
         if (template.style & WS_VISIBLE && !(GetWindowLongPtrW( hwnd, GWL_STYLE ) & WS_VISIBLE))
         {
            ShowWindow( hwnd, SW_SHOWNORMAL );   /* SW_SHOW doesn't always work */
+           UpdateWindow( hwnd );
            IntNotifyWinEvent(EVENT_SYSTEM_DIALOGSTART, hwnd, OBJID_WINDOW, CHILDID_SELF, 0);
         }
         return hwnd;
     }
-    if (modal && ownerEnabled) DIALOG_EnableOwner(owner);
+    if (disabled_owner) EnableWindow( disabled_owner, TRUE );
     IntNotifyWinEvent(EVENT_SYSTEM_DIALOGEND, hwnd, OBJID_WINDOW, CHILDID_SELF, 0);
     if( IsWindow(hwnd) )
     {
@@ -1576,7 +1563,7 @@ CreateDialogIndirectParamAorW(
  *   Also wine has one more parameter identifying weather it should call
  *   the function with unicode or not
  */
-  return DIALOG_CreateIndirect( hInstance, lpTemplate, hWndParent, lpDialogFunc, lParamInit , Flags == DLG_ISANSI ? FALSE : TRUE, FALSE );
+  return DIALOG_CreateIndirect( hInstance, lpTemplate, hWndParent, lpDialogFunc, lParamInit , Flags == DLG_ISANSI ? FALSE : TRUE, NULL );
 }
 
 
@@ -1670,7 +1657,7 @@ DefDlgProcA(
     BOOL result = FALSE;
 
     /* Perform DIALOGINFO initialization if not done */
-    if(!(dlgInfo = DIALOG_get_info( hDlg, TRUE ))) return 0;
+    if(!(dlgInfo = DIALOG_get_info( hDlg, TRUE ))) return 0; //// REACTOS : Always TRUE! See RealGetWindowClass.
 
     SetWindowLongPtrW( hDlg, DWLP_MSGRESULT, 0 );
 
@@ -1730,7 +1717,7 @@ DefDlgProcW(
     BOOL result = FALSE;
 
     /* Perform DIALOGINFO initialization if not done */
-    if(!(dlgInfo = DIALOG_get_info( hDlg, TRUE ))) return 0;
+    if(!(dlgInfo = DIALOG_get_info( hDlg, TRUE ))) return 0; //// REACTOS : Always TRUE! See RealGetWindowClass.
 
     SetWindowLongPtrW( hDlg, DWLP_MSGRESULT, 0 );
 
@@ -1792,7 +1779,7 @@ DialogBoxIndirectParamAorW(
  *  Also wine has one more parameter identifying weather it should call
  *  the function with unicode or not
  */
-  HWND hWnd = DIALOG_CreateIndirect( hInstance, hDialogTemplate, hWndParent, lpDialogFunc, dwInitParam, Flags == DLG_ISANSI ? FALSE : TRUE, TRUE );
+  HWND hWnd = DIALOG_CreateIndirect( hInstance, hDialogTemplate, hWndParent, lpDialogFunc, dwInitParam, Flags == DLG_ISANSI ? FALSE : TRUE, &hWndParent );
   if (hWnd) return DIALOG_DoDialogBox( hWnd, hWndParent );
   return -1;
 }
@@ -1857,7 +1844,7 @@ DialogBoxParamA(
         SetLastError(ERROR_INVALID_WINDOW_HANDLE);
         return 0;
     }
-    hwnd = DIALOG_CreateIndirect(hInstance, ptr, hWndParent, lpDialogFunc, dwInitParam, FALSE, TRUE);
+    hwnd = DIALOG_CreateIndirect(hInstance, ptr, hWndParent, lpDialogFunc, dwInitParam, FALSE, &hWndParent );
     if (hwnd) return DIALOG_DoDialogBox(hwnd, hWndParent);
     return -1;
 }
@@ -1890,7 +1877,7 @@ DialogBoxParamW(
         SetLastError(ERROR_INVALID_WINDOW_HANDLE);
         return 0;
     }
-    hwnd = DIALOG_CreateIndirect(hInstance, ptr, hWndParent, lpDialogFunc, dwInitParam, TRUE, TRUE);
+    hwnd = DIALOG_CreateIndirect(hInstance, ptr, hWndParent, lpDialogFunc, dwInitParam, TRUE, &hWndParent );
     if (hwnd) return DIALOG_DoDialogBox(hwnd, hWndParent);
     return -1;
 }
@@ -2021,7 +2008,7 @@ DlgDirSelectExW(
 
 
 /*
- * @implemented Modified for ReactOS.
+ * @implemented Modified for ReactOS. Do not Port Sync!!!
  */
 BOOL
 WINAPI
@@ -2029,7 +2016,6 @@ EndDialog(
   HWND hwnd,
   INT_PTR retval)
 {
-    BOOL wasEnabled = TRUE;
     DIALOGINFO * dlgInfo;
     HWND owner;
     BOOL wasActive;
@@ -2044,11 +2030,16 @@ EndDialog(
     wasActive = (hwnd == GetActiveWindow());
     dlgInfo->idResult = retval;
     dlgInfo->flags |= DF_END;
-    wasEnabled = (dlgInfo->flags & DF_OWNERENABLED);
 
-    owner = GetWindow( hwnd, GW_OWNER );
-    if (wasEnabled && owner)
-        DIALOG_EnableOwner( owner );
+    if ((GetWindowLongW( hwnd, GWL_STYLE ) & (WS_POPUP|WS_CHILD)) == WS_CHILD)
+    {
+       owner = GetAncestor( hwnd, GA_PARENT);
+    }
+    else
+       owner = GetWindow( hwnd, GW_OWNER );    
+
+    if (owner)
+        EnableWindow( owner, TRUE );
 
     /* Windows sets the focus to the dialog itself in EndDialog */
 
@@ -2063,9 +2054,7 @@ EndDialog(
 
     if (wasActive && owner)
     {
-        /* If this dialog was given an owner then set the focus to that owner
-           even when the owner is disabled (normally when a window closes any
-           disabled windows cannot receive the focus). */
+        /* If this dialog was given an owner then set the focus to that owner. */
         SetActiveWindow(owner);
     }
     else if (hwnd == GetActiveWindow()) // Check it again!
@@ -2485,6 +2474,9 @@ IsDialogMessageW(
 {
     INT dlgCode = 0;
 
+    if (!IsWindow( hDlg ))
+        return FALSE;
+
     if (CallMsgFilterW( lpMsg, MSGF_DIALOGBOX )) return TRUE;
 
     if (hDlg == GetDesktopWindow()) return FALSE;
@@ -2540,7 +2532,7 @@ IsDialogMessageW(
                             WCHAR *buffer = HeapAlloc (GetProcessHeap(), 0, maxlen * sizeof(WCHAR));
                             if (buffer)
                             {
-                                INT length;
+                                SIZE_T length;
                                 SendMessageW (hwndNext, WM_GETTEXT, maxlen, (LPARAM) buffer);
                                 length = strlenW (buffer);
                                 HeapFree (GetProcessHeap(), 0, buffer);
@@ -2564,8 +2556,16 @@ IsDialogMessageW(
              if (!(dlgCode & DLGC_WANTARROWS))
              {
                  BOOL fPrevious = (lpMsg->wParam == VK_LEFT || lpMsg->wParam == VK_UP);
-                 HWND hwndNext = GetNextDlgGroupItem (hDlg, GetFocus(), fPrevious );
-                 SendMessageW( hDlg, WM_NEXTDLGCTL, (WPARAM)hwndNext, 1 );
+                 HWND hwndNext = GetNextDlgGroupItem( hDlg, lpMsg->hwnd, fPrevious );
+                 if (hwndNext && SendMessageW( hwndNext, WM_GETDLGCODE, lpMsg->wParam, (LPARAM)lpMsg ) == (DLGC_BUTTON | DLGC_RADIOBUTTON))
+                 {
+                     SetFocus( hwndNext );
+                     if ((GetWindowLongW( hwndNext, GWL_STYLE ) & BS_TYPEMASK) == BS_AUTORADIOBUTTON &&
+                         SendMessageW( hwndNext, BM_GETCHECK, 0, 0 ) != BST_CHECKED)
+                         SendMessageW( hwndNext, BM_CLICK, 1, 0 );
+                 }
+                 else
+                     SendMessageW( hDlg, WM_NEXTDLGCTL, (WPARAM)hwndNext, 1 );
                  return TRUE;
              }
              break;