ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

tab.c
Go to the documentation of this file.
00001 /*
00002  * Tab control
00003  *
00004  * Copyright 1998 Anders Carlsson
00005  * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
00006  * Copyright 1999 Francis Beaudet
00007  * Copyright 2003 Vitaliy Margolen
00008  *
00009  * This library is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Lesser General Public
00011  * License as published by the Free Software Foundation; either
00012  * version 2.1 of the License, or (at your option) any later version.
00013  *
00014  * This library is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  * Lesser General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU Lesser General Public
00020  * License along with this library; if not, write to the Free Software
00021  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00022  *
00023  * NOTES
00024  *
00025  * This code was audited for completeness against the documented features
00026  * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
00027  *
00028  * Unless otherwise noted, we believe this code to be complete, as per
00029  * the specification mentioned above.
00030  * If you discover missing features, or bugs, please note them below.
00031  *
00032  * TODO:
00033  *
00034  *  Styles:
00035  *   TCS_MULTISELECT - implement for VK_SPACE selection
00036  *   TCS_RIGHT
00037  *   TCS_RIGHTJUSTIFY
00038  *   TCS_SCROLLOPPOSITE
00039  *   TCS_SINGLELINE
00040  *   TCIF_RTLREADING
00041  *
00042  *  Extended Styles:
00043  *   TCS_EX_REGISTERDROP
00044  *
00045  *  Notifications:
00046  *   NM_RELEASEDCAPTURE
00047  *   TCN_FOCUSCHANGE
00048  *   TCN_GETOBJECT
00049  *
00050  *  Macros:
00051  *   TabCtrl_AdjustRect
00052  *
00053  */
00054 
00055 #include <assert.h>
00056 #include <stdarg.h>
00057 #include <string.h>
00058 
00059 #include "windef.h"
00060 #include "winbase.h"
00061 #include "wingdi.h"
00062 #include "winuser.h"
00063 #include "winnls.h"
00064 #include "commctrl.h"
00065 #include "comctl32.h"
00066 #include "uxtheme.h"
00067 #include "vssym32.h"
00068 #include "wine/debug.h"
00069 #include <math.h>
00070 
00071 WINE_DEFAULT_DEBUG_CHANNEL(tab);
00072 
00073 typedef struct
00074 {
00075   DWORD  dwState;
00076   LPWSTR pszText;
00077   INT    iImage;
00078   RECT   rect;      /* bounding rectangle of the item relative to the
00079                      * leftmost item (the leftmost item, 0, would have a
00080                      * "left" member of 0 in this rectangle)
00081                      *
00082                      * additionally the top member holds the row number
00083                      * and bottom is unused and should be 0 */
00084   BYTE   extra[1];  /* Space for caller supplied info, variable size */
00085 } TAB_ITEM;
00086 
00087 /* The size of a tab item depends on how much extra data is requested.
00088    TCM_INSERTITEM always stores at least LPARAM sized data. */
00089 #define EXTRA_ITEM_SIZE(infoPtr) (max((infoPtr)->cbInfo, sizeof(LPARAM)))
00090 #define TAB_ITEM_SIZE(infoPtr) FIELD_OFFSET(TAB_ITEM, extra[EXTRA_ITEM_SIZE(infoPtr)])
00091 
00092 typedef struct
00093 {
00094   HWND       hwnd;            /* Tab control window */
00095   HWND       hwndNotify;      /* notification window (parent) */
00096   UINT       uNumItem;        /* number of tab items */
00097   UINT       uNumRows;        /* number of tab rows */
00098   INT        tabHeight;       /* height of the tab row */
00099   INT        tabWidth;        /* width of tabs */
00100   INT        tabMinWidth;     /* minimum width of items */
00101   USHORT     uHItemPadding;   /* amount of horizontal padding, in pixels */
00102   USHORT     uVItemPadding;   /* amount of vertical padding, in pixels */
00103   USHORT     uHItemPadding_s; /* Set amount of horizontal padding, in pixels */
00104   USHORT     uVItemPadding_s; /* Set amount of vertical padding, in pixels */
00105   HFONT      hFont;           /* handle to the current font */
00106   HCURSOR    hcurArrow;       /* handle to the current cursor */
00107   HIMAGELIST himl;            /* handle to an image list (may be 0) */
00108   HWND       hwndToolTip;     /* handle to tab's tooltip */
00109   INT        leftmostVisible; /* Used for scrolling, this member contains
00110                                * the index of the first visible item */
00111   INT        iSelected;       /* the currently selected item */
00112   INT        iHotTracked;     /* the highlighted item under the mouse */
00113   INT        uFocus;          /* item which has the focus */
00114   BOOL       DoRedraw;        /* flag for redrawing when tab contents is changed*/
00115   BOOL       needsScrolling;  /* TRUE if the size of the tabs is greater than
00116                                * the size of the control */
00117   BOOL       fHeightSet;      /* was the height of the tabs explicitly set? */
00118   BOOL       bUnicode;        /* Unicode control? */
00119   HWND       hwndUpDown;      /* Updown control used for scrolling */
00120   INT        cbInfo;          /* Number of bytes of caller supplied info per tab */
00121 
00122   DWORD      exStyle;         /* Extended style used, currently:
00123                                  TCS_EX_FLATSEPARATORS, TCS_EX_REGISTERDROP */
00124   DWORD      dwStyle;         /* the cached window GWL_STYLE */
00125 
00126   HDPA       items;           /* dynamic array of TAB_ITEM* pointers */
00127 } TAB_INFO;
00128 
00129 /******************************************************************************
00130  * Positioning constants
00131  */
00132 #define SELECTED_TAB_OFFSET     2
00133 #define ROUND_CORNER_SIZE       2
00134 #define DISPLAY_AREA_PADDINGX   2
00135 #define DISPLAY_AREA_PADDINGY   2
00136 #define CONTROL_BORDER_SIZEX    2
00137 #define CONTROL_BORDER_SIZEY    2
00138 #define BUTTON_SPACINGX         3
00139 #define BUTTON_SPACINGY         3
00140 #define FLAT_BTN_SPACINGX       8
00141 #define DEFAULT_MIN_TAB_WIDTH   54
00142 #define DEFAULT_PADDING_X       6
00143 #define EXTRA_ICON_PADDING      3
00144 
00145 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongPtrW(hwnd,0))
00146 
00147 #define GET_DEFAULT_MIN_TAB_WIDTH(infoPtr) (DEFAULT_MIN_TAB_WIDTH - (DEFAULT_PADDING_X - (infoPtr)->uHItemPadding) * 2)
00148 
00149 /******************************************************************************
00150  * Hot-tracking timer constants
00151  */
00152 #define TAB_HOTTRACK_TIMER            1
00153 #define TAB_HOTTRACK_TIMER_INTERVAL   100   /* milliseconds */
00154 
00155 static const WCHAR themeClass[] = { 'T','a','b',0 };
00156 
00157 static inline TAB_ITEM* TAB_GetItem(const TAB_INFO *infoPtr, INT i)
00158 {
00159     assert(i >= 0 && i < infoPtr->uNumItem);
00160     return DPA_GetPtr(infoPtr->items, i);
00161 }
00162 
00163 /******************************************************************************
00164  * Prototypes
00165  */
00166 static void TAB_InvalidateTabArea(const TAB_INFO *);
00167 static void TAB_EnsureSelectionVisible(TAB_INFO *);
00168 static void TAB_DrawItemInterior(const TAB_INFO *, HDC, INT, RECT*);
00169 static LRESULT TAB_DeselectAll(TAB_INFO *, BOOL);
00170 static BOOL TAB_InternalGetItemRect(const TAB_INFO *, INT, RECT*, RECT*);
00171 
00172 static BOOL
00173 TAB_SendSimpleNotify (const TAB_INFO *infoPtr, UINT code)
00174 {
00175     NMHDR nmhdr;
00176 
00177     nmhdr.hwndFrom = infoPtr->hwnd;
00178     nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID);
00179     nmhdr.code = code;
00180 
00181     return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
00182             nmhdr.idFrom, (LPARAM) &nmhdr);
00183 }
00184 
00185 static void
00186 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
00187             WPARAM wParam, LPARAM lParam)
00188 {
00189     MSG msg;
00190 
00191     msg.hwnd = hwndMsg;
00192     msg.message = uMsg;
00193     msg.wParam = wParam;
00194     msg.lParam = lParam;
00195     msg.time = GetMessageTime ();
00196     msg.pt.x = (short)LOWORD(GetMessagePos ());
00197     msg.pt.y = (short)HIWORD(GetMessagePos ());
00198 
00199     SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
00200 }
00201 
00202 static void
00203 TAB_DumpItemExternalT(const TCITEMW *pti, UINT iItem, BOOL isW)
00204 {
00205     if (TRACE_ON(tab)) {
00206     TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
00207           iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
00208     TRACE("external tab %d,   iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
00209           iItem, pti->iImage, pti->lParam, isW ? debugstr_w(pti->pszText) : debugstr_a((LPSTR)pti->pszText));
00210     }
00211 }
00212 
00213 static void
00214 TAB_DumpItemInternal(const TAB_INFO *infoPtr, UINT iItem)
00215 {
00216     if (TRACE_ON(tab)) {
00217     TAB_ITEM *ti = TAB_GetItem(infoPtr, iItem);
00218 
00219     TRACE("tab %d, dwState=0x%08x, pszText=%s, iImage=%d\n",
00220           iItem, ti->dwState, debugstr_w(ti->pszText), ti->iImage);
00221     TRACE("tab %d, rect.left=%d, rect.top(row)=%d\n",
00222           iItem, ti->rect.left, ti->rect.top);
00223     }
00224 }
00225 
00226 /* RETURNS
00227  *   the index of the selected tab, or -1 if no tab is selected. */
00228 static inline LRESULT TAB_GetCurSel (const TAB_INFO *infoPtr)
00229 {
00230     TRACE("(%p)\n", infoPtr);
00231     return infoPtr->iSelected;
00232 }
00233 
00234 /* RETURNS
00235  *   the index of the tab item that has the focus. */
00236 static inline LRESULT
00237 TAB_GetCurFocus (const TAB_INFO *infoPtr)
00238 {
00239     TRACE("(%p)\n", infoPtr);
00240     return infoPtr->uFocus;
00241 }
00242 
00243 static inline LRESULT TAB_GetToolTips (const TAB_INFO *infoPtr)
00244 {
00245     TRACE("(%p)\n", infoPtr);
00246     return (LRESULT)infoPtr->hwndToolTip;
00247 }
00248 
00249 static inline LRESULT TAB_SetCurSel (TAB_INFO *infoPtr, INT iItem)
00250 {
00251   INT prevItem = infoPtr->iSelected;
00252 
00253   TRACE("(%p %d)\n", infoPtr, iItem);
00254 
00255   if (iItem < 0)
00256       infoPtr->iSelected = -1;
00257   else if (iItem >= infoPtr->uNumItem)
00258       return -1;
00259   else {
00260       if (prevItem != iItem) {
00261           if (prevItem != -1)
00262               TAB_GetItem(infoPtr, prevItem)->dwState &= ~TCIS_BUTTONPRESSED;
00263           TAB_GetItem(infoPtr, iItem)->dwState |= TCIS_BUTTONPRESSED;
00264 
00265           infoPtr->iSelected = iItem;
00266           infoPtr->uFocus = iItem;
00267           TAB_EnsureSelectionVisible(infoPtr);
00268           TAB_InvalidateTabArea(infoPtr);
00269       }
00270   }
00271   return prevItem;
00272 }
00273 
00274 static LRESULT TAB_SetCurFocus (TAB_INFO *infoPtr, INT iItem)
00275 {
00276   TRACE("(%p %d)\n", infoPtr, iItem);
00277 
00278   if (iItem < 0) {
00279       infoPtr->uFocus = -1;
00280       if (infoPtr->iSelected != -1) {
00281           infoPtr->iSelected = -1;
00282           TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
00283           TAB_InvalidateTabArea(infoPtr);
00284       }
00285   }
00286   else if (iItem < infoPtr->uNumItem) {
00287     if (infoPtr->dwStyle & TCS_BUTTONS) {
00288       /* set focus to new item, leave selection as is */
00289       if (infoPtr->uFocus != iItem) {
00290         INT prev_focus = infoPtr->uFocus;
00291         RECT r;
00292 
00293         infoPtr->uFocus = iItem;
00294 
00295         if (prev_focus != infoPtr->iSelected) {
00296           if (TAB_InternalGetItemRect(infoPtr, prev_focus, &r, NULL))
00297             InvalidateRect(infoPtr->hwnd, &r, FALSE);
00298         }
00299 
00300         if (TAB_InternalGetItemRect(infoPtr, iItem, &r, NULL))
00301             InvalidateRect(infoPtr->hwnd, &r, FALSE);
00302 
00303         TAB_SendSimpleNotify(infoPtr, TCN_FOCUSCHANGE);
00304       }
00305     } else {
00306       INT oldFocus = infoPtr->uFocus;
00307       if (infoPtr->iSelected != iItem || oldFocus == -1 ) {
00308         infoPtr->uFocus = iItem;
00309         if (oldFocus != -1) {
00310           if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))  {
00311             infoPtr->iSelected = iItem;
00312             TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
00313           }
00314           else
00315             infoPtr->iSelected = iItem;
00316           TAB_EnsureSelectionVisible(infoPtr);
00317           TAB_InvalidateTabArea(infoPtr);
00318         }
00319       }
00320     }
00321   }
00322   return 0;
00323 }
00324 
00325 static inline LRESULT
00326 TAB_SetToolTips (TAB_INFO *infoPtr, HWND hwndToolTip)
00327 {
00328     TRACE("%p %p\n", infoPtr, hwndToolTip);
00329     infoPtr->hwndToolTip = hwndToolTip;
00330     return 0;
00331 }
00332 
00333 static inline LRESULT
00334 TAB_SetPadding (TAB_INFO *infoPtr, LPARAM lParam)
00335 {
00336     TRACE("(%p %d %d)\n", infoPtr, LOWORD(lParam), HIWORD(lParam));
00337     infoPtr->uHItemPadding_s = LOWORD(lParam);
00338     infoPtr->uVItemPadding_s = HIWORD(lParam);
00339 
00340     return 0;
00341 }
00342 
00343 /******************************************************************************
00344  * TAB_InternalGetItemRect
00345  *
00346  * This method will calculate the rectangle representing a given tab item in
00347  * client coordinates. This method takes scrolling into account.
00348  *
00349  * This method returns TRUE if the item is visible in the window and FALSE
00350  * if it is completely outside the client area.
00351  */
00352 static BOOL TAB_InternalGetItemRect(
00353   const TAB_INFO* infoPtr,
00354   INT         itemIndex,
00355   RECT*       itemRect,
00356   RECT*       selectedRect)
00357 {
00358   RECT tmpItemRect,clientRect;
00359 
00360   /* Perform a sanity check and a trivial visibility check. */
00361   if ( (infoPtr->uNumItem <= 0) ||
00362        (itemIndex >= infoPtr->uNumItem) ||
00363        (!(((infoPtr->dwStyle & TCS_MULTILINE) || (infoPtr->dwStyle & TCS_VERTICAL))) &&
00364          (itemIndex < infoPtr->leftmostVisible)))
00365     {
00366         TRACE("Not Visible\n");
00367         /* need to initialize these to empty rects */
00368         if (itemRect)
00369         {
00370             memset(itemRect,0,sizeof(RECT));
00371             itemRect->bottom = infoPtr->tabHeight;
00372         }
00373         if (selectedRect)
00374             memset(selectedRect,0,sizeof(RECT));
00375         return FALSE;
00376     }
00377 
00378   /*
00379    * Avoid special cases in this procedure by assigning the "out"
00380    * parameters if the caller didn't supply them
00381    */
00382   if (itemRect == NULL)
00383     itemRect = &tmpItemRect;
00384 
00385   /* Retrieve the unmodified item rect. */
00386   *itemRect = TAB_GetItem(infoPtr,itemIndex)->rect;
00387 
00388   /* calculate the times bottom and top based on the row */
00389   GetClientRect(infoPtr->hwnd, &clientRect);
00390 
00391   if ((infoPtr->dwStyle & TCS_BOTTOM) && (infoPtr->dwStyle & TCS_VERTICAL))
00392   {
00393     itemRect->right  = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * infoPtr->tabHeight -
00394                        ((infoPtr->dwStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
00395     itemRect->left   = itemRect->right - infoPtr->tabHeight;
00396   }
00397   else if (infoPtr->dwStyle & TCS_VERTICAL)
00398   {
00399     itemRect->left   = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * infoPtr->tabHeight +
00400                        ((infoPtr->dwStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
00401     itemRect->right  = itemRect->left + infoPtr->tabHeight;
00402   }
00403   else if (infoPtr->dwStyle & TCS_BOTTOM)
00404   {
00405     itemRect->bottom = clientRect.bottom - itemRect->top * infoPtr->tabHeight -
00406                        ((infoPtr->dwStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
00407     itemRect->top    = itemRect->bottom - infoPtr->tabHeight;
00408   }
00409   else /* not TCS_BOTTOM and not TCS_VERTICAL */
00410   {
00411     itemRect->top    = clientRect.top + itemRect->top * infoPtr->tabHeight +
00412                        ((infoPtr->dwStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
00413     itemRect->bottom = itemRect->top + infoPtr->tabHeight;
00414  }
00415 
00416   /*
00417    * "scroll" it to make sure the item at the very left of the
00418    * tab control is the leftmost visible tab.
00419    */
00420   if(infoPtr->dwStyle & TCS_VERTICAL)
00421   {
00422     OffsetRect(itemRect,
00423          0,
00424          -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.top);
00425 
00426     /*
00427      * Move the rectangle so the first item is slightly offset from
00428      * the bottom of the tab control.
00429      */
00430     OffsetRect(itemRect,
00431          0,
00432          SELECTED_TAB_OFFSET);
00433 
00434   } else
00435   {
00436     OffsetRect(itemRect,
00437          -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.left,
00438          0);
00439 
00440     /*
00441      * Move the rectangle so the first item is slightly offset from
00442      * the left of the tab control.
00443      */
00444     OffsetRect(itemRect,
00445          SELECTED_TAB_OFFSET,
00446          0);
00447   }
00448   TRACE("item %d tab h=%d, rect=(%s)\n",
00449         itemIndex, infoPtr->tabHeight, wine_dbgstr_rect(itemRect));
00450 
00451   /* Now, calculate the position of the item as if it were selected. */
00452   if (selectedRect!=NULL)
00453   {
00454     CopyRect(selectedRect, itemRect);
00455 
00456     /* The rectangle of a selected item is a bit wider. */
00457     if(infoPtr->dwStyle & TCS_VERTICAL)
00458       InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
00459     else
00460       InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
00461 
00462     /* If it also a bit higher. */
00463     if ((infoPtr->dwStyle & TCS_BOTTOM) && (infoPtr->dwStyle & TCS_VERTICAL))
00464     {
00465       selectedRect->left   -= 2; /* the border is thicker on the right */
00466       selectedRect->right  += SELECTED_TAB_OFFSET;
00467     }
00468     else if (infoPtr->dwStyle & TCS_VERTICAL)
00469     {
00470       selectedRect->left   -= SELECTED_TAB_OFFSET;
00471       selectedRect->right  += 1;
00472     }
00473     else if (infoPtr->dwStyle & TCS_BOTTOM)
00474     {
00475       selectedRect->bottom += SELECTED_TAB_OFFSET;
00476     }
00477     else /* not TCS_BOTTOM and not TCS_VERTICAL */
00478     {
00479       selectedRect->top    -= SELECTED_TAB_OFFSET;
00480       selectedRect->bottom -= 1;
00481     }
00482   }
00483 
00484   /* Check for visibility */
00485   if (infoPtr->dwStyle & TCS_VERTICAL)
00486     return (itemRect->top < clientRect.bottom) && (itemRect->bottom > clientRect.top);
00487   else
00488     return (itemRect->left < clientRect.right) && (itemRect->right > clientRect.left);
00489 }
00490 
00491 static inline BOOL
00492 TAB_GetItemRect(const TAB_INFO *infoPtr, INT item, RECT *rect)
00493 {
00494   TRACE("(%p, %d, %p)\n", infoPtr, item, rect);
00495   return TAB_InternalGetItemRect(infoPtr, item, rect, NULL);
00496 }
00497 
00498 /******************************************************************************
00499  * TAB_KeyDown
00500  *
00501  * This method is called to handle keyboard input
00502  */
00503 static LRESULT TAB_KeyDown(TAB_INFO* infoPtr, WPARAM keyCode, LPARAM lParam)
00504 {
00505   INT newItem = -1;
00506   NMTCKEYDOWN nm;
00507 
00508   /* TCN_KEYDOWN notification sent always */
00509   nm.hdr.hwndFrom = infoPtr->hwnd;
00510   nm.hdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID);
00511   nm.hdr.code = TCN_KEYDOWN;
00512   nm.wVKey = keyCode;
00513   nm.flags = lParam;
00514   SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nm.hdr.idFrom, (LPARAM)&nm);
00515 
00516   switch (keyCode)
00517   {
00518     case VK_LEFT:
00519       newItem = infoPtr->uFocus - 1;
00520       break;
00521     case VK_RIGHT:
00522       newItem = infoPtr->uFocus + 1;
00523       break;
00524   }
00525 
00526   /* If we changed to a valid item, change focused item */
00527   if (newItem >= 0 && newItem < infoPtr->uNumItem && infoPtr->uFocus != newItem)
00528       TAB_SetCurFocus(infoPtr, newItem);
00529 
00530   return 0;
00531 }
00532 
00533 /*
00534  * WM_KILLFOCUS handler
00535  */
00536 static void TAB_KillFocus(TAB_INFO *infoPtr)
00537 {
00538   /* clear current focused item back to selected for TCS_BUTTONS */
00539   if ((infoPtr->dwStyle & TCS_BUTTONS) && (infoPtr->uFocus != infoPtr->iSelected))
00540   {
00541     RECT r;
00542 
00543     if (TAB_InternalGetItemRect(infoPtr, infoPtr->uFocus, &r, NULL))
00544       InvalidateRect(infoPtr->hwnd, &r, FALSE);
00545 
00546     infoPtr->uFocus = infoPtr->iSelected;
00547   }
00548 }
00549 
00550 /******************************************************************************
00551  * TAB_FocusChanging
00552  *
00553  * This method is called whenever the focus goes in or out of this control
00554  * it is used to update the visual state of the control.
00555  */
00556 static void TAB_FocusChanging(const TAB_INFO *infoPtr)
00557 {
00558   RECT      selectedRect;
00559   BOOL      isVisible;
00560 
00561   /*
00562    * Get the rectangle for the item.
00563    */
00564   isVisible = TAB_InternalGetItemRect(infoPtr,
00565                       infoPtr->uFocus,
00566                       NULL,
00567                       &selectedRect);
00568 
00569   /*
00570    * If the rectangle is not completely invisible, invalidate that
00571    * portion of the window.
00572    */
00573   if (isVisible)
00574   {
00575     TRACE("invalidate (%s)\n", wine_dbgstr_rect(&selectedRect));
00576     InvalidateRect(infoPtr->hwnd, &selectedRect, TRUE);
00577   }
00578 }
00579 
00580 static INT TAB_InternalHitTest (const TAB_INFO *infoPtr, POINT pt, UINT *flags)
00581 {
00582   RECT rect;
00583   INT iCount;
00584 
00585   for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
00586   {
00587     TAB_InternalGetItemRect(infoPtr, iCount, &rect, NULL);
00588 
00589     if (PtInRect(&rect, pt))
00590     {
00591       *flags = TCHT_ONITEM;
00592       return iCount;
00593     }
00594   }
00595 
00596   *flags = TCHT_NOWHERE;
00597   return -1;
00598 }
00599 
00600 static inline LRESULT
00601 TAB_HitTest (const TAB_INFO *infoPtr, LPTCHITTESTINFO lptest)
00602 {
00603   TRACE("(%p, %p)\n", infoPtr, lptest);
00604   return TAB_InternalHitTest (infoPtr, lptest->pt, &lptest->flags);
00605 }
00606 
00607 /******************************************************************************
00608  * TAB_NCHitTest
00609  *
00610  * Napster v2b5 has a tab control for its main navigation which has a client
00611  * area that covers the whole area of the dialog pages.
00612  * That's why it receives all msgs for that area and the underlying dialog ctrls
00613  * are dead.
00614  * So I decided that we should handle WM_NCHITTEST here and return
00615  * HTTRANSPARENT if we don't hit the tab control buttons.
00616  * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
00617  * doesn't do it that way. Maybe depends on tab control styles ?
00618  */
00619 static inline LRESULT
00620 TAB_NCHitTest (const TAB_INFO *infoPtr, LPARAM lParam)
00621 {
00622   POINT pt;
00623   UINT dummyflag;
00624 
00625   pt.x = (short)LOWORD(lParam);
00626   pt.y = (short)HIWORD(lParam);
00627   ScreenToClient(infoPtr->hwnd, &pt);
00628 
00629   if (TAB_InternalHitTest(infoPtr, pt, &dummyflag) == -1)
00630     return HTTRANSPARENT;
00631   else
00632     return HTCLIENT;
00633 }
00634 
00635 static LRESULT
00636 TAB_LButtonDown (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
00637 {
00638   POINT pt;
00639   INT newItem;
00640   UINT dummy;
00641 
00642   if (infoPtr->hwndToolTip)
00643     TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
00644             WM_LBUTTONDOWN, wParam, lParam);
00645 
00646   if (!(infoPtr->dwStyle & TCS_FOCUSNEVER)) {
00647     SetFocus (infoPtr->hwnd);
00648   }
00649 
00650   if (infoPtr->hwndToolTip)
00651     TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
00652             WM_LBUTTONDOWN, wParam, lParam);
00653 
00654   pt.x = (short)LOWORD(lParam);
00655   pt.y = (short)HIWORD(lParam);
00656 
00657   newItem = TAB_InternalHitTest (infoPtr, pt, &dummy);
00658 
00659   TRACE("On Tab, item %d\n", newItem);
00660 
00661   if ((newItem != -1) && (infoPtr->iSelected != newItem))
00662   {
00663     if ((infoPtr->dwStyle & TCS_BUTTONS) && (infoPtr->dwStyle & TCS_MULTISELECT) &&
00664         (wParam & MK_CONTROL))
00665     {
00666       RECT r;
00667 
00668       /* toggle multiselection */
00669       TAB_GetItem(infoPtr, newItem)->dwState ^= TCIS_BUTTONPRESSED;
00670       if (TAB_InternalGetItemRect (infoPtr, newItem, &r, NULL))
00671         InvalidateRect (infoPtr->hwnd, &r, TRUE);
00672     }
00673     else
00674     {
00675       INT i;
00676       BOOL pressed = FALSE;
00677 
00678       /* any button pressed ? */
00679       for (i = 0; i < infoPtr->uNumItem; i++)
00680         if ((TAB_GetItem (infoPtr, i)->dwState & TCIS_BUTTONPRESSED) &&
00681             (infoPtr->iSelected != i))
00682         {
00683           pressed = TRUE;
00684           break;
00685         }
00686 
00687       if (TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
00688         return 0;
00689 
00690       if (pressed)
00691         TAB_DeselectAll (infoPtr, FALSE);
00692       else
00693         TAB_SetCurSel(infoPtr, newItem);
00694 
00695       TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
00696     }
00697   }
00698 
00699   return 0;
00700 }
00701 
00702 static inline LRESULT
00703 TAB_LButtonUp (const TAB_INFO *infoPtr)
00704 {
00705   TAB_SendSimpleNotify(infoPtr, NM_CLICK);
00706 
00707   return 0;
00708 }
00709 
00710 static inline void
00711 TAB_RButtonUp (const TAB_INFO *infoPtr)
00712 {
00713   TAB_SendSimpleNotify(infoPtr, NM_RCLICK);
00714 }
00715 
00716 /******************************************************************************
00717  * TAB_DrawLoneItemInterior
00718  *
00719  * This calls TAB_DrawItemInterior.  However, TAB_DrawItemInterior is normally
00720  * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
00721  * up the device context and font.  This routine does the same setup but
00722  * only calls TAB_DrawItemInterior for the single specified item.
00723  */
00724 static void
00725 TAB_DrawLoneItemInterior(const TAB_INFO* infoPtr, int iItem)
00726 {
00727   HDC hdc = GetDC(infoPtr->hwnd);
00728   RECT r, rC;
00729 
00730   /* Clip UpDown control to not draw over it */
00731   if (infoPtr->needsScrolling)
00732   {
00733     GetWindowRect(infoPtr->hwnd, &rC);
00734     GetWindowRect(infoPtr->hwndUpDown, &r);
00735     ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top);
00736   }
00737   TAB_DrawItemInterior(infoPtr, hdc, iItem, NULL);
00738   ReleaseDC(infoPtr->hwnd, hdc);
00739 }
00740 
00741 /* update a tab after hottracking - invalidate it or just redraw the interior,
00742  * based on whether theming is used or not */
00743 static inline void hottrack_refresh(const TAB_INFO *infoPtr, int tabIndex)
00744 {
00745     if (tabIndex == -1) return;
00746 
00747     if (GetWindowTheme (infoPtr->hwnd))
00748     {
00749         RECT rect;
00750         TAB_InternalGetItemRect(infoPtr, tabIndex, &rect, NULL);
00751         InvalidateRect (infoPtr->hwnd, &rect, FALSE);
00752     }
00753     else
00754         TAB_DrawLoneItemInterior(infoPtr, tabIndex);
00755 }
00756 
00757 /******************************************************************************
00758  * TAB_HotTrackTimerProc
00759  *
00760  * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
00761  * timer is setup so we can check if the mouse is moved out of our window.
00762  * (We don't get an event when the mouse leaves, the mouse-move events just
00763  * stop being delivered to our window and just start being delivered to
00764  * another window.)  This function is called when the timer triggers so
00765  * we can check if the mouse has left our window.  If so, we un-highlight
00766  * the hot-tracked tab.
00767  */
00768 static void CALLBACK
00769 TAB_HotTrackTimerProc
00770   (
00771   HWND hwnd,    /* handle of window for timer messages */
00772   UINT uMsg,    /* WM_TIMER message */
00773   UINT_PTR idEvent, /* timer identifier */
00774   DWORD dwTime  /* current system time */
00775   )
00776 {
00777   TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
00778 
00779   if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
00780   {
00781     POINT pt;
00782 
00783     /*
00784     ** If we can't get the cursor position, or if the cursor is outside our
00785     ** window, we un-highlight the hot-tracked tab.  Note that the cursor is
00786     ** "outside" even if it is within our bounding rect if another window
00787     ** overlaps.  Note also that the case where the cursor stayed within our
00788     ** window but has moved off the hot-tracked tab will be handled by the
00789     ** WM_MOUSEMOVE event.
00790     */
00791     if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
00792     {
00793       /* Redraw iHotTracked to look normal */
00794       INT iRedraw = infoPtr->iHotTracked;
00795       infoPtr->iHotTracked = -1;
00796       hottrack_refresh (infoPtr, iRedraw);
00797 
00798       /* Kill this timer */
00799       KillTimer(hwnd, TAB_HOTTRACK_TIMER);
00800     }
00801   }
00802 }
00803 
00804 /******************************************************************************
00805  * TAB_RecalcHotTrack
00806  *
00807  * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
00808  * should be highlighted.  This function determines which tab in a tab control,
00809  * if any, is under the mouse and records that information.  The caller may
00810  * supply output parameters to receive the item number of the tab item which
00811  * was highlighted but isn't any longer and of the tab item which is now
00812  * highlighted but wasn't previously.  The caller can use this information to
00813  * selectively redraw those tab items.
00814  *
00815  * If the caller has a mouse position, it can supply it through the pos
00816  * parameter.  For example, TAB_MouseMove does this.  Otherwise, the caller
00817  * supplies NULL and this function determines the current mouse position
00818  * itself.
00819  */
00820 static void
00821 TAB_RecalcHotTrack
00822   (
00823   TAB_INFO*       infoPtr,
00824   const LPARAM*   pos,
00825   int*            out_redrawLeave,
00826   int*            out_redrawEnter
00827   )
00828 {
00829   int item = -1;
00830 
00831 
00832   if (out_redrawLeave != NULL)
00833     *out_redrawLeave = -1;
00834   if (out_redrawEnter != NULL)
00835     *out_redrawEnter = -1;
00836 
00837   if ((infoPtr->dwStyle & TCS_HOTTRACK) || GetWindowTheme(infoPtr->hwnd))
00838   {
00839     POINT pt;
00840     UINT  flags;
00841 
00842     if (pos == NULL)
00843     {
00844       GetCursorPos(&pt);
00845       ScreenToClient(infoPtr->hwnd, &pt);
00846     }
00847     else
00848     {
00849       pt.x = (short)LOWORD(*pos);
00850       pt.y = (short)HIWORD(*pos);
00851     }
00852 
00853     item = TAB_InternalHitTest(infoPtr, pt, &flags);
00854   }
00855 
00856   if (item != infoPtr->iHotTracked)
00857   {
00858     if (infoPtr->iHotTracked >= 0)
00859     {
00860       /* Mark currently hot-tracked to be redrawn to look normal */
00861       if (out_redrawLeave != NULL)
00862         *out_redrawLeave = infoPtr->iHotTracked;
00863 
00864       if (item < 0)
00865       {
00866         /* Kill timer which forces recheck of mouse pos */
00867         KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
00868       }
00869     }
00870     else
00871     {
00872       /* Start timer so we recheck mouse pos */
00873       UINT timerID = SetTimer
00874         (
00875         infoPtr->hwnd,
00876         TAB_HOTTRACK_TIMER,
00877         TAB_HOTTRACK_TIMER_INTERVAL,
00878         TAB_HotTrackTimerProc
00879         );
00880 
00881       if (timerID == 0)
00882         return; /* Hot tracking not available */
00883     }
00884 
00885     infoPtr->iHotTracked = item;
00886 
00887     if (item >= 0)
00888     {
00889     /* Mark new hot-tracked to be redrawn to look highlighted */
00890       if (out_redrawEnter != NULL)
00891         *out_redrawEnter = item;
00892     }
00893   }
00894 }
00895 
00896 /******************************************************************************
00897  * TAB_MouseMove
00898  *
00899  * Handles the mouse-move event.  Updates tooltips.  Updates hot-tracking.
00900  */
00901 static LRESULT
00902 TAB_MouseMove (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
00903 {
00904   int redrawLeave;
00905   int redrawEnter;
00906 
00907   if (infoPtr->hwndToolTip)
00908     TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
00909             WM_LBUTTONDOWN, wParam, lParam);
00910 
00911   /* Determine which tab to highlight.  Redraw tabs which change highlight
00912   ** status. */
00913   TAB_RecalcHotTrack(infoPtr, &lParam, &redrawLeave, &redrawEnter);
00914 
00915   hottrack_refresh (infoPtr, redrawLeave);
00916   hottrack_refresh (infoPtr, redrawEnter);
00917 
00918   return 0;
00919 }
00920 
00921 /******************************************************************************
00922  * TAB_AdjustRect
00923  *
00924  * Calculates the tab control's display area given the window rectangle or
00925  * the window rectangle given the requested display rectangle.
00926  */
00927 static LRESULT TAB_AdjustRect(const TAB_INFO *infoPtr, WPARAM fLarger, LPRECT prc)
00928 {
00929     LONG *iRightBottom, *iLeftTop;
00930 
00931     TRACE ("hwnd=%p fLarger=%ld (%s)\n", infoPtr->hwnd, fLarger,
00932            wine_dbgstr_rect(prc));
00933 
00934     if (!prc) return -1;
00935 
00936     if(infoPtr->dwStyle & TCS_VERTICAL)
00937     {
00938     iRightBottom = &(prc->right);
00939     iLeftTop     = &(prc->left);
00940     }
00941     else
00942     {
00943     iRightBottom = &(prc->bottom);
00944     iLeftTop     = &(prc->top);
00945     }
00946 
00947     if (fLarger) /* Go from display rectangle */
00948     {
00949         /* Add the height of the tabs. */
00950     if (infoPtr->dwStyle & TCS_BOTTOM)
00951         *iRightBottom += infoPtr->tabHeight * infoPtr->uNumRows;
00952     else
00953         *iLeftTop -= infoPtr->tabHeight * infoPtr->uNumRows +
00954              ((infoPtr->dwStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
00955 
00956     /* Inflate the rectangle for the padding */
00957     InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY); 
00958 
00959     /* Inflate for the border */
00960     InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEY);
00961     }
00962     else /* Go from window rectangle. */
00963     {
00964     /* Deflate the rectangle for the border */
00965     InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEY);
00966 
00967     /* Deflate the rectangle for the padding */
00968     InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
00969 
00970     /* Remove the height of the tabs. */
00971     if (infoPtr->dwStyle & TCS_BOTTOM)
00972         *iRightBottom -= infoPtr->tabHeight * infoPtr->uNumRows;
00973     else
00974         *iLeftTop += (infoPtr->tabHeight) * infoPtr->uNumRows +
00975              ((infoPtr->dwStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
00976     }
00977 
00978   return 0;
00979 }
00980 
00981 /******************************************************************************
00982  * TAB_OnHScroll
00983  *
00984  * This method will handle the notification from the scroll control and
00985  * perform the scrolling operation on the tab control.
00986  */
00987 static LRESULT TAB_OnHScroll(TAB_INFO *infoPtr, int nScrollCode, int nPos)
00988 {
00989   if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
00990   {
00991      if(nPos < infoPtr->leftmostVisible)
00992         infoPtr->leftmostVisible--;
00993      else
00994         infoPtr->leftmostVisible++;
00995 
00996      TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
00997      TAB_InvalidateTabArea(infoPtr);
00998      SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
00999                    MAKELONG(infoPtr->leftmostVisible, 0));
01000    }
01001 
01002    return 0;
01003 }
01004 
01005 /******************************************************************************
01006  * TAB_SetupScrolling
01007  *
01008  * This method will check the current scrolling state and make sure the
01009  * scrolling control is displayed (or not).
01010  */
01011 static void TAB_SetupScrolling(
01012   TAB_INFO*   infoPtr,
01013   const RECT* clientRect)
01014 {
01015   static const WCHAR emptyW[] = { 0 };
01016   INT maxRange = 0;
01017 
01018   if (infoPtr->needsScrolling)
01019   {
01020     RECT controlPos;
01021     INT vsize, tabwidth;
01022 
01023     /*
01024      * Calculate the position of the scroll control.
01025      */
01026     if(infoPtr->dwStyle & TCS_VERTICAL)
01027     {
01028       controlPos.right = clientRect->right;
01029       controlPos.left  = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
01030 
01031       if (infoPtr->dwStyle & TCS_BOTTOM)
01032       {
01033         controlPos.top    = clientRect->bottom - infoPtr->tabHeight;
01034         controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
01035       }
01036       else
01037       {
01038         controlPos.bottom = clientRect->top + infoPtr->tabHeight;
01039         controlPos.top    = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
01040       }
01041     }
01042     else
01043     {
01044       controlPos.right = clientRect->right;
01045       controlPos.left  = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
01046 
01047       if (infoPtr->dwStyle & TCS_BOTTOM)
01048       {
01049         controlPos.top    = clientRect->bottom - infoPtr->tabHeight;
01050         controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
01051       }
01052       else
01053       {
01054         controlPos.bottom = clientRect->top + infoPtr->tabHeight;
01055         controlPos.top    = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
01056       }
01057     }
01058 
01059     /*
01060      * If we don't have a scroll control yet, we want to create one.
01061      * If we have one, we want to make sure it's positioned properly.
01062      */
01063     if (infoPtr->hwndUpDown==0)
01064     {
01065       infoPtr->hwndUpDown = CreateWindowW(UPDOWN_CLASSW, emptyW,
01066                       WS_VISIBLE | WS_CHILD | UDS_HORZ,
01067                       controlPos.left, controlPos.top,
01068                       controlPos.right - controlPos.left,
01069                       controlPos.bottom - controlPos.top,
01070                       infoPtr->hwnd, NULL, NULL, NULL);
01071     }
01072     else
01073     {
01074       SetWindowPos(infoPtr->hwndUpDown,
01075            NULL,
01076            controlPos.left, controlPos.top,
01077            controlPos.right - controlPos.left,
01078            controlPos.bottom - controlPos.top,
01079            SWP_SHOWWINDOW | SWP_NOZORDER);
01080     }
01081 
01082     /* Now calculate upper limit of the updown control range.
01083      * We do this by calculating how many tabs will be offscreen when the
01084      * last tab is visible.
01085      */
01086     if(infoPtr->uNumItem)
01087     {
01088        vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
01089        maxRange = infoPtr->uNumItem;
01090        tabwidth = TAB_GetItem(infoPtr, infoPtr->uNumItem - 1)->rect.right;
01091 
01092        for(; maxRange > 0; maxRange--)
01093        {
01094           if(tabwidth - TAB_GetItem(infoPtr,maxRange - 1)->rect.left > vsize)
01095              break;
01096        }
01097 
01098        if(maxRange == infoPtr->uNumItem)
01099           maxRange--;
01100     }
01101   }
01102   else
01103   {
01104     /* If we once had a scroll control... hide it */
01105     if (infoPtr->hwndUpDown)
01106       ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
01107   }
01108   if (infoPtr->hwndUpDown)
01109      SendMessageW(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
01110 }
01111 
01112 /******************************************************************************
01113  * TAB_SetItemBounds
01114  *
01115  * This method will calculate the position rectangles of all the items in the
01116  * control. The rectangle calculated starts at 0 for the first item in the
01117  * list and ignores scrolling and selection.
01118  * It also uses the current font to determine the height of the tab row and
01119  * it checks if all the tabs fit in the client area of the window. If they
01120  * don't, a scrolling control is added.
01121  */
01122 static void TAB_SetItemBounds (TAB_INFO *infoPtr)
01123 {
01124   TEXTMETRICW fontMetrics;
01125   UINT        curItem;
01126   INT         curItemLeftPos;
01127   INT         curItemRowCount;
01128   HFONT       hFont, hOldFont;
01129   HDC         hdc;
01130   RECT        clientRect;
01131   INT         iTemp;
01132   RECT*       rcItem;
01133   INT         iIndex;
01134   INT         icon_width = 0;
01135 
01136   /*
01137    * We need to get text information so we need a DC and we need to select
01138    * a font.
01139    */
01140   hdc = GetDC(infoPtr->hwnd);
01141 
01142   hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
01143   hOldFont = SelectObject (hdc, hFont);
01144 
01145   /*
01146    * We will base the rectangle calculations on the client rectangle
01147    * of the control.
01148    */
01149   GetClientRect(infoPtr->hwnd, &clientRect);
01150 
01151   /* if TCS_VERTICAL then swap the height and width so this code places the
01152      tabs along the top of the rectangle and we can just rotate them after
01153      rather than duplicate all of the below code */
01154   if(infoPtr->dwStyle & TCS_VERTICAL)
01155   {
01156      iTemp = clientRect.bottom;
01157      clientRect.bottom = clientRect.right;
01158      clientRect.right = iTemp;
01159   }
01160 
01161   /* Now use hPadding and vPadding */
01162   infoPtr->uHItemPadding = infoPtr->uHItemPadding_s;
01163   infoPtr->uVItemPadding = infoPtr->uVItemPadding_s;
01164   
01165   /* The leftmost item will be "0" aligned */
01166   curItemLeftPos = 0;
01167   curItemRowCount = infoPtr->uNumItem ? 1 : 0;
01168 
01169   if (!(infoPtr->fHeightSet))
01170   {
01171     int item_height;
01172     INT icon_height = 0, cx;
01173 
01174     /* Use the current font to determine the height of a tab. */
01175     GetTextMetricsW(hdc, &fontMetrics);
01176 
01177     /* Get the icon height */
01178     if (infoPtr->himl)
01179       ImageList_GetIconSize(infoPtr->himl, &cx, &icon_height);
01180 
01181     /* Take the highest between font or icon */
01182     if (fontMetrics.tmHeight > icon_height)
01183       item_height = fontMetrics.tmHeight + 2;
01184     else
01185       item_height = icon_height;
01186 
01187     /*
01188      * Make sure there is enough space for the letters + icon + growing the
01189      * selected item + extra space for the selected item.
01190      */
01191     infoPtr->tabHeight = item_height + 
01192                      ((infoPtr->dwStyle & TCS_BUTTONS) ? 2 : 1) *
01193                           infoPtr->uVItemPadding;
01194 
01195     TRACE("tabH=%d, tmH=%d, iconh=%d\n",
01196       infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
01197   }
01198 
01199   TRACE("client right=%d\n", clientRect.right);
01200 
01201   /* Get the icon width */
01202   if (infoPtr->himl)
01203   {
01204     INT cy;
01205 
01206     ImageList_GetIconSize(infoPtr->himl, &icon_width, &cy);
01207 
01208     if (infoPtr->dwStyle & TCS_FIXEDWIDTH)
01209       icon_width += 4;
01210     else
01211       /* Add padding if icon is present */
01212       icon_width += infoPtr->uHItemPadding;
01213   }
01214 
01215   for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
01216   {
01217     TAB_ITEM *curr = TAB_GetItem(infoPtr, curItem);
01218     
01219     /* Set the leftmost position of the tab. */
01220     curr->rect.left = curItemLeftPos;
01221 
01222     if (infoPtr->dwStyle & TCS_FIXEDWIDTH)
01223     {
01224       curr->rect.right = curr->rect.left +
01225         max(infoPtr->tabWidth, icon_width);
01226     }
01227     else if (!curr->pszText)
01228     {
01229       /* If no text use minimum tab width including padding. */
01230       if (infoPtr->tabMinWidth < 0)
01231         curr->rect.right = curr->rect.left + GET_DEFAULT_MIN_TAB_WIDTH(infoPtr);
01232       else
01233       {
01234         curr->rect.right = curr->rect.left + infoPtr->tabMinWidth;
01235 
01236         /* Add extra padding if icon is present */
01237         if (infoPtr->himl && infoPtr->tabMinWidth > 0 && infoPtr->tabMinWidth < DEFAULT_MIN_TAB_WIDTH
01238             && infoPtr->uHItemPadding > 1)
01239           curr->rect.right += EXTRA_ICON_PADDING * (infoPtr->uHItemPadding-1);
01240       }
01241     }
01242     else
01243     {
01244       int tabwidth;
01245       SIZE size;
01246       /* Calculate how wide the tab is depending on the text it contains */
01247       GetTextExtentPoint32W(hdc, curr->pszText,
01248                             lstrlenW(curr->pszText), &size);
01249 
01250       tabwidth = size.cx + icon_width + 2 * infoPtr->uHItemPadding;
01251 
01252       if (infoPtr->tabMinWidth < 0)
01253         tabwidth = max(tabwidth, GET_DEFAULT_MIN_TAB_WIDTH(infoPtr));
01254       else
01255         tabwidth = max(tabwidth, infoPtr->tabMinWidth);
01256 
01257       curr->rect.right = curr->rect.left + tabwidth;
01258       TRACE("for <%s>, l,r=%d,%d\n",
01259       debugstr_w(curr->pszText), curr->rect.left, curr->rect.right);
01260     }
01261 
01262     /*
01263      * Check if this is a multiline tab control and if so
01264      * check to see if we should wrap the tabs
01265      *
01266      * Wrap all these tabs. We will arrange them evenly later.
01267      *
01268      */
01269 
01270     if (((infoPtr->dwStyle & TCS_MULTILINE) || (infoPtr->dwStyle & TCS_VERTICAL)) &&
01271         (curr->rect.right > 
01272     (clientRect.right - CONTROL_BORDER_SIZEX - DISPLAY_AREA_PADDINGX)))
01273     {
01274         curr->rect.right -= curr->rect.left;
01275 
01276     curr->rect.left = 0;
01277         curItemRowCount++;
01278     TRACE("wrapping <%s>, l,r=%d,%d\n", debugstr_w(curr->pszText),
01279         curr->rect.left, curr->rect.right);
01280     }
01281 
01282     curr->rect.bottom = 0;
01283     curr->rect.top = curItemRowCount - 1;
01284 
01285     TRACE("Rect: %s\n", wine_dbgstr_rect(&curr->rect));
01286 
01287     /*
01288      * The leftmost position of the next item is the rightmost position
01289      * of this one.
01290      */
01291     if (infoPtr->dwStyle & TCS_BUTTONS)
01292     {
01293       curItemLeftPos = curr->rect.right + BUTTON_SPACINGX;
01294       if (infoPtr->dwStyle & TCS_FLATBUTTONS)
01295         curItemLeftPos += FLAT_BTN_SPACINGX;
01296     }
01297     else
01298       curItemLeftPos = curr->rect.right;
01299   }
01300 
01301   if (!((infoPtr->dwStyle & TCS_MULTILINE) || (infoPtr->dwStyle & TCS_VERTICAL)))
01302   {
01303     /*
01304      * Check if we need a scrolling control.
01305      */
01306     infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
01307                                clientRect.right);
01308 
01309     /* Don't need scrolling, then update infoPtr->leftmostVisible */
01310     if(!infoPtr->needsScrolling)
01311       infoPtr->leftmostVisible = 0;
01312   }
01313   else
01314   {
01315     /*
01316      * No scrolling in Multiline or Vertical styles.
01317      */
01318     infoPtr->needsScrolling = FALSE;
01319     infoPtr->leftmostVisible = 0;
01320   }
01321   TAB_SetupScrolling(infoPtr, &clientRect);
01322 
01323   /* Set the number of rows */
01324   infoPtr->uNumRows = curItemRowCount;
01325 
01326   /* Arrange all tabs evenly if style says so */
01327    if (!(infoPtr->dwStyle & TCS_RAGGEDRIGHT) &&
01328        ((infoPtr->dwStyle & TCS_MULTILINE) || (infoPtr->dwStyle & TCS_VERTICAL)) &&
01329        (infoPtr->uNumItem > 0) &&
01330        (infoPtr->uNumRows > 1))
01331    {
01332       INT tabPerRow,remTab,iRow;
01333       UINT iItm;
01334       INT iCount=0;
01335 
01336       /*
01337        * Ok windows tries to even out the rows. place the same
01338        * number of tabs in each row. So lets give that a shot
01339        */
01340 
01341       tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
01342       remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
01343 
01344       for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
01345            iItm<infoPtr->uNumItem;
01346            iItm++,iCount++)
01347       {
01348           /* normalize the current rect */
01349           TAB_ITEM *curr = TAB_GetItem(infoPtr, iItm);
01350  
01351           /* shift the item to the left side of the clientRect */
01352           curr->rect.right -= curr->rect.left;
01353           curr->rect.left = 0;
01354 
01355           TRACE("r=%d, cl=%d, cl.r=%d, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
01356           curr->rect.right, curItemLeftPos, clientRect.right,
01357           iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
01358 
01359           /* if we have reached the maximum number of tabs on this row */
01360           /* move to the next row, reset our current item left position and */
01361           /* the count of items on this row */
01362 
01363       if (infoPtr->dwStyle & TCS_VERTICAL) {
01364           /* Vert: Add the remaining tabs in the *last* remainder rows */
01365           if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
01366           iRow++;
01367           curItemLeftPos = 0;
01368           iCount = 0;
01369           }
01370       } else {
01371           /* Horz: Add the remaining tabs in the *first* remainder rows */
01372           if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
01373           iRow++;
01374           curItemLeftPos = 0;
01375           iCount = 0;
01376           }
01377       }
01378 
01379           /* shift the item to the right to place it as the next item in this row */
01380           curr->rect.left += curItemLeftPos;
01381           curr->rect.right += curItemLeftPos;
01382           curr->rect.top = iRow;
01383           if (infoPtr->dwStyle & TCS_BUTTONS)
01384       {
01385             curItemLeftPos = curr->rect.right + 1;
01386             if (infoPtr->dwStyle & TCS_FLATBUTTONS)
01387           curItemLeftPos += FLAT_BTN_SPACINGX;
01388       }
01389           else
01390             curItemLeftPos = curr->rect.right;
01391 
01392           TRACE("arranging <%s>, l,r=%d,%d, row=%d\n",
01393           debugstr_w(curr->pszText), curr->rect.left,
01394           curr->rect.right, curr->rect.top);
01395       }
01396 
01397       /*
01398        * Justify the rows
01399        */
01400       {
01401     INT widthDiff, iIndexStart=0, iIndexEnd=0;
01402     INT remainder;
01403     INT iCount=0;
01404 
01405         while(iIndexStart < infoPtr->uNumItem)
01406         {
01407           TAB_ITEM *start = TAB_GetItem(infoPtr, iIndexStart);
01408 
01409           /*
01410            * find the index of the row
01411            */
01412           /* find the first item on the next row */
01413           for (iIndexEnd=iIndexStart;
01414               (iIndexEnd < infoPtr->uNumItem) &&
01415           (TAB_GetItem(infoPtr, iIndexEnd)->rect.top ==
01416                 start->rect.top) ;
01417               iIndexEnd++)
01418           /* intentionally blank */;
01419 
01420           /*
01421            * we need to justify these tabs so they fill the whole given
01422            * client area
01423            *
01424            */
01425           /* find the amount of space remaining on this row */
01426           widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
01427             TAB_GetItem(infoPtr, iIndexEnd - 1)->rect.right;
01428 
01429       /* iCount is the number of tab items on this row */
01430       iCount = iIndexEnd - iIndexStart;
01431 
01432       if (iCount > 1)
01433       {
01434         remainder = widthDiff % iCount;
01435         widthDiff = widthDiff / iCount;
01436         /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
01437         for (iIndex=iIndexStart, iCount=0; iIndex < iIndexEnd; iIndex++, iCount++)
01438         {
01439               TAB_ITEM *item = TAB_GetItem(infoPtr, iIndex);
01440 
01441           item->rect.left += iCount * widthDiff;
01442           item->rect.right += (iCount + 1) * widthDiff;
01443 
01444               TRACE("adjusting 1 <%s>, l,r=%d,%d\n",
01445           debugstr_w(item->pszText),
01446           item->rect.left, item->rect.right);
01447 
01448         }
01449         TAB_GetItem(infoPtr, iIndex - 1)->rect.right += remainder;
01450       }
01451       else /* we have only one item on this row, make it take up the entire row */
01452       {
01453         start->rect.left = clientRect.left;
01454         start->rect.right = clientRect.right - 4;
01455 
01456             TRACE("adjusting 2 <%s>, l,r=%d,%d\n",
01457         debugstr_w(start->pszText),
01458         start->rect.left, start->rect.right);
01459 
01460       }
01461 
01462 
01463       iIndexStart = iIndexEnd;
01464     }
01465       }
01466   }
01467 
01468   /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
01469   if(infoPtr->dwStyle & TCS_VERTICAL)
01470   {
01471     RECT rcOriginal;
01472     for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
01473     {
01474       rcItem = &TAB_GetItem(infoPtr, iIndex)->rect;
01475 
01476       rcOriginal = *rcItem;
01477 
01478       /* this is rotating the items by 90 degrees clockwise around the center of the control */
01479       rcItem->top = (rcOriginal.left - clientRect.left);
01480       rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
01481       rcItem->left = rcOriginal.top;
01482       rcItem->right = rcOriginal.bottom;
01483     }
01484   }
01485 
01486   TAB_EnsureSelectionVisible(infoPtr);
01487   TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
01488 
01489   /* Cleanup */
01490   SelectObject (hdc, hOldFont);
01491   ReleaseDC (infoPtr->hwnd, hdc);
01492 }
01493 
01494 
01495 static void
01496 TAB_EraseTabInterior(const TAB_INFO *infoPtr, HDC hdc, INT iItem, const RECT *drawRect)
01497 {
01498     HBRUSH   hbr = CreateSolidBrush (comctl32_color.clrBtnFace);
01499     BOOL     deleteBrush = TRUE;
01500     RECT     rTemp = *drawRect;
01501 
01502     if (infoPtr->dwStyle & TCS_BUTTONS)
01503     {
01504     if (iItem == infoPtr->iSelected)
01505     {
01506         /* Background color */
01507         if (!(infoPtr->dwStyle & TCS_OWNERDRAWFIXED))
01508         {
01509         DeleteObject(hbr);
01510         hbr = GetSysColorBrush(COLOR_SCROLLBAR);
01511 
01512         SetTextColor(hdc, comctl32_color.clr3dFace);
01513         SetBkColor(hdc, comctl32_color.clr3dHilight);
01514 
01515         /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
01516         * we better use 0x55aa bitmap brush to make scrollbar's background
01517         * look different from the window background.
01518         */
01519         if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
01520             hbr = COMCTL32_hPattern55AABrush;
01521 
01522         deleteBrush = FALSE;
01523         }
01524         FillRect(hdc, &rTemp, hbr);
01525     }
01526     else  /* ! selected */
01527     {
01528         if (infoPtr->dwStyle & TCS_FLATBUTTONS)
01529         {
01530         InflateRect(&rTemp, 2, 2);
01531         FillRect(hdc, &rTemp, hbr);
01532         if (iItem == infoPtr->iHotTracked ||
01533                    (iItem != infoPtr->iSelected && iItem == infoPtr->uFocus))
01534             DrawEdge(hdc, &rTemp, BDR_RAISEDINNER, BF_RECT);
01535         }
01536         else
01537         FillRect(hdc, &rTemp, hbr);
01538     }
01539 
01540     }
01541     else /* !TCS_BUTTONS */
01542     {
01543         InflateRect(&rTemp, -2, -2);
01544         if (!GetWindowTheme (infoPtr->hwnd))
01545         FillRect(hdc, &rTemp, hbr);
01546     }
01547 
01548     /* highlighting is drawn on top of previous fills */
01549     if (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED)
01550     {
01551         if (deleteBrush)
01552         {
01553             DeleteObject(hbr);
01554             deleteBrush = FALSE;
01555         }
01556         hbr = GetSysColorBrush(COLOR_HIGHLIGHT);
01557         FillRect(hdc, &rTemp, hbr);
01558     }
01559 
01560     /* Cleanup */
01561     if (deleteBrush) DeleteObject(hbr);
01562 }
01563 
01564 /******************************************************************************
01565  * TAB_DrawItemInterior
01566  *
01567  * This method is used to draw the interior (text and icon) of a single tab
01568  * into the tab control.
01569  */
01570 static void
01571 TAB_DrawItemInterior(const TAB_INFO *infoPtr, HDC hdc, INT iItem, RECT *drawRect)
01572 {
01573   RECT localRect;
01574 
01575   HPEN   htextPen;
01576   HPEN   holdPen;
01577   INT    oldBkMode;
01578   HFONT  hOldFont;
01579   
01580 /*  if (drawRect == NULL) */
01581   {
01582     BOOL isVisible;
01583     RECT itemRect;
01584     RECT selectedRect;
01585 
01586     /*
01587      * Get the rectangle for the item.
01588      */
01589     isVisible = TAB_InternalGetItemRect(infoPtr, iItem, &itemRect, &selectedRect);
01590     if (!isVisible)
01591       return;
01592 
01593     /*
01594      * Make sure drawRect points to something valid; simplifies code.
01595      */
01596     drawRect = &localRect;
01597 
01598     /*
01599      * This logic copied from the part of TAB_DrawItem which draws
01600      * the tab background.  It's important to keep it in sync.  I
01601      * would have liked to avoid code duplication, but couldn't figure
01602      * out how without making spaghetti of TAB_DrawItem.
01603      */
01604     if (iItem == infoPtr->iSelected)
01605       *drawRect = selectedRect;
01606     else
01607       *drawRect = itemRect;
01608         
01609     if (infoPtr->dwStyle & TCS_BUTTONS)
01610     {
01611       if (iItem == infoPtr->iSelected)
01612       {
01613     drawRect->left   += 4;
01614     drawRect->top    += 4;
01615     drawRect->right  -= 4;
01616 
01617     if (infoPtr->dwStyle & TCS_VERTICAL)
01618     {
01619       if (!(infoPtr->dwStyle & TCS_BOTTOM)) drawRect->right  += 1;
01620       drawRect->bottom   -= 4;
01621     }
01622     else
01623     {
01624       if (infoPtr->dwStyle & TCS_BOTTOM)
01625       {
01626         drawRect->top    -= 2;
01627         drawRect->bottom -= 4;
01628       }
01629       else
01630         drawRect->bottom -= 1;
01631     }
01632       }
01633       else
01634       {
01635     drawRect->left   += 2;
01636     drawRect->top    += 2;
01637     drawRect->right  -= 2;
01638     drawRect->bottom -= 2;
01639       }
01640     }
01641     else
01642     {
01643       if ((infoPtr->dwStyle & TCS_VERTICAL) && (infoPtr->dwStyle & TCS_BOTTOM))
01644       {
01645         if (iItem != infoPtr->iSelected)
01646     {
01647       drawRect->left   += 2;
01648       drawRect->top    += 2;
01649       drawRect->bottom -= 2;
01650     }
01651       }
01652       else if (infoPtr->dwStyle & TCS_VERTICAL)
01653       {
01654         if (iItem == infoPtr->iSelected)
01655     {
01656       drawRect->right  += 1;
01657     }
01658     else
01659     {
01660       drawRect->top    += 2;
01661       drawRect->right  -= 2;
01662       drawRect->bottom -= 2;
01663     }
01664       }
01665       else if (infoPtr->dwStyle & TCS_BOTTOM)
01666       {
01667         if (iItem == infoPtr->iSelected)
01668     {
01669       drawRect->top    -= 2;
01670     }
01671     else
01672     {
01673       InflateRect(drawRect, -2, -2);
01674           drawRect->bottom += 2;
01675     }
01676       }
01677       else
01678       {
01679         if (iItem == infoPtr->iSelected)
01680     {
01681       drawRect->bottom += 3;
01682     }
01683     else
01684     {
01685       drawRect->bottom -= 2;
01686       InflateRect(drawRect, -2, 0);
01687     }
01688       }
01689     }
01690   }
01691   TRACE("drawRect=(%s)\n", wine_dbgstr_rect(drawRect));
01692 
01693   /* Clear interior */
01694   TAB_EraseTabInterior (infoPtr, hdc, iItem, drawRect);
01695 
01696   /* Draw the focus rectangle */
01697   if (!(infoPtr->dwStyle & TCS_FOCUSNEVER) &&
01698       (GetFocus() == infoPtr->hwnd) &&
01699       (iItem == infoPtr->uFocus) )
01700   {
01701     RECT rFocus = *drawRect;
01702 
01703     if (!(infoPtr->dwStyle & TCS_BUTTONS)) InflateRect(&rFocus, -3, -3);
01704     if (infoPtr->dwStyle & TCS_BOTTOM && !(infoPtr->dwStyle & TCS_VERTICAL))
01705       rFocus.top -= 3;
01706 
01707     /* focus should stay on selected item for TCS_BUTTONS style */
01708     if (!((infoPtr->dwStyle & TCS_BUTTONS) && (infoPtr->iSelected != iItem)))
01709       DrawFocusRect(hdc, &rFocus);
01710   }
01711 
01712   /*
01713    * Text pen
01714    */
01715   htextPen = CreatePen( PS_SOLID, 1, comctl32_color.clrBtnText );
01716   holdPen  = SelectObject(hdc, htextPen);
01717   hOldFont = SelectObject(hdc, infoPtr->hFont);
01718 
01719   /*
01720    * Setup for text output
01721   */
01722   oldBkMode = SetBkMode(hdc, TRANSPARENT);
01723   if (!GetWindowTheme (infoPtr->hwnd) || (infoPtr->dwStyle & TCS_BUTTONS))
01724   {
01725     if ((infoPtr->dwStyle & TCS_HOTTRACK) && (iItem == infoPtr->iHotTracked) &&
01726         !(infoPtr->dwStyle & TCS_FLATBUTTONS))
01727       SetTextColor(hdc, comctl32_color.clrHighlight);
01728     else if (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED)
01729       SetTextColor(hdc, comctl32_color.clrHighlightText);
01730     else
01731       SetTextColor(hdc, comctl32_color.clrBtnText);
01732   }
01733 
01734   /*
01735    * if owner draw, tell the owner to draw
01736    */
01737   if ((infoPtr->dwStyle & TCS_OWNERDRAWFIXED) && IsWindow(infoPtr->hwndNotify))
01738   {
01739     DRAWITEMSTRUCT dis;
01740     UINT id;
01741 
01742     drawRect->top += 2;
01743     drawRect->right -= 1;
01744     if ( iItem == infoPtr->iSelected )
01745     {
01746         drawRect->right -= 1;
01747         drawRect->left += 1;
01748     }
01749 
01750     id = (UINT)GetWindowLongPtrW( infoPtr->hwnd, GWLP_ID );
01751 
01752     /* fill DRAWITEMSTRUCT */
01753     dis.CtlType    = ODT_TAB;
01754     dis.CtlID      = id;
01755     dis.itemID     = iItem;
01756     dis.itemAction = ODA_DRAWENTIRE;
01757     dis.itemState = 0;
01758     if ( iItem == infoPtr->iSelected )
01759       dis.itemState |= ODS_SELECTED;
01760     if (infoPtr->uFocus == iItem) 
01761       dis.itemState |= ODS_FOCUS;
01762     dis.hwndItem = infoPtr->hwnd;
01763     dis.hDC      = hdc;
01764     CopyRect(&dis.rcItem,drawRect);
01765 
01766     /* when extra data fits ULONG_PTR, store it directly */
01767     if (infoPtr->cbInfo > sizeof(LPARAM))
01768         dis.itemData =  (ULONG_PTR) TAB_GetItem(infoPtr, iItem)->extra;
01769     else
01770     {
01771         /* this could be considered broken on 64 bit, but that's how it works -
01772            only first 4 bytes are copied */
01773         dis.itemData = 0;
01774         memcpy(&dis.itemData, (ULONG_PTR*)TAB_GetItem(infoPtr, iItem)->extra, 4);
01775     }
01776 
01777     /* draw notification */
01778     SendMessageW( infoPtr->hwndNotify, WM_DRAWITEM, id, (LPARAM)&dis );
01779   }
01780   else
01781   {
01782     TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
01783     RECT rcTemp;
01784     RECT rcImage;
01785 
01786     /* used to center the icon and text in the tab */
01787     RECT rcText;
01788     INT center_offset_h, center_offset_v;
01789 
01790     /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
01791     rcImage = *drawRect;
01792 
01793     rcTemp = *drawRect;
01794 
01795     rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
01796 
01797     /* get the rectangle that the text fits in */
01798     if (item->pszText)
01799     {
01800       DrawTextW(hdc, item->pszText, -1, &rcText, DT_CALCRECT);
01801     }
01802     /*
01803      * If not owner draw, then do the drawing ourselves.
01804      *
01805      * Draw the icon.
01806      */
01807     if (infoPtr->himl && item->iImage != -1)
01808     {
01809       INT cx;
01810       INT cy;
01811       
01812       ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
01813 
01814       if(infoPtr->dwStyle & TCS_VERTICAL)
01815       {
01816         center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right  - rcText.left))) / 2;
01817         center_offset_v = ((drawRect->right - drawRect->left) - cx) / 2;
01818       }
01819       else
01820       {
01821         center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right  - rcText.left))) / 2;
01822         center_offset_v = ((drawRect->bottom - drawRect->top) - cy) / 2;
01823       }
01824 
01825       /* if an item is selected, the icon is shifted up instead of down */
01826       if (iItem == infoPtr->iSelected)
01827         center_offset_v -= infoPtr->uVItemPadding / 2;
01828       else
01829         center_offset_v += infoPtr->uVItemPadding / 2;
01830 
01831       if (infoPtr->dwStyle & TCS_FIXEDWIDTH && infoPtr->dwStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
01832     center_offset_h = infoPtr->uHItemPadding;
01833 
01834       if (center_offset_h < 2)
01835         center_offset_h = 2;
01836     
01837       if (center_offset_v < 0)
01838         center_offset_v = 0;
01839     
01840       TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%s), textlen=%d\n",
01841       debugstr_w(item->pszText), center_offset_h, center_offset_v,
01842           wine_dbgstr_rect(drawRect), (rcText.right-rcText.left));
01843 
01844       if((infoPtr->dwStyle & TCS_VERTICAL) && (infoPtr->dwStyle & TCS_BOTTOM))
01845       {
01846         rcImage.top = drawRect->top + center_offset_h;
01847     /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
01848     /* right side of the tab, but the image still uses the left as its x position */
01849     /* this keeps the image always drawn off of the same side of the tab */
01850         rcImage.left = drawRect->right - cx - center_offset_v;
01851         drawRect->top += cy + infoPtr->uHItemPadding;
01852       }
01853       else if(infoPtr->dwStyle & TCS_VERTICAL)
01854       {
01855         rcImage.top  = drawRect->bottom - cy - center_offset_h;
01856     rcImage.left = drawRect->left + center_offset_v;
01857         drawRect->bottom -= cy + infoPtr->uHItemPadding;
01858       }
01859       else /* normal style, whether TCS_BOTTOM or not */
01860       {
01861         rcImage.left = drawRect->left + center_offset_h;
01862     rcImage.top = drawRect->top + center_offset_v;
01863         drawRect->left += cx + infoPtr->uHItemPadding;
01864       }
01865 
01866       TRACE("drawing image=%d, left=%d, top=%d\n",
01867         item->iImage, rcImage.left, rcImage.top-1);
01868       ImageList_Draw
01869         (
01870         infoPtr->himl,
01871         item->iImage,
01872         hdc,
01873         rcImage.left,
01874         rcImage.top,
01875         ILD_NORMAL
01876         );
01877     }
01878 
01879     /* Now position text */
01880     if (infoPtr->dwStyle & TCS_FIXEDWIDTH && infoPtr->dwStyle & TCS_FORCELABELLEFT)
01881       center_offset_h = infoPtr->uHItemPadding;
01882     else
01883       if(infoPtr->dwStyle & TCS_VERTICAL)
01884         center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
01885       else
01886         center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
01887 
01888     if(infoPtr->dwStyle & TCS_VERTICAL)
01889     {
01890       if(infoPtr->dwStyle & TCS_BOTTOM)
01891         drawRect->top+=center_offset_h;
01892       else
01893         drawRect->bottom-=center_offset_h;
01894 
01895       center_offset_v = ((drawRect->right - drawRect->left) - (rcText.bottom - rcText.top)) / 2;
01896     }
01897     else
01898     {
01899       drawRect->left += center_offset_h;
01900       center_offset_v = ((drawRect->bottom - drawRect->top) - (rcText.bottom - rcText.top)) / 2;
01901     }
01902 
01903     /* if an item is selected, the text is shifted up instead of down */
01904     if (iItem == infoPtr->iSelected)
01905         center_offset_v -= infoPtr->uVItemPadding / 2;
01906     else
01907         center_offset_v += infoPtr->uVItemPadding / 2;
01908 
01909     if (center_offset_v < 0)
01910       center_offset_v = 0;
01911 
01912     if(infoPtr->dwStyle & TCS_VERTICAL)
01913       drawRect->left += center_offset_v;
01914     else
01915       drawRect->top += center_offset_v;
01916 
01917     /* Draw the text */
01918     if(infoPtr->dwStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
01919     {
01920       static const WCHAR ArialW[] = { 'A','r','i','a','l',0 };
01921       LOGFONTW logfont;
01922       HFONT hFont = 0;
01923       INT nEscapement = 900;
01924       INT nOrientation = 900;
01925 
01926       if(infoPtr->dwStyle & TCS_BOTTOM)
01927       {
01928         nEscapement = -900;
01929         nOrientation = -900;
01930       }
01931 
01932       /* to get a font with the escapement and orientation we are looking for, we need to */
01933       /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
01934       if (!GetObjectW((infoPtr->hFont) ?
01935                 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
01936                 sizeof(LOGFONTW),&logfont))
01937       {
01938         INT iPointSize = 9;
01939 
01940         lstrcpyW(logfont.lfFaceName, ArialW);
01941         logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
01942                                     72);
01943         logfont.lfWeight = FW_NORMAL;
01944         logfont.lfItalic = 0;
01945         logfont.lfUnderline = 0;
01946         logfont.lfStrikeOut = 0;
01947       }
01948 
01949       logfont.lfEscapement = nEscapement;
01950       logfont.lfOrientation = nOrientation;
01951       hFont = CreateFontIndirectW(&logfont);
01952       SelectObject(hdc, hFont);
01953 
01954       if (item->pszText)
01955       {
01956         ExtTextOutW(hdc,
01957         (infoPtr->dwStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
01958         (!(infoPtr->dwStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
01959         ETO_CLIPPED,
01960         drawRect,
01961         item->pszText,
01962         lstrlenW(item->pszText),
01963         0);
01964       }
01965 
01966       DeleteObject(hFont);
01967     }
01968     else
01969     {
01970       TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%s), textlen=%d\n",
01971       debugstr_w(item->pszText), center_offset_h, center_offset_v,
01972           wine_dbgstr_rect(drawRect), (rcText.right-rcText.left));
01973       if (item->pszText)
01974       {
01975         DrawTextW
01976         (
01977           hdc,
01978           item->pszText,
01979           lstrlenW(item->pszText),
01980           drawRect,
01981           DT_LEFT | DT_SINGLELINE
01982         );
01983       }
01984     }
01985 
01986     *drawRect = rcTemp; /* restore drawRect */
01987   }
01988 
01989   /*
01990   * Cleanup
01991   */
01992   SelectObject(hdc, hOldFont);
01993   SetBkMode(hdc, oldBkMode);
01994   SelectObject(hdc, holdPen);
01995   DeleteObject( htextPen );
01996 }
01997 
01998 /******************************************************************************
01999  * TAB_DrawItem
02000  *
02001  * This method is used to draw a single tab into the tab control.
02002  */
02003 static void TAB_DrawItem(const TAB_INFO *infoPtr, HDC  hdc, INT  iItem)
02004 {
02005   RECT      itemRect;
02006   RECT      selectedRect;
02007   BOOL      isVisible;
02008   RECT      r, fillRect, r1;
02009   INT       clRight = 0;
02010   INT       clBottom = 0;
02011   COLORREF  bkgnd, corner;
02012   HTHEME    theme;
02013 
02014   /*
02015    * Get the rectangle for the item.
02016    */
02017   isVisible = TAB_InternalGetItemRect(infoPtr,
02018                       iItem,
02019                       &itemRect,
02020                       &selectedRect);
02021 
02022   if (isVisible)
02023   {
02024     RECT rUD, rC;
02025 
02026     /* Clip UpDown control to not draw over it */
02027     if (infoPtr->needsScrolling)
02028     {
02029       GetWindowRect(infoPtr->hwnd, &rC);
02030       GetWindowRect(infoPtr->hwndUpDown, &rUD);
02031       ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
02032     }
02033 
02034     /* If you need to see what the control is doing,
02035      * then override these variables. They will change what
02036      * fill colors are used for filling the tabs, and the
02037      * corners when drawing the edge.
02038      */
02039     bkgnd = comctl32_color.clrBtnFace;
02040     corner = comctl32_color.clrBtnFace;
02041 
02042     if (infoPtr->dwStyle & TCS_BUTTONS)
02043     {
02044       /* Get item rectangle */
02045       r = itemRect;
02046 
02047       /* Separators between flat buttons */
02048       if ((infoPtr->dwStyle & TCS_FLATBUTTONS) && (infoPtr->exStyle & TCS_EX_FLATSEPARATORS))
02049       {
02050     r1 = r;
02051     r1.right += (FLAT_BTN_SPACINGX -2);
02052     DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
02053       }
02054 
02055       if (iItem == infoPtr->iSelected)
02056       {
02057     DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
02058     
02059     OffsetRect(&r, 1, 1);
02060       }
02061       else  /* ! selected */
02062       {
02063         DWORD state = TAB_GetItem(infoPtr, iItem)->dwState;
02064 
02065         if ((state & TCIS_BUTTONPRESSED) || (iItem == infoPtr->uFocus))
02066           DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
02067         else
02068           if (!(infoPtr->dwStyle & TCS_FLATBUTTONS))
02069             DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
02070       }
02071     }
02072     else /* !TCS_BUTTONS */
02073     {
02074       /* We draw a rectangle of different sizes depending on the selection
02075        * state. */
02076       if (iItem == infoPtr->iSelected) {
02077     RECT rect;
02078     GetClientRect (infoPtr->hwnd, &rect);
02079     clRight = rect.right;
02080     clBottom = rect.bottom;
02081         r = selectedRect;
02082       }
02083       else
02084         r = itemRect;
02085 
02086       /*
02087        * Erase the background. (Delay it but setup rectangle.)
02088        * This is necessary when drawing the selected item since it is larger
02089        * than the others, it might overlap with stuff already drawn by the
02090        * other tabs
02091        */
02092       fillRect = r;
02093 
02094       /* Draw themed tabs - but only if they are at the top.
02095        * Windows draws even side or bottom tabs themed, with wacky results.
02096        * However, since in Wine apps may get themed that did not opt in via
02097        * a manifest avoid theming when we know the result will be wrong */
02098       if ((theme = GetWindowTheme (infoPtr->hwnd)) 
02099           && ((infoPtr->dwStyle & (TCS_VERTICAL | TCS_BOTTOM)) == 0))
02100       {
02101           static const int partIds[8] = {
02102               /* Normal item */
02103               TABP_TABITEM,
02104               TABP_TABITEMLEFTEDGE,
02105               TABP_TABITEMRIGHTEDGE,
02106               TABP_TABITEMBOTHEDGE,
02107               /* Selected tab */
02108               TABP_TOPTABITEM,
02109               TABP_TOPTABITEMLEFTEDGE,
02110               TABP_TOPTABITEMRIGHTEDGE,
02111               TABP_TOPTABITEMBOTHEDGE,
02112           };
02113           int partIndex = 0;
02114           int stateId = TIS_NORMAL;
02115 
02116           /* selected and unselected tabs have different parts */
02117           if (iItem == infoPtr->iSelected)
02118               partIndex += 4;
02119           /* The part also differs on the position of a tab on a line.
02120            * "Visually" determining the position works well enough. */
02121           GetClientRect(infoPtr->hwnd, &r1);
02122           if(selectedRect.left == 0)
02123               partIndex += 1;
02124           if(selectedRect.right == r1.right)
02125               partIndex += 2;
02126 
02127           if (iItem == infoPtr->iSelected)
02128               stateId = TIS_SELECTED;
02129           else if (iItem == infoPtr->iHotTracked)
02130               stateId = TIS_HOT;
02131           else if (iItem == infoPtr->uFocus)
02132               stateId = TIS_FOCUSED;
02133 
02134           /* Adjust rectangle for bottommost row */
02135           if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
02136             r.bottom += 3;
02137 
02138           DrawThemeBackground (theme, hdc, partIds[partIndex], stateId, &r, NULL);
02139           GetThemeBackgroundContentRect (theme, hdc, partIds[partIndex], stateId, &r, &r);
02140       }
02141       else if(infoPtr->dwStyle & TCS_VERTICAL)
02142       {
02143     /* These are for adjusting the drawing of a Selected tab      */
02144     /* The initial values are for the normal case of non-Selected */
02145     int ZZ = 1;   /* Do not stretch if selected */
02146     if (iItem == infoPtr->iSelected) {
02147         ZZ = 0;
02148 
02149         /* if leftmost draw the line longer */
02150         if(selectedRect.top == 0)
02151         fillRect.top += CONTROL_BORDER_SIZEY;
02152         /* if rightmost draw the line longer */
02153         if(selectedRect.bottom == clBottom)
02154         fillRect.bottom -= CONTROL_BORDER_SIZEY;
02155     }
02156 
02157         if (infoPtr->dwStyle & TCS_BOTTOM)
02158         {
02159       /* Adjust both rectangles to match native */
02160       r.left += (1-ZZ);
02161 
02162           TRACE("<right> item=%d, fill=(%s), edge=(%s)\n",
02163                 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r));
02164 
02165       /* Clear interior */
02166       SetBkColor(hdc, bkgnd);
02167       ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
02168 
02169       /* Draw rectangular edge around tab */
02170       DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
02171 
02172       /* Now erase the top corner and draw diagonal edge */
02173       SetBkColor(hdc, corner);
02174       r1.left = r.right - ROUND_CORNER_SIZE - 1;
02175       r1.top = r.top;
02176       r1.right = r.right;
02177       r1.bottom = r1.top + ROUND_CORNER_SIZE;
02178       ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
02179       r1.right--;
02180       DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
02181 
02182       /* Now erase the bottom corner and draw diagonal edge */
02183       r1.left = r.right - ROUND_CORNER_SIZE - 1;
02184       r1.bottom = r.bottom;
02185       r1.right = r.right;
02186       r1.top = r1.bottom - ROUND_CORNER_SIZE;
02187       ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
02188       r1.right--;
02189       DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
02190 
02191       if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
02192           r1 = r;
02193           r1.right = r1.left;
02194           r1.left--;
02195           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
02196       }
02197 
02198         }
02199         else
02200         {
02201           TRACE("<left> item=%d, fill=(%s), edge=(%s)\n",
02202                 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r));
02203 
02204       /* Clear interior */
02205       SetBkColor(hdc, bkgnd);
02206       ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
02207 
02208       /* Draw rectangular edge around tab */
02209       DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
02210 
02211       /* Now erase the top corner and draw diagonal edge */
02212       SetBkColor(hdc, corner);
02213       r1.left = r.left;
02214       r1.top = r.top;
02215       r1.right = r1.left + ROUND_CORNER_SIZE + 1;
02216       r1.bottom = r1.top + ROUND_CORNER_SIZE;
02217       ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
02218       r1.left++;
02219       DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
02220 
02221       /* Now erase the bottom corner and draw diagonal edge */
02222       r1.left = r.left;
02223       r1.bottom = r.bottom;
02224       r1.right = r1.left + ROUND_CORNER_SIZE + 1;
02225       r1.top = r1.bottom - ROUND_CORNER_SIZE;
02226       ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
02227       r1.left++;
02228       DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
02229         }
02230       }
02231       else  /* ! TCS_VERTICAL */
02232       {
02233     /* These are for adjusting the drawing of a Selected tab      */
02234     /* The initial values are for the normal case of non-Selected */
02235     if (iItem == infoPtr->iSelected) {
02236         /* if leftmost draw the line longer */
02237         if(selectedRect.left == 0)
02238         fillRect.left += CONTROL_BORDER_SIZEX;
02239         /* if rightmost draw the line longer */
02240         if(selectedRect.right == clRight)
02241         fillRect.right -= CONTROL_BORDER_SIZEX;
02242     }
02243 
02244         if (infoPtr->dwStyle & TCS_BOTTOM)
02245         {
02246       /* Adjust both rectangles for topmost row */
02247       if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
02248       {
02249         fillRect.top -= 2;
02250         r.top -= 1;
02251       }
02252 
02253           TRACE("<bottom> item=%d, fill=(%s), edge=(%s)\n",
02254                 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r));
02255 
02256       /* Clear interior */
02257       SetBkColor(hdc, bkgnd);
02258       ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
02259 
02260       /* Draw rectangular edge around tab */
02261       DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
02262 
02263       /* Now erase the righthand corner and draw diagonal edge */
02264       SetBkColor(hdc, corner);
02265       r1.left = r.right - ROUND_CORNER_SIZE;
02266       r1.bottom = r.bottom;
02267       r1.right = r.right;
02268       r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
02269       ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
02270       r1.bottom--;
02271       DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
02272 
02273       /* Now erase the lefthand corner and draw diagonal edge */
02274       r1.left = r.left;
02275       r1.bottom = r.bottom;
02276       r1.right = r1.left + ROUND_CORNER_SIZE;
02277       r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
02278       ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
02279       r1.bottom--;
02280       DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
02281 
02282       if (iItem == infoPtr->iSelected)
02283       {
02284         r.top += 2;
02285         r.left += 1;
02286         if (selectedRect.left == 0)
02287         {
02288           r1 = r;
02289           r1.bottom = r1.top;
02290           r1.top--;
02291           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
02292         }
02293       }
02294 
02295         }
02296         else
02297         {
02298       /* Adjust both rectangles for bottommost row */
02299       if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
02300       {
02301         fillRect.bottom += 3;
02302         r.bottom += 2;
02303       }
02304 
02305           TRACE("<top> item=%d, fill=(%s), edge=(%s)\n",
02306                 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r));
02307 
02308       /* Clear interior */
02309       SetBkColor(hdc, bkgnd);
02310       ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
02311 
02312       /* Draw rectangular edge around tab */
02313       DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
02314 
02315       /* Now erase the righthand corner and draw diagonal edge */
02316       SetBkColor(hdc, corner);
02317       r1.left = r.right - ROUND_CORNER_SIZE;
02318       r1.top = r.top;
02319       r1.right = r.right;
02320       r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
02321       ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
02322       r1.top++;
02323       DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
02324 
02325       /* Now erase the lefthand corner and draw diagonal edge */
02326       r1.left = r.left;
02327       r1.top = r.top;
02328       r1.right = r1.left + ROUND_CORNER_SIZE;
02329       r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
02330       ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
02331       r1.top++;
02332       DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
02333         }
02334       }
02335     }
02336 
02337     TAB_DumpItemInternal(infoPtr, iItem);
02338 
02339     /* This modifies r to be the text rectangle. */
02340     TAB_DrawItemInterior(infoPtr, hdc, iItem, &r);
02341   }
02342 }
02343 
02344 /******************************************************************************
02345  * TAB_DrawBorder
02346  *
02347  * This method is used to draw the raised border around the tab control
02348  * "content" area.
02349  */
02350 static void TAB_DrawBorder(const TAB_INFO *infoPtr, HDC hdc)
02351 {
02352   RECT rect;
02353   HTHEME theme = GetWindowTheme (infoPtr->hwnd);
02354 
02355   GetClientRect (infoPtr->hwnd, &rect);
02356 
02357   /*
02358    * Adjust for the style
02359    */
02360 
02361   if (infoPtr->uNumItem)
02362   {
02363     if ((infoPtr->dwStyle & TCS_BOTTOM) && !(infoPtr->dwStyle & TCS_VERTICAL))
02364       rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
02365     else if((infoPtr->dwStyle & TCS_BOTTOM) && (infoPtr->dwStyle & TCS_VERTICAL))
02366       rect.right  -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
02367     else if(infoPtr->dwStyle & TCS_VERTICAL)
02368       rect.left   += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
02369     else /* not TCS_VERTICAL and not TCS_BOTTOM */
02370       rect.top    += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
02371   }
02372 
02373   TRACE("border=(%s)\n", wine_dbgstr_rect(&rect));
02374 
02375   if (theme)
02376       DrawThemeBackground (theme, hdc, TABP_PANE, 0, &rect, NULL);
02377   else
02378       DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
02379 }
02380 
02381 /******************************************************************************
02382  * TAB_Refresh
02383  *
02384  * This method repaints the tab control..
02385  */
02386 static void TAB_Refresh (const TAB_INFO *infoPtr, HDC hdc)
02387 {
02388   HFONT hOldFont;
02389   INT i;
02390 
02391   if (!infoPtr->DoRedraw)
02392     return;
02393 
02394   hOldFont = SelectObject (hdc, infoPtr->hFont);
02395 
02396   if (infoPtr->dwStyle & TCS_BUTTONS)
02397   {
02398     for (i = 0; i < infoPtr->uNumItem; i++)
02399       TAB_DrawItem (infoPtr, hdc, i);
02400   }
02401   else
02402   {
02403     /* Draw all the non selected item first */
02404     for (i = 0; i < infoPtr->uNumItem; i++)
02405     {
02406       if (i != infoPtr->iSelected)
02407     TAB_DrawItem (infoPtr, hdc, i);
02408     }
02409 
02410     /* Now, draw the border, draw it before the selected item
02411      * since the selected item overwrites part of the border. */
02412     TAB_DrawBorder (infoPtr, hdc);
02413 
02414     /* Then, draw the selected item */
02415     TAB_DrawItem (infoPtr, hdc, infoPtr->iSelected);
02416   }
02417 
02418   SelectObject (hdc, hOldFont);
02419 }
02420 
02421 static inline DWORD TAB_GetRowCount (const TAB_INFO *infoPtr)
02422 {
02423   TRACE("(%p)\n", infoPtr);
02424   return infoPtr->uNumRows;
02425 }
02426 
02427 static inline LRESULT TAB_SetRedraw (TAB_INFO *infoPtr, BOOL doRedraw)
02428 {
02429   infoPtr->DoRedraw = doRedraw;
02430   return 0;
02431 }
02432 
02433 /******************************************************************************
02434  * TAB_EnsureSelectionVisible
02435  *
02436  * This method will make sure that the current selection is completely
02437  * visible by scrolling until it is.
02438  */
02439 static void TAB_EnsureSelectionVisible(
02440   TAB_INFO* infoPtr)
02441 {
02442   INT iSelected = infoPtr->iSelected;
02443   INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
02444 
02445   if (iSelected < 0)
02446     return;
02447 
02448   /* set the items row to the bottommost row or topmost row depending on
02449    * style */
02450   if ((infoPtr->uNumRows > 1) && !(infoPtr->dwStyle & TCS_BUTTONS))
02451   {
02452       TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
02453       INT newselected;
02454       INT iTargetRow;
02455 
02456       if(infoPtr->dwStyle & TCS_VERTICAL)
02457         newselected = selected->rect.left;
02458       else
02459         newselected = selected->rect.top;
02460 
02461       /* the target row is always (number of rows - 1)
02462          as row 0 is furthest from the clientRect */
02463       iTargetRow = infoPtr->uNumRows - 1;
02464 
02465       if (newselected != iTargetRow)
02466       {
02467          UINT i;
02468          if(infoPtr->dwStyle & TCS_VERTICAL)
02469          {
02470            for (i=0; i < infoPtr->uNumItem; i++)
02471            {
02472              /* move everything in the row of the selected item to the iTargetRow */
02473              TAB_ITEM *item = TAB_GetItem(infoPtr, i);
02474 
02475              if (item->rect.left == newselected )
02476                  item->rect.left = iTargetRow;
02477              else
02478              {
02479                if (item->rect.left > newselected)
02480                  item->rect.left-=1;
02481              }
02482            }
02483          }
02484          else
02485          {
02486            for (i=0; i < infoPtr->uNumItem; i++)
02487            {
02488              TAB_ITEM *item = TAB_GetItem(infoPtr, i);
02489 
02490              if (item->rect.top == newselected )
02491                  item->rect.top = iTargetRow;
02492              else
02493              {
02494                if (item->rect.top > newselected)
02495                  item->rect.top-=1;
02496              }
02497           }
02498         }
02499         TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
02500       }
02501   }
02502 
02503   /*
02504    * Do the trivial cases first.
02505    */
02506   if ( (!infoPtr->needsScrolling) ||
02507        (infoPtr->hwndUpDown==0) || (infoPtr->dwStyle & TCS_VERTICAL))
02508     return;
02509 
02510   if (infoPtr->leftmostVisible >= iSelected)
02511   {
02512     infoPtr->leftmostVisible = iSelected;
02513   }
02514   else
02515   {
02516      TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
02517      RECT r;
02518      INT width;
02519      UINT i;
02520 
02521      /* Calculate the part of the client area that is visible */
02522      GetClientRect(infoPtr->hwnd, &r);
02523      width = r.right;
02524 
02525      GetClientRect(infoPtr->hwndUpDown, &r);
02526      width -= r.right;
02527 
02528      if ((selected->rect.right -
02529           selected->rect.left) >= width )
02530      {
02531         /* Special case: width of selected item is greater than visible
02532          * part of control.
02533          */
02534         infoPtr->leftmostVisible = iSelected;
02535      }
02536      else
02537      {
02538         for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
02539         {
02540            if ((selected->rect.right - TAB_GetItem(infoPtr, i)->rect.left) < width)
02541               break;
02542         }
02543         infoPtr->leftmostVisible = i;
02544      }
02545   }
02546 
02547   if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
02548     TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
02549 
02550   SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
02551                MAKELONG(infoPtr->leftmostVisible, 0));
02552 }
02553 
02554 /******************************************************************************
02555  * TAB_InvalidateTabArea
02556  *
02557  * This method will invalidate the portion of the control that contains the
02558  * tabs. It is called when the state of the control changes and needs
02559  * to be redisplayed
02560  */
02561 static void TAB_InvalidateTabArea(const TAB_INFO *infoPtr)
02562 {
02563   RECT clientRect, rInvalidate, rAdjClient;
02564   INT lastRow = infoPtr->uNumRows - 1;
02565   RECT rect;
02566 
02567   if (lastRow < 0) return;
02568 
02569   GetClientRect(infoPtr->hwnd, &clientRect);
02570   rInvalidate = clientRect;
02571   rAdjClient = clientRect;
02572 
02573   TAB_AdjustRect(infoPtr, 0, &rAdjClient);
02574 
02575   TAB_InternalGetItemRect(infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
02576   if ((infoPtr->dwStyle & TCS_BOTTOM) && (infoPtr->dwStyle & TCS_VERTICAL))
02577   {
02578     rInvalidate.left = rAdjClient.right;
02579     if (infoPtr->uNumRows == 1)
02580       rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
02581   }
02582   else if(infoPtr->dwStyle & TCS_VERTICAL)
02583   {
02584     rInvalidate.right = rAdjClient.left;
02585     if (infoPtr->uNumRows == 1)
02586       rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
02587   }
02588   else if (infoPtr->dwStyle & TCS_BOTTOM)
02589   {
02590     rInvalidate.top = rAdjClient.bottom;
02591     if (infoPtr->uNumRows == 1)
02592       rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
02593   }
02594   else 
02595   {
02596     rInvalidate.bottom = rAdjClient.top;
02597     if (infoPtr->uNumRows == 1)
02598       rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
02599   }
02600   
02601   /* Punch out the updown control */
02602   if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
02603     RECT r;
02604     GetClientRect(infoPtr->hwndUpDown, &r);
02605     if (rInvalidate.right > clientRect.right - r.left)
02606       rInvalidate.right = rInvalidate.right - (r.right - r.left);
02607     else
02608       rInvalidate.right = clientRect.right - r.left;
02609   }
02610 
02611   TRACE("invalidate (%s)\n", wine_dbgstr_rect(&rInvalidate));
02612 
02613   InvalidateRect(infoPtr->hwnd, &rInvalidate, TRUE);
02614 }
02615 
02616 static inline LRESULT TAB_Paint (TAB_INFO *infoPtr, HDC hdcPaint)
02617 {
02618   HDC hdc;
02619   PAINTSTRUCT ps;
02620 
02621   if (hdcPaint)
02622     hdc = hdcPaint;
02623   else
02624   {
02625     hdc = BeginPaint (infoPtr->hwnd, &ps);
02626     TRACE("erase %d, rect=(%s)\n", ps.fErase, wine_dbgstr_rect(&ps.rcPaint));
02627   }
02628 
02629   TAB_Refresh (infoPtr, hdc);
02630 
02631   if (!hdcPaint)
02632     EndPaint (infoPtr->hwnd, &ps);
02633 
02634   return 0;
02635 }
02636 
02637 static LRESULT
02638 TAB_InsertItemT (TAB_INFO *infoPtr, INT iItem, const TCITEMW *pti, BOOL bUnicode)
02639 {
02640   TAB_ITEM *item;
02641   RECT rect;
02642 
02643   GetClientRect (infoPtr->hwnd, &rect);
02644   TRACE("Rect: %p %s\n", infoPtr->hwnd, wine_dbgstr_rect(&rect));
02645 
02646   if (iItem < 0) return -1;
02647   if (iItem > infoPtr->uNumItem)
02648     iItem = infoPtr->uNumItem;
02649 
02650   TAB_DumpItemExternalT(pti, iItem, bUnicode);
02651 
02652   if (!(item = Alloc(TAB_ITEM_SIZE(infoPtr)))) return FALSE;
02653   if (DPA_InsertPtr(infoPtr->items, iItem, item) == -1)
02654   {
02655       Free(item);
02656       return FALSE;
02657   }
02658 
02659   if (infoPtr->uNumItem == 0)
02660       infoPtr->iSelected = 0;
02661   else if (iItem <= infoPtr->iSelected)
02662       infoPtr->iSelected++;
02663 
02664   infoPtr->uNumItem++;
02665 
02666   item->pszText = NULL;
02667   if (pti->mask & TCIF_TEXT)
02668   {
02669     if (bUnicode)
02670       Str_SetPtrW (&item->pszText, pti->pszText);
02671     else
02672       Str_SetPtrAtoW (&item->pszText, (LPSTR)pti->pszText);
02673   }
02674 
02675   if (pti->mask & TCIF_IMAGE)
02676     item->iImage = pti->iImage;
02677   else
02678     item->iImage = -1;
02679 
02680   if (pti->mask & TCIF_PARAM)
02681     memcpy(item->extra, &pti->lParam, EXTRA_ITEM_SIZE(infoPtr));
02682   else
02683     memset(item->extra, 0, EXTRA_ITEM_SIZE(infoPtr));
02684 
02685   TAB_SetItemBounds(infoPtr);
02686   if (infoPtr->uNumItem > 1)
02687     TAB_InvalidateTabArea(infoPtr);
02688   else
02689     InvalidateRect(infoPtr->hwnd, NULL, TRUE);
02690 
02691   TRACE("[%p]: added item %d %s\n",
02692         infoPtr->hwnd, iItem, debugstr_w(item->pszText));
02693 
02694   /* If we haven't set the current focus yet, set it now. */
02695   if (infoPtr->uFocus == -1)
02696     TAB_SetCurFocus(infoPtr, iItem);
02697 
02698   return iItem;
02699 }
02700 
02701 static LRESULT
02702 TAB_SetItemSize (TAB_INFO *infoPtr, INT cx, INT cy)
02703 {
02704   LONG lResult = 0;
02705   BOOL bNeedPaint = FALSE;
02706 
02707   lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
02708 
02709   /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
02710   if (infoPtr->dwStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != cx))
02711   {
02712     infoPtr->tabWidth = cx;
02713     bNeedPaint = TRUE;
02714   }
02715 
02716   if (infoPtr->tabHeight != cy)
02717   {
02718     if ((infoPtr->fHeightSet = (cy != 0)))
02719       infoPtr->tabHeight = cy;
02720 
02721     bNeedPaint = TRUE;
02722   }
02723   TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
02724        HIWORD(lResult), LOWORD(lResult),
02725        infoPtr->tabHeight, infoPtr->tabWidth);
02726 
02727   if (bNeedPaint)
02728   {
02729     TAB_SetItemBounds(infoPtr);
02730     RedrawWindow(infoPtr->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
02731   }
02732 
02733   return lResult;
02734 }
02735 
02736 static inline LRESULT TAB_SetMinTabWidth (TAB_INFO *infoPtr, INT cx)
02737 {
02738   INT oldcx = 0;
02739 
02740   TRACE("(%p,%d)\n", infoPtr, cx);
02741 
02742   if (infoPtr->tabMinWidth < 0)
02743     oldcx = DEFAULT_MIN_TAB_WIDTH;
02744   else
02745     oldcx = infoPtr->tabMinWidth;
02746   infoPtr->tabMinWidth = cx;
02747   TAB_SetItemBounds(infoPtr);
02748   return oldcx;
02749 }
02750 
02751 static inline LRESULT 
02752 TAB_HighlightItem (TAB_INFO *infoPtr, INT iItem, BOOL fHighlight)
02753 {
02754   LPDWORD lpState;
02755   DWORD oldState;
02756   RECT r;
02757 
02758   TRACE("(%p,%d,%s)\n", infoPtr, iItem, fHighlight ? "true" : "false");
02759 
02760   if (iItem < 0 || iItem >= infoPtr->uNumItem)
02761     return FALSE;
02762 
02763   lpState = &TAB_GetItem(infoPtr, iItem)->dwState;
02764   oldState = *lpState;
02765 
02766   if (fHighlight)
02767     *lpState |= TCIS_HIGHLIGHTED;
02768   else
02769     *lpState &= ~TCIS_HIGHLIGHTED;
02770 
02771   if ((oldState != *lpState) && TAB_InternalGetItemRect (infoPtr, iItem, &r, NULL))
02772     InvalidateRect (infoPtr->hwnd, &r, TRUE);
02773 
02774   return TRUE;
02775 }
02776 
02777 static LRESULT
02778 TAB_SetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
02779 {
02780   TAB_ITEM *wineItem;
02781 
02782   TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
02783 
02784   if (iItem < 0 || iItem >= infoPtr->uNumItem)
02785     return FALSE;
02786 
02787   TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
02788 
02789   wineItem = TAB_GetItem(infoPtr, iItem);
02790 
02791   if (tabItem->mask & TCIF_IMAGE)
02792     wineItem->iImage = tabItem->iImage;
02793 
02794   if (tabItem->mask & TCIF_PARAM)
02795     memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
02796 
02797   if (tabItem->mask & TCIF_RTLREADING)
02798     FIXME("TCIF_RTLREADING\n");
02799 
02800   if (tabItem->mask & TCIF_STATE)
02801     wineItem->dwState = (wineItem->dwState & ~tabItem->dwStateMask) |
02802                         ( tabItem->dwState &  tabItem->dwStateMask);
02803 
02804   if (tabItem->mask & TCIF_TEXT)
02805   {
02806     Free(wineItem->pszText);
02807     wineItem->pszText = NULL;
02808     if (bUnicode)
02809       Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
02810     else
02811       Str_SetPtrAtoW(&wineItem->pszText, (LPSTR)tabItem->pszText);
02812   }
02813 
02814   /* Update and repaint tabs */
02815   TAB_SetItemBounds(infoPtr);
02816   TAB_InvalidateTabArea(infoPtr);
02817 
02818   return TRUE;
02819 }
02820 
02821 static inline LRESULT TAB_GetItemCount (const TAB_INFO *infoPtr)
02822 {
02823   TRACE("\n");
02824   return infoPtr->uNumItem;
02825 }
02826 
02827 
02828 static LRESULT
02829 TAB_GetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
02830 {
02831   TAB_ITEM *wineItem;
02832 
02833   TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
02834 
02835   if (!tabItem) return FALSE;
02836 
02837   if (iItem < 0 || iItem >= infoPtr->uNumItem)
02838   {
02839     /* init requested fields */
02840     if (tabItem->mask & TCIF_IMAGE) tabItem->iImage  = 0;
02841     if (tabItem->mask & TCIF_PARAM) tabItem->lParam  = 0;
02842     if (tabItem->mask & TCIF_STATE) tabItem->dwState = 0;
02843     return FALSE;
02844   }
02845 
02846   wineItem = TAB_GetItem(infoPtr, iItem);
02847 
02848   if (tabItem->mask & TCIF_IMAGE)
02849     tabItem->iImage = wineItem->iImage;
02850 
02851   if (tabItem->mask & TCIF_PARAM)
02852     memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
02853 
02854   if (tabItem->mask & TCIF_RTLREADING)
02855     FIXME("TCIF_RTLREADING\n");
02856 
02857   if (tabItem->mask & TCIF_STATE)
02858     tabItem->dwState = wineItem->dwState & tabItem->dwStateMask;
02859 
02860   if (tabItem->mask & TCIF_TEXT)
02861   {
02862     if (bUnicode)
02863       Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
02864     else
02865       Str_GetPtrWtoA (wineItem->pszText, (LPSTR)tabItem->pszText, tabItem->cchTextMax);
02866   }
02867 
02868   TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
02869 
02870   return TRUE;
02871 }
02872 
02873 
02874 static LRESULT TAB_DeleteItem (TAB_INFO *infoPtr, INT iItem)
02875 {
02876     TAB_ITEM *item;
02877 
02878     TRACE("(%p, %d)\n", infoPtr, iItem);
02879 
02880     if (iItem < 0 || iItem >= infoPtr->uNumItem) return FALSE;
02881 
02882     item = TAB_GetItem(infoPtr, iItem);
02883     Free(item->pszText);
02884     Free(item);
02885     infoPtr->uNumItem--;
02886     DPA_DeletePtr(infoPtr->items, iItem);
02887 
02888     TAB_InvalidateTabArea(infoPtr);
02889 
02890     if (infoPtr->uNumItem == 0)
02891     {
02892         if (infoPtr->iHotTracked >= 0)
02893         {
02894             KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
02895             infoPtr->iHotTracked = -1;
02896         }
02897 
02898         infoPtr->iSelected = -1;
02899     }
02900     else
02901     {
02902         if (iItem <= infoPtr->iHotTracked)
02903         {
02904             /* When tabs move left/up, the hot track item may change */
02905             FIXME("Recalc hot track\n");
02906         }
02907     }
02908 
02909     /* adjust the selected index */
02910     if (iItem == infoPtr->iSelected)
02911         infoPtr->iSelected = -1;
02912     else if (iItem < infoPtr->iSelected)
02913         infoPtr->iSelected--;
02914 
02915     /* reposition and repaint tabs */
02916     TAB_SetItemBounds(infoPtr);
02917 
02918     return TRUE;
02919 }
02920 
02921 static inline LRESULT TAB_DeleteAllItems (TAB_INFO *infoPtr)
02922 {
02923     TRACE("(%p)\n", infoPtr);
02924     while (infoPtr->uNumItem)
02925       TAB_DeleteItem (infoPtr, 0);
02926     return TRUE;
02927 }
02928 
02929 
02930 static inline LRESULT TAB_GetFont (const TAB_INFO *infoPtr)
02931 {
02932   TRACE("(%p) returning %p\n", infoPtr, infoPtr->hFont);
02933   return (LRESULT)infoPtr->hFont;
02934 }
02935 
02936 static inline LRESULT TAB_SetFont (TAB_INFO *infoPtr, HFONT hNewFont)
02937 {
02938   TRACE("(%p,%p)\n", infoPtr, hNewFont);
02939 
02940   infoPtr->hFont = hNewFont;
02941 
02942   TAB_SetItemBounds(infoPtr);
02943 
02944   TAB_InvalidateTabArea(infoPtr);
02945 
02946   return 0;
02947 }
02948 
02949 
02950 static inline LRESULT TAB_GetImageList (const TAB_INFO *infoPtr)
02951 {
02952   TRACE("\n");
02953   return (LRESULT)infoPtr->himl;
02954 }
02955 
02956 static inline LRESULT TAB_SetImageList (TAB_INFO *infoPtr, HIMAGELIST himlNew)
02957 {
02958     HIMAGELIST himlPrev = infoPtr->himl;
02959     TRACE("himl=%p\n", himlNew);
02960     infoPtr->himl = himlNew;
02961     TAB_SetItemBounds(infoPtr);
02962     InvalidateRect(infoPtr->hwnd, NULL, TRUE);
02963     return (LRESULT)himlPrev;
02964 }
02965 
02966 static inline LRESULT TAB_GetUnicodeFormat (const TAB_INFO *infoPtr)
02967 {
02968     TRACE("(%p)\n", infoPtr);
02969     return infoPtr->bUnicode;
02970 }
02971 
02972 static inline LRESULT TAB_SetUnicodeFormat (TAB_INFO *infoPtr, BOOL bUnicode)
02973 {
02974     BOOL bTemp = infoPtr->bUnicode;
02975 
02976     TRACE("(%p %d)\n", infoPtr, bUnicode);
02977     infoPtr->bUnicode = bUnicode;
02978 
02979     return bTemp;
02980 }
02981 
02982 static inline LRESULT TAB_Size (TAB_INFO *infoPtr)
02983 {
02984 /* I'm not really sure what the following code was meant to do.
02985    This is what it is doing:
02986    When WM_SIZE is sent with SIZE_RESTORED, the control
02987    gets positioned in the top left corner.
02988 
02989   RECT parent_rect;
02990   HWND parent;
02991   UINT uPosFlags,cx,cy;
02992 
02993   uPosFlags=0;
02994   if (!wParam) {
02995     parent = GetParent (hwnd);
02996     GetClientRect(parent, &parent_rect);
02997     cx=LOWORD (lParam);
02998     cy=HIWORD (lParam);
02999     if (GetWindowLongW(hwnd, GWL_STYLE) & CCS_NORESIZE)
03000         uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
03001 
03002     SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
03003             cx, cy, uPosFlags | SWP_NOZORDER);
03004   } else {
03005     FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
03006   } */
03007 
03008   /* Recompute the size/position of the tabs. */
03009   TAB_SetItemBounds (infoPtr);
03010 
03011   /* Force a repaint of the control. */
03012   InvalidateRect(infoPtr->hwnd, NULL, TRUE);
03013 
03014   return 0;
03015 }
03016 
03017 
03018 static LRESULT TAB_Create (HWND hwnd, LPARAM lParam)
03019 {
03020   TAB_INFO *infoPtr;
03021   TEXTMETRICW fontMetrics;
03022   HDC hdc;
03023   HFONT hOldFont;
03024   DWORD dwStyle;
03025 
03026   infoPtr = Alloc (sizeof(TAB_INFO));
03027 
03028   SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
03029 
03030   infoPtr->hwnd            = hwnd;
03031   infoPtr->hwndNotify      = ((LPCREATESTRUCTW)lParam)->hwndParent;
03032   infoPtr->uNumItem        = 0;
03033   infoPtr->uNumRows        = 0;
03034   infoPtr->uHItemPadding   = 6;
03035   infoPtr->uVItemPadding   = 3;
03036   infoPtr->uHItemPadding_s = 6;
03037   infoPtr->uVItemPadding_s = 3;
03038   infoPtr->hFont           = 0;
03039   infoPtr->items           = DPA_Create(8);
03040   infoPtr->hcurArrow       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
03041   infoPtr->iSelected       = -1;
03042   infoPtr->iHotTracked     = -1;
03043   infoPtr->uFocus          = -1;
03044   infoPtr->hwndToolTip     = 0;
03045   infoPtr->DoRedraw        = TRUE;
03046   infoPtr->needsScrolling  = FALSE;
03047   infoPtr->hwndUpDown      = 0;
03048   infoPtr->leftmostVisible = 0;
03049   infoPtr->fHeightSet      = FALSE;
03050   infoPtr->bUnicode        = IsWindowUnicode (hwnd);
03051   infoPtr->cbInfo          = sizeof(LPARAM);
03052 
03053   TRACE("Created tab control, hwnd [%p]\n", hwnd);
03054 
03055   /* The tab control always has the WS_CLIPSIBLINGS style. Even
03056      if you don't specify it in CreateWindow. This is necessary in
03057      order for paint to work correctly. This follows windows behaviour. */
03058   dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
03059   SetWindowLongW(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
03060 
03061   infoPtr->dwStyle = dwStyle | WS_CLIPSIBLINGS;
03062   infoPtr->exStyle = (dwStyle & TCS_FLATBUTTONS) ? TCS_EX_FLATSEPARATORS : 0;
03063 
03064   if (infoPtr->dwStyle & TCS_TOOLTIPS) {
03065     /* Create tooltip control */
03066     infoPtr->hwndToolTip =
03067       CreateWindowExW (0, TOOLTIPS_CLASSW, NULL, WS_POPUP,
03068                CW_USEDEFAULT, CW_USEDEFAULT,
03069                CW_USEDEFAULT, CW_USEDEFAULT,
03070                hwnd, 0, 0, 0);
03071 
03072     /* Send NM_TOOLTIPSCREATED notification */
03073     if (infoPtr->hwndToolTip) {
03074       NMTOOLTIPSCREATED nmttc;
03075 
03076       nmttc.hdr.hwndFrom = hwnd;
03077       nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
03078       nmttc.hdr.code = NM_TOOLTIPSCREATED;
03079       nmttc.hwndToolTips = infoPtr->hwndToolTip;
03080 
03081       SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
03082                     GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc);
03083     }
03084   }
03085 
03086   OpenThemeData (infoPtr->hwnd, themeClass);
03087   
03088   /*
03089    * We need to get text information so we need a DC and we need to select
03090    * a font.
03091    */
03092   hdc = GetDC(hwnd);
03093   hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
03094 
03095   /* Use the system font to determine the initial height of a tab. */
03096   GetTextMetricsW(hdc, &fontMetrics);
03097 
03098   /*
03099    * Make sure there is enough space for the letters + growing the
03100    * selected item + extra space for the selected item.
03101    */
03102   infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
03103                    ((infoPtr->dwStyle & TCS_BUTTONS) ? 2 : 1) *
03104                         infoPtr->uVItemPadding;
03105 
03106   /* Initialize the width of a tab. */
03107   if (infoPtr->dwStyle & TCS_FIXEDWIDTH)
03108     infoPtr->tabWidth = GetDeviceCaps(hdc, LOGPIXELSX);
03109 
03110   infoPtr->tabMinWidth = -1;
03111 
03112   TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
03113 
03114   SelectObject (hdc, hOldFont);
03115   ReleaseDC(hwnd, hdc);
03116 
03117   return 0;
03118 }
03119 
03120 static LRESULT
03121 TAB_Destroy (TAB_INFO *infoPtr)
03122 {
03123   INT iItem;
03124 
03125   SetWindowLongPtrW(infoPtr->hwnd, 0, 0);
03126 
03127   for (iItem = infoPtr->uNumItem - 1; iItem >= 0; iItem--)
03128   {
03129       TAB_ITEM *tab = TAB_GetItem(infoPtr, iItem);
03130 
03131       DPA_DeletePtr(infoPtr->items, iItem);
03132       infoPtr->uNumItem--;
03133 
03134       Free(tab->pszText);
03135       Free(tab);
03136   }
03137   DPA_Destroy(infoPtr->items);
03138   infoPtr->items = NULL;
03139 
03140   if (infoPtr->hwndToolTip)
03141     DestroyWindow (infoPtr->hwndToolTip);
03142 
03143   if (infoPtr->hwndUpDown)
03144     DestroyWindow(infoPtr->hwndUpDown);
03145 
03146   if (infoPtr->iHotTracked >= 0)
03147     KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
03148 
03149   CloseThemeData (GetWindowTheme (infoPtr->hwnd));
03150 
03151   Free (infoPtr);
03152   return 0;
03153 }
03154 
03155 /* update theme after a WM_THEMECHANGED message */
03156 static LRESULT theme_changed(const TAB_INFO *infoPtr)
03157 {
03158     HTHEME theme = GetWindowTheme (infoPtr->hwnd);
03159     CloseThemeData (theme);
03160     OpenThemeData (infoPtr->hwnd, themeClass);
03161     return 0;
03162 }
03163 
03164 static LRESULT TAB_NCCalcSize(WPARAM wParam)
03165 {
03166   if (!wParam)
03167     return 0;
03168   return WVR_ALIGNTOP;
03169 }
03170 
03171 static inline LRESULT
03172 TAB_SetItemExtra (TAB_INFO *infoPtr, INT cbInfo)
03173 {
03174   TRACE("(%p %d)\n", infoPtr, cbInfo);
03175 
03176   if (cbInfo < 0 || infoPtr->uNumItem) return FALSE;
03177 
03178   infoPtr->cbInfo = cbInfo;
03179   return TRUE;
03180 }
03181 
03182 static LRESULT TAB_RemoveImage (TAB_INFO *infoPtr, INT image)
03183 {
03184   TRACE("%p %d\n", infoPtr, image);
03185 
03186   if (ImageList_Remove (infoPtr->himl, image))
03187   {
03188     INT i, *idx;
03189     RECT r;
03190 
03191     /* shift indices, repaint items if needed */
03192     for (i = 0; i < infoPtr->uNumItem; i++)
03193     {
03194       idx = &TAB_GetItem(infoPtr, i)->iImage;
03195       if (*idx >= image)
03196       {
03197         if (*idx == image)
03198           *idx = -1;
03199         else
03200           (*idx)--;
03201 
03202         /* repaint item */
03203         if (TAB_InternalGetItemRect (infoPtr, i, &r, NULL))
03204           InvalidateRect (infoPtr->hwnd, &r, TRUE);
03205       }
03206     }
03207   }
03208 
03209   return 0;
03210 }
03211 
03212 static LRESULT
03213 TAB_SetExtendedStyle (TAB_INFO *infoPtr, DWORD exMask, DWORD exStyle)
03214 {
03215   DWORD prevstyle = infoPtr->exStyle;
03216 
03217   /* zero mask means all styles */
03218   if (exMask == 0) exMask = ~0;
03219 
03220   if (exMask & TCS_EX_REGISTERDROP)
03221   {
03222     FIXME("TCS_EX_REGISTERDROP style unimplemented\n");
03223     exMask  &= ~TCS_EX_REGISTERDROP;
03224     exStyle &= ~TCS_EX_REGISTERDROP;
03225   }
03226 
03227   if (exMask & TCS_EX_FLATSEPARATORS)
03228   {
03229     if ((prevstyle ^ exStyle) & TCS_EX_FLATSEPARATORS)
03230     {
03231         infoPtr->exStyle ^= TCS_EX_FLATSEPARATORS;
03232         TAB_InvalidateTabArea(infoPtr);
03233     }
03234   }
03235 
03236   return prevstyle;
03237 }
03238 
03239 static inline LRESULT
03240 TAB_GetExtendedStyle (const TAB_INFO *infoPtr)
03241 {
03242   return infoPtr->exStyle;
03243 }
03244 
03245 static LRESULT
03246 TAB_DeselectAll (TAB_INFO *infoPtr, BOOL excludesel)
03247 {
03248   BOOL paint = FALSE;
03249   INT i, selected = infoPtr->iSelected;
03250 
03251   TRACE("(%p, %d)\n", infoPtr, excludesel);
03252 
03253   if (!(infoPtr->dwStyle & TCS_BUTTONS))
03254     return 0;
03255 
03256   for (i = 0; i < infoPtr->uNumItem; i++)
03257   {
03258     if ((TAB_GetItem(infoPtr, i)->dwState & TCIS_BUTTONPRESSED) &&
03259         (selected != i))
03260     {
03261       TAB_GetItem(infoPtr, i)->dwState &= ~TCIS_BUTTONPRESSED;
03262       paint = TRUE;
03263     }
03264   }
03265 
03266   if (!excludesel && (selected != -1))
03267   {
03268     TAB_GetItem(infoPtr, selected)->dwState &= ~TCIS_BUTTONPRESSED;
03269     infoPtr->iSelected = -1;
03270     paint = TRUE;
03271   }
03272 
03273   if (paint)
03274     TAB_InvalidateTabArea (infoPtr);
03275 
03276   return 0;
03277 }
03278 
03279 /***
03280  * DESCRIPTION:
03281  * Processes WM_STYLECHANGED messages.
03282  *
03283  * PARAMETER(S):
03284  * [I] infoPtr : valid pointer to the tab data structure
03285  * [I] wStyleType : window style type (normal or extended)
03286  * [I] lpss : window style information
03287  *
03288  * RETURN:
03289  * Zero
03290  */
03291 static INT TAB_StyleChanged(TAB_INFO *infoPtr, WPARAM wStyleType,
03292                             const STYLESTRUCT *lpss)
03293 {
03294     TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
03295           wStyleType, lpss->styleOld, lpss->styleNew);
03296 
03297     if (wStyleType != GWL_STYLE) return 0;
03298 
03299     infoPtr->dwStyle = lpss->styleNew;
03300 
03301     TAB_SetItemBounds (infoPtr);
03302     InvalidateRect(infoPtr->hwnd, NULL, TRUE);
03303 
03304     return 0;
03305 }
03306 
03307 static LRESULT WINAPI
03308 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
03309 {
03310     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
03311 
03312     TRACE("hwnd=%p msg=%x wParam=%lx lParam=%lx\n", hwnd, uMsg, wParam, lParam);
03313     if (!infoPtr && (uMsg != WM_CREATE))
03314       return DefWindowProcW (hwnd, uMsg, wParam, lParam);
03315 
03316     switch (uMsg)
03317     {
03318     case TCM_GETIMAGELIST:
03319       return TAB_GetImageList (infoPtr);
03320 
03321     case TCM_SETIMAGELIST:
03322       return TAB_SetImageList (infoPtr, (HIMAGELIST)lParam);
03323 
03324     case TCM_GETITEMCOUNT:
03325       return TAB_GetItemCount (infoPtr);
03326 
03327     case TCM_GETITEMA:
03328     case TCM_GETITEMW:
03329       return TAB_GetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_GETITEMW);
03330 
03331     case TCM_SETITEMA:
03332     case TCM_SETITEMW:
03333       return TAB_SetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_SETITEMW);
03334 
03335     case TCM_DELETEITEM:
03336       return TAB_DeleteItem (infoPtr, (INT)wParam);
03337 
03338     case TCM_DELETEALLITEMS:
03339      return TAB_DeleteAllItems (infoPtr);
03340 
03341     case TCM_GETITEMRECT:
03342      return TAB_GetItemRect (infoPtr, (INT)wParam, (LPRECT)lParam);
03343 
03344     case TCM_GETCURSEL:
03345       return TAB_GetCurSel (infoPtr);
03346 
03347     case TCM_HITTEST:
03348       return TAB_HitTest (infoPtr, (LPTCHITTESTINFO)lParam);
03349 
03350     case TCM_SETCURSEL:
03351       return TAB_SetCurSel (infoPtr, (INT)wParam);
03352 
03353     case TCM_INSERTITEMA:
03354     case TCM_INSERTITEMW:
03355       return TAB_InsertItemT (infoPtr, (INT)wParam, (TCITEMW*)lParam, uMsg == TCM_INSERTITEMW);
03356 
03357     case TCM_SETITEMEXTRA:
03358       return TAB_SetItemExtra (infoPtr, (INT)wParam);
03359 
03360     case TCM_ADJUSTRECT:
03361       return TAB_AdjustRect (infoPtr, (BOOL)wParam, (LPRECT)lParam);
03362 
03363     case TCM_SETITEMSIZE:
03364       return TAB_SetItemSize (infoPtr, (INT)LOWORD(lParam), (INT)HIWORD(lParam));
03365 
03366     case TCM_REMOVEIMAGE:
03367       return TAB_RemoveImage (infoPtr, (INT)wParam);
03368 
03369     case TCM_SETPADDING:
03370       return TAB_SetPadding (infoPtr, lParam);
03371 
03372     case TCM_GETROWCOUNT:
03373       return TAB_GetRowCount(infoPtr);
03374 
03375     case TCM_GETUNICODEFORMAT:
03376       return TAB_GetUnicodeFormat (infoPtr);
03377 
03378     case TCM_SETUNICODEFORMAT:
03379       return TAB_SetUnicodeFormat (infoPtr, (BOOL)wParam);
03380 
03381     case TCM_HIGHLIGHTITEM:
03382       return TAB_HighlightItem (infoPtr, (INT)wParam, (BOOL)LOWORD(lParam));
03383 
03384     case TCM_GETTOOLTIPS:
03385       return TAB_GetToolTips (infoPtr);
03386 
03387     case TCM_SETTOOLTIPS:
03388       return TAB_SetToolTips (infoPtr, (HWND)wParam);
03389 
03390     case TCM_GETCURFOCUS:
03391       return TAB_GetCurFocus (infoPtr);
03392 
03393     case TCM_SETCURFOCUS:
03394       return TAB_SetCurFocus (infoPtr, (INT)wParam);
03395 
03396     case TCM_SETMINTABWIDTH:
03397       return TAB_SetMinTabWidth(infoPtr, (INT)lParam);
03398 
03399     case TCM_DESELECTALL:
03400       return TAB_DeselectAll (infoPtr, (BOOL)wParam);
03401 
03402     case TCM_GETEXTENDEDSTYLE:
03403       return TAB_GetExtendedStyle (infoPtr);
03404 
03405     case TCM_SETEXTENDEDSTYLE:
03406       return TAB_SetExtendedStyle (infoPtr, wParam, lParam);
03407 
03408     case WM_GETFONT:
03409       return TAB_GetFont (infoPtr);
03410 
03411     case WM_SETFONT:
03412       return TAB_SetFont (infoPtr, (HFONT)wParam);
03413 
03414     case WM_CREATE:
03415       return TAB_Create (hwnd, lParam);
03416 
03417     case WM_NCDESTROY:
03418       return TAB_Destroy (infoPtr);
03419 
03420     case WM_GETDLGCODE:
03421       return DLGC_WANTARROWS | DLGC_WANTCHARS;
03422 
03423     case WM_LBUTTONDOWN:
03424       return TAB_LButtonDown (infoPtr, wParam, lParam);
03425 
03426     case WM_LBUTTONUP:
03427       return TAB_LButtonUp (infoPtr);
03428 
03429     case WM_NOTIFY:
03430       return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
03431 
03432     case WM_RBUTTONUP:
03433       TAB_RButtonUp (infoPtr);
03434       return DefWindowProcW (hwnd, uMsg, wParam, lParam);
03435 
03436     case WM_MOUSEMOVE:
03437       return TAB_MouseMove (infoPtr, wParam, lParam);
03438 
03439     case WM_PRINTCLIENT:
03440     case WM_PAINT:
03441       return TAB_Paint (infoPtr, (HDC)wParam);
03442 
03443     case WM_SIZE:
03444       return TAB_Size (infoPtr);
03445 
03446     case WM_SETREDRAW:
03447       return TAB_SetRedraw (infoPtr, (BOOL)wParam);
03448 
03449     case WM_HSCROLL:
03450       return TAB_OnHScroll(infoPtr, (int)LOWORD(wParam), (int)HIWORD(wParam));
03451 
03452     case WM_STYLECHANGED:
03453       return TAB_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
03454 
03455     case WM_SYSCOLORCHANGE:
03456       COMCTL32_RefreshSysColors();
03457       return 0;
03458 
03459     case WM_THEMECHANGED:
03460       return theme_changed (infoPtr);
03461 
03462     case WM_KILLFOCUS:
03463       TAB_KillFocus(infoPtr);
03464     case WM_SETFOCUS:
03465       TAB_FocusChanging(infoPtr);
03466       break;   /* Don't disturb normal focus behavior */
03467 
03468     case WM_KEYDOWN:
03469       return TAB_KeyDown(infoPtr, wParam, lParam);
03470 
03471     case WM_NCHITTEST:
03472       return TAB_NCHitTest(infoPtr, lParam);
03473 
03474     case WM_NCCALCSIZE:
03475       return TAB_NCCalcSize(wParam);
03476 
03477     default:
03478       if (uMsg >= WM_USER && uMsg < WM_APP && !COMCTL32_IsReflectedMessage(uMsg))
03479     WARN("unknown msg %04x wp=%08lx lp=%08lx\n",
03480          uMsg, wParam, lParam);
03481       break;
03482     }
03483     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
03484 }
03485 
03486 
03487 void
03488 TAB_Register (void)
03489 {
03490   WNDCLASSW wndClass;
03491 
03492   ZeroMemory (&wndClass, sizeof(WNDCLASSW));
03493   wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
03494   wndClass.lpfnWndProc   = TAB_WindowProc;
03495   wndClass.cbClsExtra    = 0;
03496   wndClass.cbWndExtra    = sizeof(TAB_INFO *);
03497   wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
03498   wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
03499   wndClass.lpszClassName = WC_TABCONTROLW;
03500 
03501   RegisterClassW (&wndClass);
03502 }
03503 
03504 
03505 void
03506 TAB_Unregister (void)
03507 {
03508     UnregisterClassW (WC_TABCONTROLW, NULL);
03509 }

Generated on Sun May 27 2012 04:23:04 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.