From 047513f3de7df81e874d483b72c5f79c6ef4771e Mon Sep 17 00:00:00 2001
From: Alexandre Julliard <julliard@winehq.org>
Date: Sat, 26 Feb 2000 18:31:01 +0000
Subject: [PATCH] Don Kelly In Windows Property Sheet can have any mix of
 icon-less tabs and tabs with icons.  Adds a check to see if the icon we're
 adding is non-NULL (otherwise random junk from memory can be rendered) when
 the application has specified the PSP_USEICONID flag is set.  Changes to the
 Tab control to only render icons for tabs that have the TCIF_IMAGE flag set
 (previously, if the flag was set the entire image list of icons was
 rendered).

Stephane Lussier <stephane@macadamian.com>
Fixes for some tab control bugs

Henning Hoffmann
Fixed some width problem with OWNERDRAW tab.

Luc Tourangeau <luc@macadamian.com>
TCM_ADJUSTRECT is now returning consistant compare to Windows.

Serge Ivanov <sergei@corel.ca>
Fixed problem with tab selection. When you select tab it becames first
visible tab. Now leftmost visible tab is calculated properly.
- Added code for correct handling of updown control.
- Forced recalculation of tabs' coordinates when:
a) all items are deleted,
b) window style is canged
---
 dlls/comctl32/tab.c | 250 ++++++++++++++++++++++++++------------------
 include/tab.h       |   1 +
 2 files changed, 147 insertions(+), 104 deletions(-)

diff --git a/dlls/comctl32/tab.c b/dlls/comctl32/tab.c
index be8cce66663..760d73d1d1c 100644
--- a/dlls/comctl32/tab.c
+++ b/dlls/comctl32/tab.c
@@ -18,6 +18,7 @@
 #include "tab.h"
 #include "debugtools.h"
 #include "cache.h"
+#include "win.h"
 
 DEFAULT_DEBUG_CHANNEL(tab)
 
@@ -30,8 +31,8 @@ DEFAULT_DEBUG_CHANNEL(tab)
 #define ROUND_CORNER_SIZE       2
 #define FOCUS_RECT_HOFFSET      2
 #define FOCUS_RECT_VOFFSET      1
-#define DISPLAY_AREA_PADDINGX   5
-#define DISPLAY_AREA_PADDINGY   5
+#define DISPLAY_AREA_PADDINGX   2
+#define DISPLAY_AREA_PADDINGY   2
 #define CONTROL_BORDER_SIZEX    2
 #define CONTROL_BORDER_SIZEY    2
 #define BUTTON_SPACINGX         10
