From 2c6ab0dba50db55c922b8ee8199869f59cccdf61 Mon Sep 17 00:00:00 2001
From: Robert Shearman <rob@codeweavers.com>
Date: Wed, 14 Sep 2005 19:16:03 +0000
Subject: [PATCH] - Add support for navigating a toolbar with the arrow keys. -
 Fix WrapToolbar in the case of no parent window. - Use the newly added
 NMTBINITCUSTOMIZE for sending the   TBN_INITCUSTOMIZE so that it is safe on
 64-bit platforms.

---
 dlls/comctl32/toolbar.c | 124 ++++++++++++++++++++++++++++++++++++----
 include/commctrl.h      |  17 ++++++
 2 files changed, 129 insertions(+), 12 deletions(-)

diff --git a/dlls/comctl32/toolbar.c b/dlls/comctl32/toolbar.c
index 2b62dfc42ee..f3d5c94cc7c 100644
--- a/dlls/comctl32/toolbar.c
+++ b/dlls/comctl32/toolbar.c
@@ -41,7 +41,6 @@
  *     - WM_WININICHANGE
  *   - Notifications:
  *     - NM_CHAR
- *     - NM_KEYDOWN
  *     - TBN_GETOBJECT
  *     - TBN_SAVE
  *   - Button wrapping (under construction).
@@ -1278,12 +1277,21 @@ TOOLBAR_WrapToolbar( HWND hwnd, DWORD dwStyle )
     btnPtr = infoPtr->buttons;
     x  = infoPtr->nIndent;
 
-    /* this can get the parents width, to know how far we can extend
-     * this toolbar.  We cannot use its height, as there may be multiple
-     * toolbars in a rebar control
-     */
-    GetClientRect( GetParent(hwnd), &rc );
-    infoPtr->nWidth = rc.right - rc.left;
+    if (GetParent(hwnd))
+    {
+        /* this can get the parents width, to know how far we can extend
+         * this toolbar.  We cannot use its height, as there may be multiple
+         * toolbars in a rebar control
+         */
+        GetClientRect( GetParent(hwnd), &rc );
+        infoPtr->nWidth = rc.right - rc.left;
+    }
+    else
+    {
+        GetWindowRect( hwnd, &rc );
+        infoPtr->nWidth = rc.right - rc.left;
+    }
+
     bButtonWrap = FALSE;
 
     TRACE("start ButtonWidth=%d, BitmapWidth=%d, nWidth=%d, nIndent=%d\n",
@@ -2193,6 +2201,7 @@ TOOLBAR_CustomizeDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 		WCHAR Buffer[256];
 		int i = 0;
 		int index;
+		NMTBINITCUSTOMIZE nmtbic;
 
 		infoPtr = custInfo->tbInfo;
 
@@ -2202,10 +2211,9 @@ TOOLBAR_CustomizeDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 		if (!TOOLBAR_SendNotify(&nmtb.hdr, infoPtr, TBN_QUERYINSERT))
 		    return FALSE;
 
-		/* UNDOCUMENTED: dialog hwnd immediately follows NMHDR */
-		memcpy(&nmtb.iItem, &hwnd, sizeof(hwnd));
+		nmtbic.hwndDialog = hwnd;
 		/* Send TBN_INITCUSTOMIZE notification */
-		if (TOOLBAR_SendNotify ((NMHDR *) &nmtb, infoPtr, TBN_INITCUSTOMIZE) ==
+		if (TOOLBAR_SendNotify (&nmtbic.hdr, infoPtr, TBN_INITCUSTOMIZE) ==
 		    TBNRF_HIDEHELP)
                 {
                     TRACE("TBNRF_HIDEHELP requested\n");
@@ -5783,6 +5791,77 @@ TOOLBAR_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
 }
 
 
+static void
+TOOLBAR_SetRelativeHotItem(TOOLBAR_INFO *infoPtr, INT iDirection, DWORD dwReason)
+{
+    INT i;
+    INT nNewHotItem = infoPtr->nHotItem;
+
+    for (i = 0; i < infoPtr->nNumButtons; i++)
+    {
+        /* did we wrap? */
+        if ((nNewHotItem + iDirection < 0) ||
+            (nNewHotItem + iDirection >= infoPtr->nNumButtons))
+        {
+            NMTBWRAPHOTITEM nmtbwhi;
+            nmtbwhi.idNew = infoPtr->buttons[nNewHotItem].idCommand;
+            nmtbwhi.iDirection = iDirection;
+            nmtbwhi.dwReason = dwReason;
+    
+            if (TOOLBAR_SendNotify(&nmtbwhi.hdr, infoPtr, TBN_WRAPHOTITEM))
+                return;
+        }
+
+        nNewHotItem += iDirection;
+        nNewHotItem = (nNewHotItem + infoPtr->nNumButtons) % infoPtr->nNumButtons;
+
+        if ((infoPtr->buttons[nNewHotItem].fsState & TBSTATE_ENABLED) &&
+            !(infoPtr->buttons[nNewHotItem].fsStyle & BTNS_SEP))
+        {
+            TOOLBAR_SetHotItemEx(infoPtr, nNewHotItem, dwReason);
+            break;
+        }
+    }
+}
+
+static LRESULT
+TOOLBAR_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
+{
+    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
+    NMKEY nmkey;
+
+    nmkey.nVKey = (UINT)wParam;
+    nmkey.uFlags = HIWORD(lParam);
+
+    if (TOOLBAR_SendNotify(&nmkey.hdr, infoPtr, NM_KEYDOWN))
+        return DefWindowProcW(hwnd, WM_KEYDOWN, wParam, lParam);
+
+    switch ((UINT)wParam)
+    {
+    case VK_LEFT:
+    case VK_UP:
+        TOOLBAR_SetRelativeHotItem(infoPtr, -1, HICF_ARROWKEYS);
+        break;
+    case VK_RIGHT:
+    case VK_DOWN:
+        TOOLBAR_SetRelativeHotItem(infoPtr, 1, HICF_ARROWKEYS);
+        break;
+    case VK_SPACE:
+    case VK_RETURN:
+        if ((infoPtr->nHotItem >= 0) &&
+            (infoPtr->buttons[infoPtr->nHotItem].fsState & TBSTATE_ENABLED))
+        {
+            SendMessageW (infoPtr->hwndNotify, WM_COMMAND,
+                MAKEWPARAM(infoPtr->buttons[infoPtr->nHotItem].idCommand, BN_CLICKED),
+                (LPARAM)hwnd);
+        }
+        break;
+    }
+
+    return 0;
+}
+
+
 static LRESULT
 TOOLBAR_LButtonDblClk (HWND hwnd, WPARAM wParam, LPARAM lParam)
 {
@@ -6080,7 +6159,7 @@ TOOLBAR_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
 	if (btnPtr->fsState & TBSTATE_ENABLED)
 	{
 	    SendMessageW (infoPtr->hwndNotify, WM_COMMAND,
-	      MAKEWPARAM(infoPtr->buttons[nHit].idCommand, 0), (LPARAM)hwnd);
+	      MAKEWPARAM(infoPtr->buttons[nHit].idCommand, BN_CLICKED), (LPARAM)hwnd);
         }
     }
 
@@ -6320,6 +6399,7 @@ TOOLBAR_NCCreate (HWND hwnd, WPARAM wParam, LPARAM lParam)
     /* paranoid!! */
     infoPtr->dwStructSize = sizeof(TBBUTTON);
     infoPtr->nRows = 1;
+    infoPtr->nWidth = 0;
 
     /* fix instance handle, if the toolbar was created by CreateToolbarEx() */
     if (!GetWindowLongPtrW (hwnd, GWLP_HINSTANCE)) {
@@ -6639,6 +6719,21 @@ TOOLBAR_Paint (HWND hwnd, WPARAM wParam)
 }
 
 
+static LRESULT
+TOOLBAR_SetFocus (HWND hwnd, WPARAM wParam)
+{
+    TOOLBAR_INFO *infoPtr = TOOLBAR_GetInfoPtr (hwnd);
+
+    TRACE("nHotItem = %d\n", infoPtr->nHotItem);
+
+    /* make first item hot */
+    if (infoPtr->nNumButtons > 0)
+        TOOLBAR_SetHotItemEx(infoPtr, 0, HICF_OTHER);
+
+    return 0;
+}
+
+
 static LRESULT
 TOOLBAR_SetRedraw (HWND hwnd, WPARAM wParam, LPARAM lParam)
      /*****************************************************
@@ -7092,7 +7187,9 @@ ToolbarWindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 	case WM_GETFONT:
 		return TOOLBAR_GetFont (hwnd, wParam, lParam);
 
-/*	case WM_KEYDOWN: */
+	case WM_KEYDOWN:
+	    return TOOLBAR_KeyDown (hwnd, wParam, lParam);
+
 /*	case WM_KILLFOCUS: */
 
 	case WM_LBUTTONDBLCLK:
@@ -7140,6 +7237,9 @@ ToolbarWindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 	case WM_PAINT:
 	    return TOOLBAR_Paint (hwnd, wParam);
 
+	case WM_SETFOCUS:
+	    return TOOLBAR_SetFocus (hwnd, wParam);
+
 	case WM_SETREDRAW:
 	    return TOOLBAR_SetRedraw (hwnd, wParam, lParam);
 
diff --git a/include/commctrl.h b/include/commctrl.h
index fc756ee5bd2..30558766992 100644
--- a/include/commctrl.h
+++ b/include/commctrl.h
@@ -1235,6 +1235,7 @@ static const WCHAR TOOLBARCLASSNAMEW[] = { 'T','o','o','l','b','a','r',
 #define TBN_RESTORE	        (TBN_FIRST-21)
 #define TBN_SAVE	        (TBN_FIRST-22)
 #define TBN_INITCUSTOMIZE	(TBN_FIRST-23)
+#define TBN_WRAPHOTITEM         (TBN_FIRST-24) /* this is undocumented and the name is a guess */
 #define TBNRF_HIDEHELP		0x00000001
 
 
@@ -1552,6 +1553,22 @@ typedef struct
     INT cyButtonSpacing;
 } TBMETRICS, *LPTBMETRICS;
 
+/* these are undocumented and the names are guesses */
+typedef struct
+{
+    NMHDR hdr;
+    HWND hwndDialog;
+} NMTBINITCUSTOMIZE;
+
+typedef struct
+{
+    NMHDR hdr;
+    INT idNew;
+    INT iDirection; /* left is -1, right is 1 */
+    DWORD dwReason; /* HICF_* */
+} NMTBWRAPHOTITEM;
+
+
 HWND WINAPI
 CreateToolbar(HWND, DWORD, UINT, INT, HINSTANCE,
               UINT, LPCTBBUTTON, INT);
-- 
GitLab