@@ -519,26 +520,19 @@ static LRESULT TAB_OnHScroll(
 {
   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
 
-  if (nScrollCode == SB_LINELEFT)
+  if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
   {
-    if (infoPtr->leftmostVisible>0)
-    {
-      infoPtr->leftmostVisible--;
+     if(nPos < infoPtr->leftmostVisible)
+        infoPtr->leftmostVisible--;
+     else
+        infoPtr->leftmostVisible++;
 
-      TAB_InvalidateTabArea(hwnd, infoPtr);
-    }
-  }
-  else if (nScrollCode == SB_LINERIGHT)
-  {
-    if (infoPtr->leftmostVisible< (infoPtr->uNumItem-1))
-    {
-      infoPtr->leftmostVisible++;
+     TAB_InvalidateTabArea(hwnd, infoPtr);
+     SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
+                   MAKELONG(infoPtr->leftmostVisible, 0));
+   }
 
-      TAB_InvalidateTabArea(hwnd, infoPtr);
-    }
-  }
-
-  return 0;
+   return 0;
 }
 
 /******************************************************************************
@@ -552,9 +546,11 @@ static void TAB_SetupScrolling(
   TAB_INFO*   infoPtr,
   const RECT* clientRect)
 {
+  INT maxRange = 0;
   if (infoPtr->needsScrolling)
   {
     RECT controlPos;
+    INT vsize, tabwidth;
 
     /*
      * Calculate the position of the scroll control.
@@ -579,13 +575,9 @@ static void TAB_SetupScrolling(
      */
     if (infoPtr->hwndUpDown==0)
     {
-      /*
-       * I use a scrollbar since it seems to be more stable than the Updown
-       * control.
-       */
-      infoPtr->hwndUpDown = CreateWindowA("ScrollBar",
+      infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
 					  "",
-					  WS_VISIBLE | WS_CHILD | WS_OVERLAPPED | SBS_HORZ,
+					  WS_VISIBLE | WS_CHILD | UDS_HORZ,
 					  controlPos.left, controlPos.top,
 					  controlPos.right - controlPos.left,
 					  controlPos.bottom - controlPos.top,
@@ -603,6 +595,26 @@ static void TAB_SetupScrolling(
 		   controlPos.bottom - controlPos.top,
 		   SWP_SHOWWINDOW | SWP_NOZORDER);		   
     }
+
+    /* Now calculate upper limit of the updown control range.
+     * We do this by calculating how many tabs will be offscreen when the
+     * last tab is visible.
+     */
+    if(infoPtr->uNumItem)
+    {
+       vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
+       maxRange = infoPtr->uNumItem;
+       tabwidth = infoPtr->items[maxRange-1].rect.right;
+
+       for(; maxRange > 0; maxRange--)
+       {
+          if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
+             break;
+       }
+
+       if(maxRange == infoPtr->uNumItem)
+          maxRange--;
+    }
   }
   else
   {
@@ -614,6 +626,8 @@ static void TAB_SetupScrolling(
       ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
     }
   }
+  if (infoPtr->hwndUpDown)
+     SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
 }
 
 /******************************************************************************
@@ -658,7 +672,7 @@ static void TAB_SetItemBounds (HWND hwnd)
    */
   curItemLeftPos = 0;
 
-  if (!((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED)))
+  if ( !(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet) )
   {
     int item_height;
     int icon_height = 0;
@@ -715,7 +729,7 @@ static void TAB_SetItemBounds (HWND hwnd)
      */
     infoPtr->items[curItem].rect.left = curItemLeftPos;
 
-    if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
+    if ( (lStyle & TCS_FIXEDWIDTH) || ((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
     {
       infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
                                            infoPtr->tabWidth +
@@ -830,7 +844,7 @@ static void TAB_DrawItem(
         /* 
          * Background color. 
          */
-        if (!(lStyle & TCS_OWNERDRAWFIXED))
+        if (!((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
 	{
               COLORREF bk = GetSysColor(COLOR_3DHILIGHT);
 	  DeleteObject(hbr);
@@ -968,11 +982,52 @@ static void TAB_DrawItem(
     InflateRect(&r, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
 
     /*
+     * if owner draw, tell the owner to draw
+     */
+    if ( (lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd) )
+    {
+	DRAWITEMSTRUCT dis;
+	WND *pwndPtr;
+	UINT id;
+
+	/*
+	 * get the control id
+	 */
+	pwndPtr = WIN_FindWndPtr( hwnd );
+	id = pwndPtr->wIDmenu;
+	WIN_ReleaseWndPtr(pwndPtr);
+
+	/* 
+	 * put together the DRAWITEMSTRUCT
+	 */
+	dis.CtlType    = ODT_TAB;	
+	dis.CtlID      = id;		
+	dis.itemID     = iItem;		
+	dis.itemAction = ODA_DRAWENTIRE;	
+	if ( iItem == infoPtr->iSelected )
+	    dis.itemState = ODS_SELECTED;	
+	else				
+	    dis.itemState = 0;		
+	dis.hwndItem = hwnd;		/* */
+	dis.hDC      = hdc;		
+	dis.rcItem   = r;		/* */
+	dis.itemData = infoPtr->items[iItem].lParam;
+
+	/*
+	 * send the draw message
+	 */
+	SendMessageA( GetParent(hwnd), WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
+    }
+    else
+    {
+	/*
+	 * If not owner draw, then do the drawing ourselves.
+	 *
      * Draw the icon.
      */
-    if (infoPtr->himl) 
+    if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE) )
     {
-      ImageList_Draw (infoPtr->himl, iItem, hdc, 
+      ImageList_Draw (infoPtr->himl, infoPtr->items[iItem].iImage, hdc, 
 		      r.left, r.top+1, ILD_NORMAL);
       ImageList_GetIconSize (infoPtr->himl, &cx, &cy);
       r.left+=(cx + HORIZONTAL_ITEM_PADDING);
@@ -986,6 +1041,7 @@ static void TAB_DrawItem(
 	      lstrlenA(infoPtr->items[iItem].pszText),
 	      &r, 
 	      DT_LEFT|DT_SINGLELINE|DT_VCENTER);
+    }
 
     /*
      * Draw the focus rectangle
@@ -1163,10 +1219,7 @@ static void TAB_EnsureSelectionVisible(
   HWND      hwnd,
   TAB_INFO* infoPtr)
 {
-  RECT selectedRect;
-  RECT visibleRect;
-  RECT scrollerRect;
-  BOOL isVisible;
+  INT iSelected = infoPtr->iSelected;
 
   /*
    * Do the trivial cases first.
@@ -1175,76 +1228,45 @@ static void TAB_EnsureSelectionVisible(
        (infoPtr->hwndUpDown==0) )
     return;
 
-  if (infoPtr->leftmostVisible > infoPtr->iSelected)
+  if (infoPtr->leftmostVisible >= iSelected)
   {
-    infoPtr->leftmostVisible = infoPtr->iSelected;
-    return;
+    infoPtr->leftmostVisible = iSelected;
   }
-
-  /*
-   * Calculate the part of the client area that is visible.
-   */
-  GetClientRect(hwnd, &visibleRect);
-  GetClientRect(infoPtr->hwndUpDown, &scrollerRect);
-  visibleRect.right -= scrollerRect.right;
-  
-  /*
-   * Get the rectangle for the item
-   */
-  isVisible = TAB_InternalGetItemRect(hwnd,
-				      infoPtr,
-				      infoPtr->iSelected,
-				      NULL,
-				      &selectedRect);
-
-  /*
-   * If this function can't say it's completely invisible, maybe it
-   * is partially visible. Let's check.
-   */
-  if (isVisible)
+  else
   {
-    POINT pt1;
-    POINT pt2;
-
-    pt1.x = selectedRect.left;
-    pt1.y = selectedRect.top;
-    pt2.x = selectedRect.right - 1;
-    pt2.y = selectedRect.bottom - 1;
-
-    isVisible = PtInRect(&visibleRect, pt1) &&  PtInRect(&visibleRect, pt2);
+     RECT r;
+     INT  width, i;
+     /*
+      * Calculate the part of the client area that is visible.
+      */
+     GetClientRect(hwnd, &r);
+     width = r.right;
+
+     GetClientRect(infoPtr->hwndUpDown, &r);
+     width -= r.right;
+
+     if ((infoPtr->items[iSelected].rect.right -
+          infoPtr->items[iSelected].rect.left) >= width )
+     {
+        /* Special case: width of selected item is greater than visible
+         * part of control.
+         */
+        infoPtr->leftmostVisible = iSelected;
+     }
+     else
+     {
+        for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
+        {
+           if ((infoPtr->items[iSelected].rect.right -
+                infoPtr->items[i].rect.left) < width)
+              break;
+        }
+        infoPtr->leftmostVisible = i;
+     }
   }
 
-  while ( (infoPtr->leftmostVisible < infoPtr->iSelected) &&
-	  !isVisible)
-  {
-    infoPtr->leftmostVisible++;
-
-    /*
-     * Get the rectangle for the item
-     */
-    isVisible = TAB_InternalGetItemRect(hwnd,
-					infoPtr,
-					infoPtr->iSelected,
-					NULL,
-					&selectedRect);
-
-    /*
-     * If this function can't say it's completely invisible, maybe it
-     * is partially visible. Let's check.
-     */
-    if (isVisible)
-    {
-      POINT pt1;
-      POINT pt2;
-
-      pt1.x = selectedRect.left;
-      pt1.y = selectedRect.top;
-      pt2.x = selectedRect.right - 1;
-      pt2.y = selectedRect.bottom - 1;
-      
-      isVisible = PtInRect(&visibleRect, pt1) &&  PtInRect(&visibleRect, pt2);
-    }
-  }
+  SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
+               MAKELONG(infoPtr->leftmostVisible, 0));
 }
 
 /******************************************************************************
@@ -1264,7 +1286,7 @@ static void TAB_InvalidateTabArea(
 
   if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM) 
   {
-    clientRect.top = clientRect.bottom - (infoPtr->tabHeight + 1);
+    clientRect.top = clientRect.bottom - (infoPtr->tabHeight + 3);
   }
   else
   {
@@ -1374,6 +1396,7 @@ TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
     infoPtr->tabWidth = (INT)LOWORD(lParam);
     infoPtr->tabHeight = (INT)HIWORD(lParam);
   }
+  infoPtr->fSizeSet = TRUE;
 
   return lResult;
 }
@@ -1518,7 +1541,9 @@ TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
   COMCTL32_Free (infoPtr->items);
   infoPtr->uNumItem = 0;
   infoPtr->iSelected = -1;
-  
+ 
+  TAB_SetItemBounds(hwnd);
+  TAB_InvalidateTabArea(hwnd,infoPtr);
   return TRUE;
 }
 
@@ -1621,6 +1646,7 @@ TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
   TEXTMETRICA fontMetrics;
   HDC hdc;
   HFONT hOldFont;
+  DWORD dwStyle;
 
   infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
 
@@ -1637,9 +1663,17 @@ TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
   infoPtr->needsScrolling  = FALSE;
   infoPtr->hwndUpDown      = 0;
   infoPtr->leftmostVisible = 0;
+  infoPtr->fSizeSet	   = FALSE;
   
   TRACE("Created tab control, hwnd [%04x]\n", hwnd); 
-  if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_TOOLTIPS) {
+
+  /* The tab control always has the WS_CLIPSIBLINGS style. Even 
+     if you don't specify in CreateWindow. This is necesary in 
+     order for paint to work correctly. This follows windows behaviour. */
+  dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
+  SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
+
+  if (dwStyle & TCS_TOOLTIPS) {
     /* Create tooltip control */
     infoPtr->hwndToolTip =
       CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
@@ -1697,6 +1731,9 @@ TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
   INT iItem;
 
+  if (!infoPtr)
+      return 0;
+
   if (infoPtr->items) {
     for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
       if (infoPtr->items[iItem].pszText)
@@ -1814,8 +1851,8 @@ TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
     case TCM_SETCURFOCUS:
       return TAB_SetCurFocus (hwnd, wParam);
       
-    case TCM_SETMINTTABWIDTH:
-      FIXME("Unimplemented msg TCM_SETMINTTABWIDTH\n");
+    case TCM_SETMINTABWIDTH:
+      FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
       return 0;
       
     case TCM_DESELECTALL:
@@ -1871,6 +1908,11 @@ TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 
     case WM_HSCROLL:
       return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
+
+    case WM_STYLECHANGED:
+      TAB_SetItemBounds (hwnd);
+      InvalidateRect(hwnd, NULL, TRUE);
+      return 0;
       
     case WM_KILLFOCUS:
     case WM_SETFOCUS:
diff --git a/include/tab.h b/include/tab.h
index 2adb506aaab..9279b8c7110 100644
--- a/include/tab.h
+++ b/include/tab.h
@@ -41,6 +41,7 @@ typedef struct tagTAB_INFO
   BOOL       DoRedraw;        /* flag for redrawing when tab contents is changed*/
   BOOL       needsScrolling;  /* TRUE if the size of the tabs is greater than 
 			       * the size of the control */
+  BOOL	     fSizeSet;	      /* was the size of the tabs explicitly set? */
   HWND       hwndUpDown;      /* Updown control used for scrolling */
 } TAB_INFO;
 
-- 
GitLab