xref: /reactos/dll/win32/comctl32/tab.c (revision cdf90707)
1 /*
2  * Tab control
3  *
4  * Copyright 1998 Anders Carlsson
5  * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6  * Copyright 1999 Francis Beaudet
7  * Copyright 2003 Vitaliy Margolen
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  *
23  * NOTES
24  *
25  * This code was audited for completeness against the documented features
26  * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
27  *
28  * Unless otherwise noted, we believe this code to be complete, as per
29  * the specification mentioned above.
30  * If you discover missing features, or bugs, please note them below.
31  *
32  * TODO:
33  *
34  *  Styles:
35  *   TCS_MULTISELECT - implement for VK_SPACE selection
36  *   TCS_RIGHT
37  *   TCS_RIGHTJUSTIFY
38  *   TCS_SCROLLOPPOSITE
39  *   TCS_SINGLELINE
40  *   TCIF_RTLREADING
41  *
42  *  Extended Styles:
43  *   TCS_EX_REGISTERDROP
44  *
45  *  Notifications:
46  *   NM_RELEASEDCAPTURE
47  *   TCN_FOCUSCHANGE
48  *   TCN_GETOBJECT
49  *
50  *  Macros:
51  *   TabCtrl_AdjustRect
52  *
53  */
54 
55 #include <assert.h>
56 #include <stdarg.h>
57 #include <string.h>
58 
59 #include "windef.h"
60 #include "winbase.h"
61 #include "wingdi.h"
62 #include "winuser.h"
63 #include "winnls.h"
64 #include "commctrl.h"
65 #include "comctl32.h"
66 #include "uxtheme.h"
67 #include "vssym32.h"
68 #include "wine/debug.h"
69 #include <math.h>
70 
71 WINE_DEFAULT_DEBUG_CHANNEL(tab);
72 
73 typedef struct
74 {
75   DWORD  dwState;
76   LPWSTR pszText;
77   INT    iImage;
78   RECT   rect;      /* bounding rectangle of the item relative to the
79                      * leftmost item (the leftmost item, 0, would have a
80                      * "left" member of 0 in this rectangle)
81                      *
82                      * additionally the top member holds the row number
83                      * and bottom is unused and should be 0 */
84   BYTE   extra[1];  /* Space for caller supplied info, variable size */
85 } TAB_ITEM;
86 
87 /* The size of a tab item depends on how much extra data is requested.
88    TCM_INSERTITEM always stores at least LPARAM sized data. */
89 #define EXTRA_ITEM_SIZE(infoPtr) (max((infoPtr)->cbInfo, sizeof(LPARAM)))
90 #define TAB_ITEM_SIZE(infoPtr) FIELD_OFFSET(TAB_ITEM, extra[EXTRA_ITEM_SIZE(infoPtr)])
91 
92 typedef struct
93 {
94   HWND       hwnd;            /* Tab control window */
95   HWND       hwndNotify;      /* notification window (parent) */
96   UINT       uNumItem;        /* number of tab items */
97   UINT       uNumRows;	      /* number of tab rows */
98   INT        tabHeight;       /* height of the tab row */
99   INT        tabWidth;        /* width of tabs */
100   INT        tabMinWidth;     /* minimum width of items */
101   USHORT     uHItemPadding;   /* amount of horizontal padding, in pixels */
102   USHORT     uVItemPadding;   /* amount of vertical padding, in pixels */
103   USHORT     uHItemPadding_s; /* Set amount of horizontal padding, in pixels */
104   USHORT     uVItemPadding_s; /* Set amount of vertical padding, in pixels */
105   HFONT      hFont;           /* handle to the current font */
106   HCURSOR    hcurArrow;       /* handle to the current cursor */
107   HIMAGELIST himl;            /* handle to an image list (may be 0) */
108   HWND       hwndToolTip;     /* handle to tab's tooltip */
109   INT        leftmostVisible; /* Used for scrolling, this member contains
110                                * the index of the first visible item */
111   INT        iSelected;       /* the currently selected item */
112   INT        iHotTracked;     /* the highlighted item under the mouse */
113   INT        uFocus;          /* item which has the focus */
114   BOOL       DoRedraw;        /* flag for redrawing when tab contents is changed*/
115   BOOL       needsScrolling;  /* TRUE if the size of the tabs is greater than
116                                * the size of the control */
117   BOOL       fHeightSet;      /* was the height of the tabs explicitly set? */
118   BOOL       bUnicode;        /* Unicode control? */
119   HWND       hwndUpDown;      /* Updown control used for scrolling */
120   INT        cbInfo;          /* Number of bytes of caller supplied info per tab */
121 
122   DWORD      exStyle;         /* Extended style used, currently:
123                                  TCS_EX_FLATSEPARATORS, TCS_EX_REGISTERDROP */
124   DWORD      dwStyle;         /* the cached window GWL_STYLE */
125 
126   HDPA       items;           /* dynamic array of TAB_ITEM* pointers */
127 } TAB_INFO;
128 
129 /******************************************************************************
130  * Positioning constants
131  */
132 #define SELECTED_TAB_OFFSET     2
133 #define ROUND_CORNER_SIZE       2
134 #define DISPLAY_AREA_PADDINGX   2
135 #define DISPLAY_AREA_PADDINGY   2
136 #define CONTROL_BORDER_SIZEX    2
137 #define CONTROL_BORDER_SIZEY    2
138 #define BUTTON_SPACINGX         3
139 #define BUTTON_SPACINGY         3
140 #define FLAT_BTN_SPACINGX       8
141 #define DEFAULT_MIN_TAB_WIDTH   54
142 #define DEFAULT_PADDING_X       6
143 #define EXTRA_ICON_PADDING      3
144 
145 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongPtrW(hwnd,0))
146 
147 #define GET_DEFAULT_MIN_TAB_WIDTH(infoPtr) (DEFAULT_MIN_TAB_WIDTH - (DEFAULT_PADDING_X - (infoPtr)->uHItemPadding) * 2)
148 
149 /******************************************************************************
150  * Hot-tracking timer constants
151  */
152 #define TAB_HOTTRACK_TIMER            1
153 #define TAB_HOTTRACK_TIMER_INTERVAL   100   /* milliseconds */
154 
155 static const WCHAR themeClass[] = { 'T','a','b',0 };
156 
157 static inline TAB_ITEM* TAB_GetItem(const TAB_INFO *infoPtr, INT i)
158 {
159     assert(i >= 0 && i < infoPtr->uNumItem);
160     return DPA_GetPtr(infoPtr->items, i);
161 }
162 
163 /******************************************************************************
164  * Prototypes
165  */
166 static void TAB_InvalidateTabArea(const TAB_INFO *);
167 static void TAB_EnsureSelectionVisible(TAB_INFO *);
168 static void TAB_DrawItemInterior(const TAB_INFO *, HDC, INT, RECT*);
169 static LRESULT TAB_DeselectAll(TAB_INFO *, BOOL);
170 static BOOL TAB_InternalGetItemRect(const TAB_INFO *, INT, RECT*, RECT*);
171 
172 static BOOL
173 TAB_SendSimpleNotify (const TAB_INFO *infoPtr, UINT code)
174 {
175     NMHDR nmhdr;
176 
177     nmhdr.hwndFrom = infoPtr->hwnd;
178     nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID);
179     nmhdr.code = code;
180 
181     return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
182             nmhdr.idFrom, (LPARAM) &nmhdr);
183 }
184 
185 static void
186 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
187             WPARAM wParam, LPARAM lParam)
188 {
189     MSG msg;
190 
191     msg.hwnd = hwndMsg;
192     msg.message = uMsg;
193     msg.wParam = wParam;
194     msg.lParam = lParam;
195     msg.time = GetMessageTime ();
196     msg.pt.x = (short)LOWORD(GetMessagePos ());
197     msg.pt.y = (short)HIWORD(GetMessagePos ());
198 
199     SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
200 }
201 
202 static void
203 TAB_DumpItemExternalT(const TCITEMW *pti, UINT iItem, BOOL isW)
204 {
205     if (TRACE_ON(tab)) {
206 	TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
207 	      iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
208 	TRACE("external tab %d,   iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
209 	      iItem, pti->iImage, pti->lParam, isW ? debugstr_w(pti->pszText) : debugstr_a((LPSTR)pti->pszText));
210     }
211 }
212 
213 static void
214 TAB_DumpItemInternal(const TAB_INFO *infoPtr, UINT iItem)
215 {
216     if (TRACE_ON(tab)) {
217 	TAB_ITEM *ti = TAB_GetItem(infoPtr, iItem);
218 
219 	TRACE("tab %d, dwState=0x%08x, pszText=%s, iImage=%d\n",
220 	      iItem, ti->dwState, debugstr_w(ti->pszText), ti->iImage);
221 	TRACE("tab %d, rect.left=%d, rect.top(row)=%d\n",
222 	      iItem, ti->rect.left, ti->rect.top);
223     }
224 }
225 
226 /* RETURNS
227  *   the index of the selected tab, or -1 if no tab is selected. */
228 static inline LRESULT TAB_GetCurSel (const TAB_INFO *infoPtr)
229 {
230     TRACE("(%p)\n", infoPtr);
231     return infoPtr->iSelected;
232 }
233 
234 /* RETURNS
235  *   the index of the tab item that has the focus. */
236 static inline LRESULT
237 TAB_GetCurFocus (const TAB_INFO *infoPtr)
238 {
239     TRACE("(%p)\n", infoPtr);
240     return infoPtr->uFocus;
241 }
242 
243 static inline LRESULT TAB_GetToolTips (const TAB_INFO *infoPtr)
244 {
245     TRACE("(%p)\n", infoPtr);
246     return (LRESULT)infoPtr->hwndToolTip;
247 }
248 
249 static inline LRESULT TAB_SetCurSel (TAB_INFO *infoPtr, INT iItem)
250 {
251   INT prevItem = infoPtr->iSelected;
252 
253   TRACE("(%p %d)\n", infoPtr, iItem);
254 
255   if (iItem >= (INT)infoPtr->uNumItem)
256       return -1;
257 
258   if (prevItem != iItem) {
259       if (prevItem != -1)
260           TAB_GetItem(infoPtr, prevItem)->dwState &= ~TCIS_BUTTONPRESSED;
261 
262       if (iItem >= 0)
263       {
264           TAB_GetItem(infoPtr, iItem)->dwState |= TCIS_BUTTONPRESSED;
265           infoPtr->iSelected = iItem;
266           infoPtr->uFocus = iItem;
267       }
268       else
269       {
270           infoPtr->iSelected = -1;
271           infoPtr->uFocus = -1;
272       }
273 
274       TAB_EnsureSelectionVisible(infoPtr);
275       TAB_InvalidateTabArea(infoPtr);
276   }
277 
278   return prevItem;
279 }
280 
281 static LRESULT TAB_SetCurFocus (TAB_INFO *infoPtr, INT iItem)
282 {
283   TRACE("(%p %d)\n", infoPtr, iItem);
284 
285   if (iItem < 0) {
286       infoPtr->uFocus = -1;
287       if (infoPtr->iSelected != -1) {
288 #ifdef __REACTOS__
289           TAB_GetItem(infoPtr, infoPtr->iSelected)->dwState &= ~TCIS_BUTTONPRESSED;
290 #endif
291           infoPtr->iSelected = -1;
292           TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
293           TAB_InvalidateTabArea(infoPtr);
294       }
295   }
296   else if (iItem < infoPtr->uNumItem) {
297     if (infoPtr->dwStyle & TCS_BUTTONS) {
298       /* set focus to new item, leave selection as is */
299       if (infoPtr->uFocus != iItem) {
300         INT prev_focus = infoPtr->uFocus;
301         RECT r;
302 
303         infoPtr->uFocus = iItem;
304 
305         if (prev_focus != infoPtr->iSelected) {
306           if (TAB_InternalGetItemRect(infoPtr, prev_focus, &r, NULL))
307             InvalidateRect(infoPtr->hwnd, &r, FALSE);
308         }
309 
310         if (TAB_InternalGetItemRect(infoPtr, iItem, &r, NULL))
311             InvalidateRect(infoPtr->hwnd, &r, FALSE);
312 
313         TAB_SendSimpleNotify(infoPtr, TCN_FOCUSCHANGE);
314       }
315     } else {
316 #ifdef __REACTOS__
317       INT oldItem = infoPtr->iSelected;
318 #endif
319       INT oldFocus = infoPtr->uFocus;
320       if (infoPtr->iSelected != iItem || oldFocus == -1 ) {
321         infoPtr->uFocus = iItem;
322         if (oldFocus != -1) {
323           if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))  {
324 #ifdef __REACTOS__
325             if (oldItem != -1)
326               TAB_GetItem(infoPtr, oldItem)->dwState &= ~TCIS_BUTTONPRESSED;
327 #endif
328             infoPtr->iSelected = iItem;
329 #ifdef __REACTOS__
330             TAB_GetItem(infoPtr, iItem)->dwState |= TCIS_BUTTONPRESSED;
331 #endif
332             TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
333           }
334           else
335             infoPtr->iSelected = iItem;
336           TAB_EnsureSelectionVisible(infoPtr);
337           TAB_InvalidateTabArea(infoPtr);
338         }
339       }
340     }
341   }
342   return 0;
343 }
344 
345 static inline LRESULT
346 TAB_SetToolTips (TAB_INFO *infoPtr, HWND hwndToolTip)
347 {
348     TRACE("%p %p\n", infoPtr, hwndToolTip);
349     infoPtr->hwndToolTip = hwndToolTip;
350     return 0;
351 }
352 
353 static inline LRESULT
354 TAB_SetPadding (TAB_INFO *infoPtr, LPARAM lParam)
355 {
356     TRACE("(%p %d %d)\n", infoPtr, LOWORD(lParam), HIWORD(lParam));
357     infoPtr->uHItemPadding_s = LOWORD(lParam);
358     infoPtr->uVItemPadding_s = HIWORD(lParam);
359 
360     return 0;
361 }
362 
363 /******************************************************************************
364  * TAB_InternalGetItemRect
365  *
366  * This method will calculate the rectangle representing a given tab item in
367  * client coordinates. This method takes scrolling into account.
368  *
369  * This method returns TRUE if the item is visible in the window and FALSE
370  * if it is completely outside the client area.
371  */
372 static BOOL TAB_InternalGetItemRect(
373   const TAB_INFO* infoPtr,
374   INT         itemIndex,
375   RECT*       itemRect,
376   RECT*       selectedRect)
377 {
378   RECT tmpItemRect,clientRect;
379 
380   /* Perform a sanity check and a trivial visibility check. */
381   if ( (infoPtr->uNumItem <= 0) ||
382        (itemIndex >= infoPtr->uNumItem) ||
383        (!(((infoPtr->dwStyle & TCS_MULTILINE) || (infoPtr->dwStyle & TCS_VERTICAL))) &&
384          (itemIndex < infoPtr->leftmostVisible)))
385     {
386         TRACE("Not Visible\n");
387         SetRect(itemRect, 0, 0, 0, infoPtr->tabHeight);
388         SetRectEmpty(selectedRect);
389         return FALSE;
390     }
391 
392   /*
393    * Avoid special cases in this procedure by assigning the "out"
394    * parameters if the caller didn't supply them
395    */
396   if (itemRect == NULL)
397     itemRect = &tmpItemRect;
398 
399   /* Retrieve the unmodified item rect. */
400   *itemRect = TAB_GetItem(infoPtr,itemIndex)->rect;
401 
402   /* calculate the times bottom and top based on the row */
403   GetClientRect(infoPtr->hwnd, &clientRect);
404 
405   if ((infoPtr->dwStyle & TCS_BOTTOM) && (infoPtr->dwStyle & TCS_VERTICAL))
406   {
407     itemRect->right  = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * infoPtr->tabHeight -
408                        ((infoPtr->dwStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
409     itemRect->left   = itemRect->right - infoPtr->tabHeight;
410   }
411   else if (infoPtr->dwStyle & TCS_VERTICAL)
412   {
413     itemRect->left   = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * infoPtr->tabHeight +
414                        ((infoPtr->dwStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
415     itemRect->right  = itemRect->left + infoPtr->tabHeight;
416   }
417   else if (infoPtr->dwStyle & TCS_BOTTOM)
418   {
419     itemRect->bottom = clientRect.bottom - itemRect->top * infoPtr->tabHeight -
420                        ((infoPtr->dwStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
421     itemRect->top    = itemRect->bottom - infoPtr->tabHeight;
422   }
423   else /* not TCS_BOTTOM and not TCS_VERTICAL */
424   {
425     itemRect->top    = clientRect.top + itemRect->top * infoPtr->tabHeight +
426                        ((infoPtr->dwStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
427     itemRect->bottom = itemRect->top + infoPtr->tabHeight;
428  }
429 
430   /*
431    * "scroll" it to make sure the item at the very left of the
432    * tab control is the leftmost visible tab.
433    */
434   if(infoPtr->dwStyle & TCS_VERTICAL)
435   {
436     OffsetRect(itemRect,
437 	     0,
438 	     -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.top);
439 
440     /*
441      * Move the rectangle so the first item is slightly offset from
442      * the bottom of the tab control.
443      */
444     OffsetRect(itemRect,
445 	     0,
446 	     SELECTED_TAB_OFFSET);
447 
448   } else
449   {
450     OffsetRect(itemRect,
451 	     -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.left,
452 	     0);
453 
454     /*
455      * Move the rectangle so the first item is slightly offset from
456      * the left of the tab control.
457      */
458     OffsetRect(itemRect,
459 	     SELECTED_TAB_OFFSET,
460 	     0);
461   }
462   TRACE("item %d tab h=%d, rect=(%s)\n",
463         itemIndex, infoPtr->tabHeight, wine_dbgstr_rect(itemRect));
464 
465   /* Now, calculate the position of the item as if it were selected. */
466   if (selectedRect!=NULL)
467   {
468     *selectedRect = *itemRect;
469 
470     /* The rectangle of a selected item is a bit wider. */
471     if(infoPtr->dwStyle & TCS_VERTICAL)
472       InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
473     else
474       InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
475 
476     /* If it also a bit higher. */
477     if ((infoPtr->dwStyle & TCS_BOTTOM) && (infoPtr->dwStyle & TCS_VERTICAL))
478     {
479       selectedRect->left   -= 2; /* the border is thicker on the right */
480       selectedRect->right  += SELECTED_TAB_OFFSET;
481     }
482     else if (infoPtr->dwStyle & TCS_VERTICAL)
483     {
484       selectedRect->left   -= SELECTED_TAB_OFFSET;
485       selectedRect->right  += 1;
486     }
487     else if (infoPtr->dwStyle & TCS_BOTTOM)
488     {
489       selectedRect->bottom += SELECTED_TAB_OFFSET;
490     }
491     else /* not TCS_BOTTOM and not TCS_VERTICAL */
492     {
493       selectedRect->top    -= SELECTED_TAB_OFFSET;
494       selectedRect->bottom -= 1;
495     }
496   }
497 
498   /* Check for visibility */
499   if (infoPtr->dwStyle & TCS_VERTICAL)
500     return (itemRect->top < clientRect.bottom) && (itemRect->bottom > clientRect.top);
501   else
502     return (itemRect->left < clientRect.right) && (itemRect->right > clientRect.left);
503 }
504 
505 static inline BOOL
506 TAB_GetItemRect(const TAB_INFO *infoPtr, INT item, RECT *rect)
507 {
508   TRACE("(%p, %d, %p)\n", infoPtr, item, rect);
509   return TAB_InternalGetItemRect(infoPtr, item, rect, NULL);
510 }
511 
512 /******************************************************************************
513  * TAB_KeyDown
514  *
515  * This method is called to handle keyboard input
516  */
517 static LRESULT TAB_KeyDown(TAB_INFO* infoPtr, WPARAM keyCode, LPARAM lParam)
518 {
519   INT newItem = -1;
520   NMTCKEYDOWN nm;
521 
522   /* TCN_KEYDOWN notification sent always */
523   nm.hdr.hwndFrom = infoPtr->hwnd;
524   nm.hdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID);
525   nm.hdr.code = TCN_KEYDOWN;
526   nm.wVKey = keyCode;
527   nm.flags = lParam;
528   SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nm.hdr.idFrom, (LPARAM)&nm);
529 
530   switch (keyCode)
531   {
532     case VK_LEFT:
533       newItem = infoPtr->uFocus - 1;
534       break;
535     case VK_RIGHT:
536       newItem = infoPtr->uFocus + 1;
537       break;
538   }
539 
540   /* If we changed to a valid item, change focused item */
541   if (newItem >= 0 && newItem < infoPtr->uNumItem && infoPtr->uFocus != newItem)
542       TAB_SetCurFocus(infoPtr, newItem);
543 
544   return 0;
545 }
546 
547 /*
548  * WM_KILLFOCUS handler
549  */
550 static void TAB_KillFocus(TAB_INFO *infoPtr)
551 {
552   /* clear current focused item back to selected for TCS_BUTTONS */
553   if ((infoPtr->dwStyle & TCS_BUTTONS) && (infoPtr->uFocus != infoPtr->iSelected))
554   {
555     RECT r;
556 
557     if (TAB_InternalGetItemRect(infoPtr, infoPtr->uFocus, &r, NULL))
558       InvalidateRect(infoPtr->hwnd, &r, FALSE);
559 
560     infoPtr->uFocus = infoPtr->iSelected;
561   }
562 }
563 
564 /******************************************************************************
565  * TAB_FocusChanging
566  *
567  * This method is called whenever the focus goes in or out of this control
568  * it is used to update the visual state of the control.
569  */
570 static void TAB_FocusChanging(const TAB_INFO *infoPtr)
571 {
572   RECT      selectedRect;
573   BOOL      isVisible;
574 
575   /*
576    * Get the rectangle for the item.
577    */
578   isVisible = TAB_InternalGetItemRect(infoPtr,
579 				      infoPtr->uFocus,
580 				      NULL,
581 				      &selectedRect);
582 
583   /*
584    * If the rectangle is not completely invisible, invalidate that
585    * portion of the window.
586    */
587   if (isVisible)
588   {
589     TRACE("invalidate (%s)\n", wine_dbgstr_rect(&selectedRect));
590     InvalidateRect(infoPtr->hwnd, &selectedRect, TRUE);
591   }
592 }
593 
594 static INT TAB_InternalHitTest (const TAB_INFO *infoPtr, POINT pt, UINT *flags)
595 {
596   RECT rect;
597   INT iCount;
598 
599   for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
600   {
601     TAB_InternalGetItemRect(infoPtr, iCount, &rect, NULL);
602 
603     if (PtInRect(&rect, pt))
604     {
605       *flags = TCHT_ONITEM;
606       return iCount;
607     }
608   }
609 
610   *flags = TCHT_NOWHERE;
611   return -1;
612 }
613 
614 static inline LRESULT
615 TAB_HitTest (const TAB_INFO *infoPtr, LPTCHITTESTINFO lptest)
616 {
617   TRACE("(%p, %p)\n", infoPtr, lptest);
618   return TAB_InternalHitTest (infoPtr, lptest->pt, &lptest->flags);
619 }
620 
621 /******************************************************************************
622  * TAB_NCHitTest
623  *
624  * Napster v2b5 has a tab control for its main navigation which has a client
625  * area that covers the whole area of the dialog pages.
626  * That's why it receives all msgs for that area and the underlying dialog ctrls
627  * are dead.
628  * So I decided that we should handle WM_NCHITTEST here and return
629  * HTTRANSPARENT if we don't hit the tab control buttons.
630  * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
631  * doesn't do it that way. Maybe depends on tab control styles ?
632  */
633 static inline LRESULT
634 TAB_NCHitTest (const TAB_INFO *infoPtr, LPARAM lParam)
635 {
636   POINT pt;
637   UINT dummyflag;
638 
639   pt.x = (short)LOWORD(lParam);
640   pt.y = (short)HIWORD(lParam);
641   ScreenToClient(infoPtr->hwnd, &pt);
642 
643   if (TAB_InternalHitTest(infoPtr, pt, &dummyflag) == -1)
644     return HTTRANSPARENT;
645   else
646     return HTCLIENT;
647 }
648 
649 static LRESULT
650 TAB_LButtonDown (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
651 {
652   POINT pt;
653   INT newItem;
654   UINT dummy;
655 
656   if (infoPtr->hwndToolTip)
657     TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
658 		    WM_LBUTTONDOWN, wParam, lParam);
659 
660   if (!(infoPtr->dwStyle & TCS_FOCUSNEVER)) {
661     SetFocus (infoPtr->hwnd);
662   }
663 
664   if (infoPtr->hwndToolTip)
665     TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
666 		    WM_LBUTTONDOWN, wParam, lParam);
667 
668   pt.x = (short)LOWORD(lParam);
669   pt.y = (short)HIWORD(lParam);
670 
671   newItem = TAB_InternalHitTest (infoPtr, pt, &dummy);
672 
673   TRACE("On Tab, item %d\n", newItem);
674 
675   if ((newItem != -1) && (infoPtr->iSelected != newItem))
676   {
677     if ((infoPtr->dwStyle & TCS_BUTTONS) && (infoPtr->dwStyle & TCS_MULTISELECT) &&
678         (wParam & MK_CONTROL))
679     {
680       RECT r;
681 
682       /* toggle multiselection */
683       TAB_GetItem(infoPtr, newItem)->dwState ^= TCIS_BUTTONPRESSED;
684       if (TAB_InternalGetItemRect (infoPtr, newItem, &r, NULL))
685         InvalidateRect (infoPtr->hwnd, &r, TRUE);
686     }
687     else
688     {
689       INT i;
690       BOOL pressed = FALSE;
691 
692       /* any button pressed ? */
693       for (i = 0; i < infoPtr->uNumItem; i++)
694         if ((TAB_GetItem (infoPtr, i)->dwState & TCIS_BUTTONPRESSED) &&
695             (infoPtr->iSelected != i))
696         {
697           pressed = TRUE;
698           break;
699         }
700 
701       if (TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
702         return 0;
703 
704       if (pressed)
705         TAB_DeselectAll (infoPtr, FALSE);
706       else
707         TAB_SetCurSel(infoPtr, newItem);
708 
709       TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
710     }
711   }
712 
713   return 0;
714 }
715 
716 static inline LRESULT
717 TAB_LButtonUp (const TAB_INFO *infoPtr)
718 {
719   TAB_SendSimpleNotify(infoPtr, NM_CLICK);
720 
721   return 0;
722 }
723 
724 static inline void
725 TAB_RButtonUp (const TAB_INFO *infoPtr)
726 {
727   TAB_SendSimpleNotify(infoPtr, NM_RCLICK);
728 }
729 
730 /******************************************************************************
731  * TAB_DrawLoneItemInterior
732  *
733  * This calls TAB_DrawItemInterior.  However, TAB_DrawItemInterior is normally
734  * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
735  * up the device context and font.  This routine does the same setup but
736  * only calls TAB_DrawItemInterior for the single specified item.
737  */
738 static void
739 TAB_DrawLoneItemInterior(const TAB_INFO* infoPtr, int iItem)
740 {
741   HDC hdc = GetDC(infoPtr->hwnd);
742   RECT r, rC;
743 
744   /* Clip UpDown control to not draw over it */
745   if (infoPtr->needsScrolling)
746   {
747     GetWindowRect(infoPtr->hwnd, &rC);
748     GetWindowRect(infoPtr->hwndUpDown, &r);
749     ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top);
750   }
751   TAB_DrawItemInterior(infoPtr, hdc, iItem, NULL);
752   ReleaseDC(infoPtr->hwnd, hdc);
753 }
754 
755 /* update a tab after hottracking - invalidate it or just redraw the interior,
756  * based on whether theming is used or not */
757 static inline void hottrack_refresh(const TAB_INFO *infoPtr, int tabIndex)
758 {
759     if (tabIndex == -1) return;
760 
761     if (GetWindowTheme (infoPtr->hwnd))
762     {
763         RECT rect;
764         TAB_InternalGetItemRect(infoPtr, tabIndex, &rect, NULL);
765         InvalidateRect (infoPtr->hwnd, &rect, FALSE);
766     }
767     else
768         TAB_DrawLoneItemInterior(infoPtr, tabIndex);
769 }
770 
771 /******************************************************************************
772  * TAB_HotTrackTimerProc
773  *
774  * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
775  * timer is setup so we can check if the mouse is moved out of our window.
776  * (We don't get an event when the mouse leaves, the mouse-move events just
777  * stop being delivered to our window and just start being delivered to
778  * another window.)  This function is called when the timer triggers so
779  * we can check if the mouse has left our window.  If so, we un-highlight
780  * the hot-tracked tab.
781  */
782 static void CALLBACK
783 TAB_HotTrackTimerProc
784   (
785   HWND hwnd,    /* handle of window for timer messages */
786   UINT uMsg,    /* WM_TIMER message */
787   UINT_PTR idEvent, /* timer identifier */
788   DWORD dwTime  /* current system time */
789   )
790 {
791   TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
792 
793   if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
794   {
795     POINT pt;
796 
797     /*
798     ** If we can't get the cursor position, or if the cursor is outside our
799     ** window, we un-highlight the hot-tracked tab.  Note that the cursor is
800     ** "outside" even if it is within our bounding rect if another window
801     ** overlaps.  Note also that the case where the cursor stayed within our
802     ** window but has moved off the hot-tracked tab will be handled by the
803     ** WM_MOUSEMOVE event.
804     */
805     if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
806     {
807       /* Redraw iHotTracked to look normal */
808       INT iRedraw = infoPtr->iHotTracked;
809       infoPtr->iHotTracked = -1;
810       hottrack_refresh (infoPtr, iRedraw);
811 
812       /* Kill this timer */
813       KillTimer(hwnd, TAB_HOTTRACK_TIMER);
814     }
815   }
816 }
817 
818 /******************************************************************************
819  * TAB_RecalcHotTrack
820  *
821  * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
822  * should be highlighted.  This function determines which tab in a tab control,
823  * if any, is under the mouse and records that information.  The caller may
824  * supply output parameters to receive the item number of the tab item which
825  * was highlighted but isn't any longer and of the tab item which is now
826  * highlighted but wasn't previously.  The caller can use this information to
827  * selectively redraw those tab items.
828  *
829  * If the caller has a mouse position, it can supply it through the pos
830  * parameter.  For example, TAB_MouseMove does this.  Otherwise, the caller
831  * supplies NULL and this function determines the current mouse position
832  * itself.
833  */
834 static void
835 TAB_RecalcHotTrack
836   (
837   TAB_INFO*       infoPtr,
838   const LPARAM*   pos,
839   int*            out_redrawLeave,
840   int*            out_redrawEnter
841   )
842 {
843   int item = -1;
844 
845 
846   if (out_redrawLeave != NULL)
847     *out_redrawLeave = -1;
848   if (out_redrawEnter != NULL)
849     *out_redrawEnter = -1;
850 
851   if ((infoPtr->dwStyle & TCS_HOTTRACK) || GetWindowTheme(infoPtr->hwnd))
852   {
853     POINT pt;
854     UINT  flags;
855 
856     if (pos == NULL)
857     {
858       GetCursorPos(&pt);
859       ScreenToClient(infoPtr->hwnd, &pt);
860     }
861     else
862     {
863       pt.x = (short)LOWORD(*pos);
864       pt.y = (short)HIWORD(*pos);
865     }
866 
867     item = TAB_InternalHitTest(infoPtr, pt, &flags);
868   }
869 
870   if (item != infoPtr->iHotTracked)
871   {
872     if (infoPtr->iHotTracked >= 0)
873     {
874       /* Mark currently hot-tracked to be redrawn to look normal */
875       if (out_redrawLeave != NULL)
876         *out_redrawLeave = infoPtr->iHotTracked;
877 
878       if (item < 0)
879       {
880         /* Kill timer which forces recheck of mouse pos */
881         KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
882       }
883     }
884     else
885     {
886       /* Start timer so we recheck mouse pos */
887       UINT timerID = SetTimer
888         (
889         infoPtr->hwnd,
890         TAB_HOTTRACK_TIMER,
891         TAB_HOTTRACK_TIMER_INTERVAL,
892         TAB_HotTrackTimerProc
893         );
894 
895       if (timerID == 0)
896         return; /* Hot tracking not available */
897     }
898 
899     infoPtr->iHotTracked = item;
900 
901     if (item >= 0)
902     {
903 	/* Mark new hot-tracked to be redrawn to look highlighted */
904       if (out_redrawEnter != NULL)
905         *out_redrawEnter = item;
906     }
907   }
908 }
909 
910 /******************************************************************************
911  * TAB_MouseMove
912  *
913  * Handles the mouse-move event.  Updates tooltips.  Updates hot-tracking.
914  */
915 static LRESULT
916 TAB_MouseMove (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
917 {
918   int redrawLeave;
919   int redrawEnter;
920 
921   if (infoPtr->hwndToolTip)
922     TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
923 		    WM_LBUTTONDOWN, wParam, lParam);
924 
925   /* Determine which tab to highlight.  Redraw tabs which change highlight
926   ** status. */
927   TAB_RecalcHotTrack(infoPtr, &lParam, &redrawLeave, &redrawEnter);
928 
929   hottrack_refresh (infoPtr, redrawLeave);
930   hottrack_refresh (infoPtr, redrawEnter);
931 
932   return 0;
933 }
934 
935 /******************************************************************************
936  * TAB_AdjustRect
937  *
938  * Calculates the tab control's display area given the window rectangle or
939  * the window rectangle given the requested display rectangle.
940  */
941 static LRESULT TAB_AdjustRect(const TAB_INFO *infoPtr, WPARAM fLarger, LPRECT prc)
942 {
943     LONG *iRightBottom, *iLeftTop;
944 
945     TRACE ("hwnd=%p fLarger=%ld (%s)\n", infoPtr->hwnd, fLarger,
946            wine_dbgstr_rect(prc));
947 
948     if (!prc) return -1;
949 
950     if(infoPtr->dwStyle & TCS_VERTICAL)
951     {
952 	iRightBottom = &(prc->right);
953 	iLeftTop     = &(prc->left);
954     }
955     else
956     {
957 	iRightBottom = &(prc->bottom);
958 	iLeftTop     = &(prc->top);
959     }
960 
961     if (fLarger) /* Go from display rectangle */
962     {
963         /* Add the height of the tabs. */
964 	if (infoPtr->dwStyle & TCS_BOTTOM)
965 	    *iRightBottom += infoPtr->tabHeight * infoPtr->uNumRows;
966 	else
967 	    *iLeftTop -= infoPtr->tabHeight * infoPtr->uNumRows +
968 			 ((infoPtr->dwStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
969 
970 	/* Inflate the rectangle for the padding */
971 	InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
972 
973 	/* Inflate for the border */
974 	InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEY);
975     }
976     else /* Go from window rectangle. */
977     {
978 	/* Deflate the rectangle for the border */
979 	InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEY);
980 
981 	/* Deflate the rectangle for the padding */
982 	InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
983 
984 	/* Remove the height of the tabs. */
985 	if (infoPtr->dwStyle & TCS_BOTTOM)
986 	    *iRightBottom -= infoPtr->tabHeight * infoPtr->uNumRows;
987 	else
988 	    *iLeftTop += (infoPtr->tabHeight) * infoPtr->uNumRows +
989 			 ((infoPtr->dwStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
990     }
991 
992   return 0;
993 }
994 
995 /******************************************************************************
996  * TAB_OnHScroll
997  *
998  * This method will handle the notification from the scroll control and
999  * perform the scrolling operation on the tab control.
1000  */
1001 static LRESULT TAB_OnHScroll(TAB_INFO *infoPtr, int nScrollCode, int nPos)
1002 {
1003   if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
1004   {
1005      if(nPos < infoPtr->leftmostVisible)
1006         infoPtr->leftmostVisible--;
1007      else
1008         infoPtr->leftmostVisible++;
1009 
1010      TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
1011      TAB_InvalidateTabArea(infoPtr);
1012      SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
1013                    MAKELONG(infoPtr->leftmostVisible, 0));
1014    }
1015 
1016    return 0;
1017 }
1018 
1019 /******************************************************************************
1020  * TAB_SetupScrolling
1021  *
1022  * This method will check the current scrolling state and make sure the
1023  * scrolling control is displayed (or not).
1024  */
1025 static void TAB_SetupScrolling(
1026   TAB_INFO*   infoPtr,
1027   const RECT* clientRect)
1028 {
1029   static const WCHAR emptyW[] = { 0 };
1030   INT maxRange = 0;
1031 
1032   if (infoPtr->needsScrolling)
1033   {
1034     RECT controlPos;
1035     INT vsize, tabwidth;
1036 
1037     /*
1038      * Calculate the position of the scroll control.
1039      */
1040     controlPos.right = clientRect->right;
1041     controlPos.left  = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
1042 
1043     if (infoPtr->dwStyle & TCS_BOTTOM)
1044     {
1045       controlPos.top    = clientRect->bottom - infoPtr->tabHeight;
1046       controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
1047     }
1048     else
1049     {
1050       controlPos.bottom = clientRect->top + infoPtr->tabHeight;
1051       controlPos.top    = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
1052     }
1053 
1054     /*
1055      * If we don't have a scroll control yet, we want to create one.
1056      * If we have one, we want to make sure it's positioned properly.
1057      */
1058     if (infoPtr->hwndUpDown==0)
1059     {
1060       infoPtr->hwndUpDown = CreateWindowW(UPDOWN_CLASSW, emptyW,
1061 					  WS_VISIBLE | WS_CHILD | UDS_HORZ,
1062 					  controlPos.left, controlPos.top,
1063 					  controlPos.right - controlPos.left,
1064 					  controlPos.bottom - controlPos.top,
1065 					  infoPtr->hwnd, NULL, NULL, NULL);
1066     }
1067     else
1068     {
1069       SetWindowPos(infoPtr->hwndUpDown,
1070 		   NULL,
1071 		   controlPos.left, controlPos.top,
1072 		   controlPos.right - controlPos.left,
1073 		   controlPos.bottom - controlPos.top,
1074 		   SWP_SHOWWINDOW | SWP_NOZORDER);
1075     }
1076 
1077     /* Now calculate upper limit of the updown control range.
1078      * We do this by calculating how many tabs will be offscreen when the
1079      * last tab is visible.
1080      */
1081     if(infoPtr->uNumItem)
1082     {
1083        vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
1084        maxRange = infoPtr->uNumItem;
1085        tabwidth = TAB_GetItem(infoPtr, infoPtr->uNumItem - 1)->rect.right;
1086 
1087        for(; maxRange > 0; maxRange--)
1088        {
1089           if(tabwidth - TAB_GetItem(infoPtr,maxRange - 1)->rect.left > vsize)
1090              break;
1091        }
1092 
1093        if(maxRange == infoPtr->uNumItem)
1094           maxRange--;
1095     }
1096   }
1097   else
1098   {
1099     /* If we once had a scroll control... hide it */
1100     if (infoPtr->hwndUpDown)
1101       ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1102   }
1103   if (infoPtr->hwndUpDown)
1104      SendMessageW(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1105 }
1106 
1107 /******************************************************************************
1108  * TAB_SetItemBounds
1109  *
1110  * This method will calculate the position rectangles of all the items in the
1111  * control. The rectangle calculated starts at 0 for the first item in the
1112  * list and ignores scrolling and selection.
1113  * It also uses the current font to determine the height of the tab row and
1114  * it checks if all the tabs fit in the client area of the window. If they
1115  * don't, a scrolling control is added.
1116  */
1117 static void TAB_SetItemBounds (TAB_INFO *infoPtr)
1118 {
1119   TEXTMETRICW fontMetrics;
1120   UINT        curItem;
1121   INT         curItemLeftPos;
1122   INT         curItemRowCount;
1123   HFONT       hFont, hOldFont;
1124   HDC         hdc;
1125   RECT        clientRect;
1126   INT         iTemp;
1127   RECT*       rcItem;
1128   INT         iIndex;
1129   INT         icon_width = 0;
1130 
1131   /*
1132    * We need to get text information so we need a DC and we need to select
1133    * a font.
1134    */
1135   hdc = GetDC(infoPtr->hwnd);
1136 
1137   hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1138   hOldFont = SelectObject (hdc, hFont);
1139 
1140   /*
1141    * We will base the rectangle calculations on the client rectangle
1142    * of the control.
1143    */
1144   GetClientRect(infoPtr->hwnd, &clientRect);
1145 
1146   /* if TCS_VERTICAL then swap the height and width so this code places the
1147      tabs along the top of the rectangle and we can just rotate them after
1148      rather than duplicate all of the below code */
1149   if(infoPtr->dwStyle & TCS_VERTICAL)
1150   {
1151      iTemp = clientRect.bottom;
1152      clientRect.bottom = clientRect.right;
1153      clientRect.right = iTemp;
1154   }
1155 
1156   /* Now use hPadding and vPadding */
1157   infoPtr->uHItemPadding = infoPtr->uHItemPadding_s;
1158   infoPtr->uVItemPadding = infoPtr->uVItemPadding_s;
1159 
1160   /* The leftmost item will be "0" aligned */
1161   curItemLeftPos = 0;
1162   curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1163 
1164   if (!(infoPtr->fHeightSet))
1165   {
1166     int item_height;
1167     INT icon_height = 0, cx;
1168 
1169     /* Use the current font to determine the height of a tab. */
1170     GetTextMetricsW(hdc, &fontMetrics);
1171 
1172     /* Get the icon height */
1173     if (infoPtr->himl)
1174       ImageList_GetIconSize(infoPtr->himl, &cx, &icon_height);
1175 
1176     /* Take the highest between font or icon */
1177     if (fontMetrics.tmHeight > icon_height)
1178       item_height = fontMetrics.tmHeight + 2;
1179     else
1180       item_height = icon_height;
1181 
1182     /*
1183      * Make sure there is enough space for the letters + icon + growing the
1184      * selected item + extra space for the selected item.
1185      */
1186     infoPtr->tabHeight = item_height +
1187 	                 ((infoPtr->dwStyle & TCS_BUTTONS) ? 2 : 1) *
1188                           infoPtr->uVItemPadding;
1189 
1190     TRACE("tabH=%d, tmH=%d, iconh=%d\n",
1191 	  infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1192   }
1193 
1194   TRACE("client right=%d\n", clientRect.right);
1195 
1196   /* Get the icon width */
1197   if (infoPtr->himl)
1198   {
1199     INT cy;
1200 
1201     ImageList_GetIconSize(infoPtr->himl, &icon_width, &cy);
1202 
1203     if (infoPtr->dwStyle & TCS_FIXEDWIDTH)
1204       icon_width += 4;
1205     else
1206       /* Add padding if icon is present */
1207       icon_width += infoPtr->uHItemPadding;
1208   }
1209 
1210   for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1211   {
1212     TAB_ITEM *curr = TAB_GetItem(infoPtr, curItem);
1213 
1214     /* Set the leftmost position of the tab. */
1215     curr->rect.left = curItemLeftPos;
1216 
1217     if (infoPtr->dwStyle & TCS_FIXEDWIDTH)
1218     {
1219       curr->rect.right = curr->rect.left +
1220         max(infoPtr->tabWidth, icon_width);
1221     }
1222     else if (!curr->pszText)
1223     {
1224       /* If no text use minimum tab width including padding. */
1225       if (infoPtr->tabMinWidth < 0)
1226         curr->rect.right = curr->rect.left + GET_DEFAULT_MIN_TAB_WIDTH(infoPtr);
1227       else
1228       {
1229         curr->rect.right = curr->rect.left + infoPtr->tabMinWidth;
1230 
1231         /* Add extra padding if icon is present */
1232         if (infoPtr->himl && infoPtr->tabMinWidth > 0 && infoPtr->tabMinWidth < DEFAULT_MIN_TAB_WIDTH
1233             && infoPtr->uHItemPadding > 1)
1234           curr->rect.right += EXTRA_ICON_PADDING * (infoPtr->uHItemPadding-1);
1235       }
1236     }
1237     else
1238     {
1239       int tabwidth;
1240       SIZE size;
1241       /* Calculate how wide the tab is depending on the text it contains */
1242       GetTextExtentPoint32W(hdc, curr->pszText,
1243                             lstrlenW(curr->pszText), &size);
1244 
1245       tabwidth = size.cx + icon_width + 2 * infoPtr->uHItemPadding;
1246 
1247       if (infoPtr->tabMinWidth < 0)
1248         tabwidth = max(tabwidth, GET_DEFAULT_MIN_TAB_WIDTH(infoPtr));
1249       else
1250         tabwidth = max(tabwidth, infoPtr->tabMinWidth);
1251 
1252       curr->rect.right = curr->rect.left + tabwidth;
1253       TRACE("for <%s>, rect %s\n", debugstr_w(curr->pszText), wine_dbgstr_rect(&curr->rect));
1254     }
1255 
1256     /*
1257      * Check if this is a multiline tab control and if so
1258      * check to see if we should wrap the tabs
1259      *
1260      * Wrap all these tabs. We will arrange them evenly later.
1261      *
1262      */
1263 
1264     if (((infoPtr->dwStyle & TCS_MULTILINE) || (infoPtr->dwStyle & TCS_VERTICAL)) &&
1265         (curr->rect.right >
1266 	(clientRect.right - CONTROL_BORDER_SIZEX - DISPLAY_AREA_PADDINGX)))
1267     {
1268         curr->rect.right -= curr->rect.left;
1269 
1270 	curr->rect.left = 0;
1271         curItemRowCount++;
1272 	TRACE("wrapping <%s>, rect %s\n", debugstr_w(curr->pszText), wine_dbgstr_rect(&curr->rect));
1273     }
1274 
1275     curr->rect.bottom = 0;
1276     curr->rect.top = curItemRowCount - 1;
1277 
1278     TRACE("Rect: %s\n", wine_dbgstr_rect(&curr->rect));
1279 
1280     /*
1281      * The leftmost position of the next item is the rightmost position
1282      * of this one.
1283      */
1284     if (infoPtr->dwStyle & TCS_BUTTONS)
1285     {
1286       curItemLeftPos = curr->rect.right + BUTTON_SPACINGX;
1287       if (infoPtr->dwStyle & TCS_FLATBUTTONS)
1288         curItemLeftPos += FLAT_BTN_SPACINGX;
1289     }
1290     else
1291       curItemLeftPos = curr->rect.right;
1292   }
1293 
1294   if (!((infoPtr->dwStyle & TCS_MULTILINE) || (infoPtr->dwStyle & TCS_VERTICAL)))
1295   {
1296     /*
1297      * Check if we need a scrolling control.
1298      */
1299     infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1300                                clientRect.right);
1301 
1302     /* Don't need scrolling, then update infoPtr->leftmostVisible */
1303     if(!infoPtr->needsScrolling)
1304       infoPtr->leftmostVisible = 0;
1305   }
1306   else
1307   {
1308     /*
1309      * No scrolling in Multiline or Vertical styles.
1310      */
1311     infoPtr->needsScrolling = FALSE;
1312     infoPtr->leftmostVisible = 0;
1313   }
1314   TAB_SetupScrolling(infoPtr, &clientRect);
1315 
1316   /* Set the number of rows */
1317   infoPtr->uNumRows = curItemRowCount;
1318 
1319   /* Arrange all tabs evenly if style says so */
1320    if (!(infoPtr->dwStyle & TCS_RAGGEDRIGHT) &&
1321        ((infoPtr->dwStyle & TCS_MULTILINE) || (infoPtr->dwStyle & TCS_VERTICAL)) &&
1322        (infoPtr->uNumItem > 0) &&
1323        (infoPtr->uNumRows > 1))
1324    {
1325       INT tabPerRow,remTab,iRow;
1326       UINT iItm;
1327       INT iCount=0;
1328 
1329       /*
1330        * Ok windows tries to even out the rows. place the same
1331        * number of tabs in each row. So lets give that a shot
1332        */
1333 
1334       tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1335       remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1336 
1337       for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1338            iItm<infoPtr->uNumItem;
1339            iItm++,iCount++)
1340       {
1341           /* normalize the current rect */
1342           TAB_ITEM *curr = TAB_GetItem(infoPtr, iItm);
1343 
1344           /* shift the item to the left side of the clientRect */
1345           curr->rect.right -= curr->rect.left;
1346           curr->rect.left = 0;
1347 
1348           TRACE("r=%d, cl=%d, cl.r=%d, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1349 	      curr->rect.right, curItemLeftPos, clientRect.right,
1350 	      iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1351 
1352           /* if we have reached the maximum number of tabs on this row */
1353           /* move to the next row, reset our current item left position and */
1354           /* the count of items on this row */
1355 
1356 	  if (infoPtr->dwStyle & TCS_VERTICAL) {
1357 	      /* Vert: Add the remaining tabs in the *last* remainder rows */
1358 	      if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1359 		  iRow++;
1360 		  curItemLeftPos = 0;
1361 		  iCount = 0;
1362 	      }
1363 	  } else {
1364 	      /* Horz: Add the remaining tabs in the *first* remainder rows */
1365 	      if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1366 		  iRow++;
1367 		  curItemLeftPos = 0;
1368 		  iCount = 0;
1369 	      }
1370 	  }
1371 
1372           /* shift the item to the right to place it as the next item in this row */
1373           curr->rect.left += curItemLeftPos;
1374           curr->rect.right += curItemLeftPos;
1375           curr->rect.top = iRow;
1376           if (infoPtr->dwStyle & TCS_BUTTONS)
1377 	  {
1378             curItemLeftPos = curr->rect.right + 1;
1379             if (infoPtr->dwStyle & TCS_FLATBUTTONS)
1380 	      curItemLeftPos += FLAT_BTN_SPACINGX;
1381 	  }
1382           else
1383             curItemLeftPos = curr->rect.right;
1384 
1385           TRACE("arranging <%s>, rect %s\n", debugstr_w(curr->pszText), wine_dbgstr_rect(&curr->rect));
1386       }
1387 
1388       /*
1389        * Justify the rows
1390        */
1391       {
1392 	INT widthDiff, iIndexStart=0, iIndexEnd=0;
1393 	INT remainder;
1394 	INT iCount=0;
1395 
1396         while(iIndexStart < infoPtr->uNumItem)
1397         {
1398           TAB_ITEM *start = TAB_GetItem(infoPtr, iIndexStart);
1399 
1400           /*
1401            * find the index of the row
1402            */
1403           /* find the first item on the next row */
1404           for (iIndexEnd=iIndexStart;
1405               (iIndexEnd < infoPtr->uNumItem) &&
1406  	      (TAB_GetItem(infoPtr, iIndexEnd)->rect.top ==
1407                 start->rect.top) ;
1408               iIndexEnd++)
1409           /* intentionally blank */;
1410 
1411           /*
1412            * we need to justify these tabs so they fill the whole given
1413            * client area
1414            *
1415            */
1416           /* find the amount of space remaining on this row */
1417           widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1418 			TAB_GetItem(infoPtr, iIndexEnd - 1)->rect.right;
1419 
1420 	  /* iCount is the number of tab items on this row */
1421 	  iCount = iIndexEnd - iIndexStart;
1422 
1423 	  if (iCount > 1)
1424 	  {
1425 	    remainder = widthDiff % iCount;
1426 	    widthDiff = widthDiff / iCount;
1427 	    /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1428 	    for (iIndex=iIndexStart, iCount=0; iIndex < iIndexEnd; iIndex++, iCount++)
1429 	    {
1430               TAB_ITEM *item = TAB_GetItem(infoPtr, iIndex);
1431 
1432 	      item->rect.left += iCount * widthDiff;
1433 	      item->rect.right += (iCount + 1) * widthDiff;
1434 
1435               TRACE("adjusting 1 <%s>, rect %s\n", debugstr_w(item->pszText), wine_dbgstr_rect(&item->rect));
1436 
1437 	    }
1438 	    TAB_GetItem(infoPtr, iIndex - 1)->rect.right += remainder;
1439 	  }
1440 	  else /* we have only one item on this row, make it take up the entire row */
1441 	  {
1442 	    start->rect.left = clientRect.left;
1443 	    start->rect.right = clientRect.right - 4;
1444 
1445             TRACE("adjusting 2 <%s>, rect %s\n", debugstr_w(start->pszText), wine_dbgstr_rect(&start->rect));
1446 	  }
1447 
1448 	  iIndexStart = iIndexEnd;
1449 	}
1450       }
1451   }
1452 
1453   /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1454   if(infoPtr->dwStyle & TCS_VERTICAL)
1455   {
1456     RECT rcOriginal;
1457     for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1458     {
1459       rcItem = &TAB_GetItem(infoPtr, iIndex)->rect;
1460 
1461       rcOriginal = *rcItem;
1462 
1463       /* this is rotating the items by 90 degrees clockwise around the center of the control */
1464       rcItem->top = (rcOriginal.left - clientRect.left);
1465       rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1466       rcItem->left = rcOriginal.top;
1467       rcItem->right = rcOriginal.bottom;
1468     }
1469   }
1470 
1471   TAB_EnsureSelectionVisible(infoPtr);
1472   TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
1473 
1474   /* Cleanup */
1475   SelectObject (hdc, hOldFont);
1476   ReleaseDC (infoPtr->hwnd, hdc);
1477 }
1478 
1479 
1480 static void
1481 TAB_EraseTabInterior(const TAB_INFO *infoPtr, HDC hdc, INT iItem, const RECT *drawRect)
1482 {
1483     HBRUSH   hbr = CreateSolidBrush (comctl32_color.clrBtnFace);
1484     BOOL     deleteBrush = TRUE;
1485     RECT     rTemp = *drawRect;
1486 
1487     if (infoPtr->dwStyle & TCS_BUTTONS)
1488     {
1489 	if (iItem == infoPtr->iSelected)
1490 	{
1491 	    /* Background color */
1492 	    if (!(infoPtr->dwStyle & TCS_OWNERDRAWFIXED))
1493 	    {
1494 		DeleteObject(hbr);
1495 		hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1496 
1497 		SetTextColor(hdc, comctl32_color.clr3dFace);
1498 		SetBkColor(hdc, comctl32_color.clr3dHilight);
1499 
1500 		/* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1501 		* we better use 0x55aa bitmap brush to make scrollbar's background
1502 		* look different from the window background.
1503 		*/
1504 		if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1505 		    hbr = COMCTL32_hPattern55AABrush;
1506 
1507 		deleteBrush = FALSE;
1508 	    }
1509 	    FillRect(hdc, &rTemp, hbr);
1510 	}
1511 	else  /* ! selected */
1512 	{
1513 	    if (infoPtr->dwStyle & TCS_FLATBUTTONS)
1514 	    {
1515 		InflateRect(&rTemp, 2, 2);
1516 		FillRect(hdc, &rTemp, hbr);
1517 		if (iItem == infoPtr->iHotTracked ||
1518                    (iItem != infoPtr->iSelected && iItem == infoPtr->uFocus))
1519 		    DrawEdge(hdc, &rTemp, BDR_RAISEDINNER, BF_RECT);
1520 	    }
1521 	    else
1522 		FillRect(hdc, &rTemp, hbr);
1523 	}
1524 
1525     }
1526     else /* !TCS_BUTTONS */
1527     {
1528         InflateRect(&rTemp, -2, -2);
1529         if (!GetWindowTheme (infoPtr->hwnd))
1530 	    FillRect(hdc, &rTemp, hbr);
1531     }
1532 
1533     /* highlighting is drawn on top of previous fills */
1534     if (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED)
1535     {
1536         if (deleteBrush)
1537         {
1538             DeleteObject(hbr);
1539             deleteBrush = FALSE;
1540         }
1541         hbr = GetSysColorBrush(COLOR_HIGHLIGHT);
1542         FillRect(hdc, &rTemp, hbr);
1543     }
1544 
1545     /* Cleanup */
1546     if (deleteBrush) DeleteObject(hbr);
1547 }
1548 
1549 /******************************************************************************
1550  * TAB_DrawItemInterior
1551  *
1552  * This method is used to draw the interior (text and icon) of a single tab
1553  * into the tab control.
1554  */
1555 static void
1556 TAB_DrawItemInterior(const TAB_INFO *infoPtr, HDC hdc, INT iItem, RECT *drawRect)
1557 {
1558   RECT localRect;
1559 
1560   HPEN   htextPen;
1561   HPEN   holdPen;
1562   INT    oldBkMode;
1563   HFONT  hOldFont;
1564 #ifdef __REACTOS__
1565 HTHEME    theme = GetWindowTheme (infoPtr->hwnd);
1566 #endif
1567 
1568 /*  if (drawRect == NULL) */
1569   {
1570     BOOL isVisible;
1571     RECT itemRect;
1572     RECT selectedRect;
1573 
1574     /*
1575      * Get the rectangle for the item.
1576      */
1577     isVisible = TAB_InternalGetItemRect(infoPtr, iItem, &itemRect, &selectedRect);
1578     if (!isVisible)
1579       return;
1580 
1581     /*
1582      * Make sure drawRect points to something valid; simplifies code.
1583      */
1584     drawRect = &localRect;
1585 
1586     /*
1587      * This logic copied from the part of TAB_DrawItem which draws
1588      * the tab background.  It's important to keep it in sync.  I
1589      * would have liked to avoid code duplication, but couldn't figure
1590      * out how without making spaghetti of TAB_DrawItem.
1591      */
1592     if (iItem == infoPtr->iSelected)
1593       *drawRect = selectedRect;
1594     else
1595       *drawRect = itemRect;
1596 
1597     if (infoPtr->dwStyle & TCS_BUTTONS)
1598     {
1599       if (iItem == infoPtr->iSelected)
1600       {
1601 	drawRect->left   += 4;
1602 	drawRect->top    += 4;
1603 	drawRect->right  -= 4;
1604 
1605 	if (infoPtr->dwStyle & TCS_VERTICAL)
1606 	{
1607 	  if (!(infoPtr->dwStyle & TCS_BOTTOM)) drawRect->right  += 1;
1608 	  drawRect->bottom   -= 4;
1609 	}
1610 	else
1611 	{
1612 	  if (infoPtr->dwStyle & TCS_BOTTOM)
1613 	  {
1614 	    drawRect->top    -= 2;
1615 	    drawRect->bottom -= 4;
1616 	  }
1617 	  else
1618 	    drawRect->bottom -= 1;
1619 	}
1620       }
1621       else
1622         InflateRect(drawRect, -2, -2);
1623     }
1624     else
1625     {
1626       if ((infoPtr->dwStyle & TCS_VERTICAL) && (infoPtr->dwStyle & TCS_BOTTOM))
1627       {
1628         if (iItem != infoPtr->iSelected)
1629 	{
1630 	  drawRect->left   += 2;
1631           InflateRect(drawRect, 0, -2);
1632 	}
1633       }
1634       else if (infoPtr->dwStyle & TCS_VERTICAL)
1635       {
1636         if (iItem == infoPtr->iSelected)
1637 	{
1638 	  drawRect->right  += 1;
1639 	}
1640 	else
1641 	{
1642 	  drawRect->right  -= 2;
1643           InflateRect(drawRect, 0, -2);
1644 	}
1645       }
1646       else if (infoPtr->dwStyle & TCS_BOTTOM)
1647       {
1648         if (iItem == infoPtr->iSelected)
1649 	{
1650 	  drawRect->top    -= 2;
1651 	}
1652 	else
1653 	{
1654 	  InflateRect(drawRect, -2, -2);
1655           drawRect->bottom += 2;
1656 	}
1657       }
1658       else
1659       {
1660         if (iItem == infoPtr->iSelected)
1661 	{
1662 	  drawRect->bottom += 3;
1663 	}
1664 	else
1665 	{
1666 	  drawRect->bottom -= 2;
1667 	  InflateRect(drawRect, -2, 0);
1668 	}
1669       }
1670     }
1671   }
1672   TRACE("drawRect=(%s)\n", wine_dbgstr_rect(drawRect));
1673 
1674   /* Clear interior */
1675   TAB_EraseTabInterior (infoPtr, hdc, iItem, drawRect);
1676 
1677   /* Draw the focus rectangle */
1678   if (!(infoPtr->dwStyle & TCS_FOCUSNEVER) &&
1679       (GetFocus() == infoPtr->hwnd) &&
1680       (iItem == infoPtr->uFocus) )
1681   {
1682     RECT rFocus = *drawRect;
1683 
1684     if (!(infoPtr->dwStyle & TCS_BUTTONS)) InflateRect(&rFocus, -3, -3);
1685     if (infoPtr->dwStyle & TCS_BOTTOM && !(infoPtr->dwStyle & TCS_VERTICAL))
1686       rFocus.top -= 3;
1687 
1688     /* focus should stay on selected item for TCS_BUTTONS style */
1689     if (!((infoPtr->dwStyle & TCS_BUTTONS) && (infoPtr->iSelected != iItem)))
1690       DrawFocusRect(hdc, &rFocus);
1691   }
1692 
1693   /*
1694    * Text pen
1695    */
1696   htextPen = CreatePen( PS_SOLID, 1, comctl32_color.clrBtnText );
1697   holdPen  = SelectObject(hdc, htextPen);
1698   hOldFont = SelectObject(hdc, infoPtr->hFont);
1699 
1700   /*
1701    * Setup for text output
1702   */
1703   oldBkMode = SetBkMode(hdc, TRANSPARENT);
1704   if (!GetWindowTheme (infoPtr->hwnd) || (infoPtr->dwStyle & TCS_BUTTONS))
1705   {
1706     if ((infoPtr->dwStyle & TCS_HOTTRACK) && (iItem == infoPtr->iHotTracked) &&
1707         !(infoPtr->dwStyle & TCS_FLATBUTTONS))
1708       SetTextColor(hdc, comctl32_color.clrHighlight);
1709     else if (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED)
1710       SetTextColor(hdc, comctl32_color.clrHighlightText);
1711     else
1712       SetTextColor(hdc, comctl32_color.clrBtnText);
1713   }
1714 
1715   /*
1716    * if owner draw, tell the owner to draw
1717    */
1718   if ((infoPtr->dwStyle & TCS_OWNERDRAWFIXED) && IsWindow(infoPtr->hwndNotify))
1719   {
1720     DRAWITEMSTRUCT dis;
1721     UINT id;
1722 
1723     drawRect->top += 2;
1724     drawRect->right -= 1;
1725     if ( iItem == infoPtr->iSelected )
1726         InflateRect(drawRect, -1, 0);
1727 
1728     id = (UINT)GetWindowLongPtrW( infoPtr->hwnd, GWLP_ID );
1729 
1730     /* fill DRAWITEMSTRUCT */
1731     dis.CtlType    = ODT_TAB;
1732     dis.CtlID      = id;
1733     dis.itemID     = iItem;
1734     dis.itemAction = ODA_DRAWENTIRE;
1735     dis.itemState = 0;
1736     if ( iItem == infoPtr->iSelected )
1737       dis.itemState |= ODS_SELECTED;
1738     if (infoPtr->uFocus == iItem)
1739       dis.itemState |= ODS_FOCUS;
1740     dis.hwndItem = infoPtr->hwnd;
1741     dis.hDC      = hdc;
1742     dis.rcItem = *drawRect;
1743 
1744     /* when extra data fits ULONG_PTR, store it directly */
1745     if (infoPtr->cbInfo > sizeof(LPARAM))
1746         dis.itemData =  (ULONG_PTR) TAB_GetItem(infoPtr, iItem)->extra;
1747     else
1748     {
1749         /* this could be considered broken on 64 bit, but that's how it works -
1750            only first 4 bytes are copied */
1751         dis.itemData = 0;
1752         memcpy(&dis.itemData, (ULONG_PTR*)TAB_GetItem(infoPtr, iItem)->extra, 4);
1753     }
1754 
1755     /* draw notification */
1756     SendMessageW( infoPtr->hwndNotify, WM_DRAWITEM, id, (LPARAM)&dis );
1757   }
1758   else
1759   {
1760     TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
1761     RECT rcTemp;
1762     RECT rcImage;
1763 
1764     /* used to center the icon and text in the tab */
1765     RECT rcText;
1766     INT center_offset_h, center_offset_v;
1767 
1768     /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1769     rcImage = *drawRect;
1770 
1771     rcTemp = *drawRect;
1772     SetRectEmpty(&rcText);
1773 
1774     /* get the rectangle that the text fits in */
1775     if (item->pszText)
1776     {
1777       DrawTextW(hdc, item->pszText, -1, &rcText, DT_CALCRECT);
1778     }
1779     /*
1780      * If not owner draw, then do the drawing ourselves.
1781      *
1782      * Draw the icon.
1783      */
1784     if (infoPtr->himl && item->iImage != -1)
1785     {
1786       INT cx;
1787       INT cy;
1788 
1789       ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1790 
1791       if(infoPtr->dwStyle & TCS_VERTICAL)
1792       {
1793         center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right  - rcText.left))) / 2;
1794         center_offset_v = ((drawRect->right - drawRect->left) - cx) / 2;
1795       }
1796       else
1797       {
1798         center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right  - rcText.left))) / 2;
1799         center_offset_v = ((drawRect->bottom - drawRect->top) - cy) / 2;
1800       }
1801 
1802       /* if an item is selected, the icon is shifted up instead of down */
1803       if (iItem == infoPtr->iSelected)
1804         center_offset_v -= infoPtr->uVItemPadding / 2;
1805       else
1806         center_offset_v += infoPtr->uVItemPadding / 2;
1807 
1808       if (infoPtr->dwStyle & TCS_FIXEDWIDTH && infoPtr->dwStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1809 	center_offset_h = infoPtr->uHItemPadding;
1810 
1811       if (center_offset_h < 2)
1812         center_offset_h = 2;
1813 
1814       if (center_offset_v < 0)
1815         center_offset_v = 0;
1816 
1817       TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%s), textlen=%d\n",
1818 	  debugstr_w(item->pszText), center_offset_h, center_offset_v,
1819           wine_dbgstr_rect(drawRect), (rcText.right-rcText.left));
1820 
1821       if((infoPtr->dwStyle & TCS_VERTICAL) && (infoPtr->dwStyle & TCS_BOTTOM))
1822       {
1823         rcImage.top = drawRect->top + center_offset_h;
1824 	/* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1825 	/* right side of the tab, but the image still uses the left as its x position */
1826 	/* this keeps the image always drawn off of the same side of the tab */
1827         rcImage.left = drawRect->right - cx - center_offset_v;
1828         drawRect->top += cy + infoPtr->uHItemPadding;
1829       }
1830       else if(infoPtr->dwStyle & TCS_VERTICAL)
1831       {
1832         rcImage.top  = drawRect->bottom - cy - center_offset_h;
1833 	rcImage.left = drawRect->left + center_offset_v;
1834         drawRect->bottom -= cy + infoPtr->uHItemPadding;
1835       }
1836       else /* normal style, whether TCS_BOTTOM or not */
1837       {
1838         rcImage.left = drawRect->left + center_offset_h;
1839 	rcImage.top = drawRect->top + center_offset_v;
1840         drawRect->left += cx + infoPtr->uHItemPadding;
1841       }
1842 
1843       TRACE("drawing image=%d, left=%d, top=%d\n",
1844 	    item->iImage, rcImage.left, rcImage.top-1);
1845       ImageList_Draw
1846         (
1847         infoPtr->himl,
1848         item->iImage,
1849         hdc,
1850         rcImage.left,
1851         rcImage.top,
1852         ILD_NORMAL
1853         );
1854     }
1855 
1856     /* Now position text */
1857     if (infoPtr->dwStyle & TCS_FIXEDWIDTH && infoPtr->dwStyle & TCS_FORCELABELLEFT)
1858       center_offset_h = infoPtr->uHItemPadding;
1859     else
1860       if(infoPtr->dwStyle & TCS_VERTICAL)
1861         center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1862       else
1863         center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1864 
1865     if(infoPtr->dwStyle & TCS_VERTICAL)
1866     {
1867       if(infoPtr->dwStyle & TCS_BOTTOM)
1868         drawRect->top+=center_offset_h;
1869       else
1870         drawRect->bottom-=center_offset_h;
1871 
1872       center_offset_v = ((drawRect->right - drawRect->left) - (rcText.bottom - rcText.top)) / 2;
1873     }
1874     else
1875     {
1876       drawRect->left += center_offset_h;
1877       center_offset_v = ((drawRect->bottom - drawRect->top) - (rcText.bottom - rcText.top)) / 2;
1878     }
1879 
1880     /* if an item is selected, the text is shifted up instead of down */
1881     if (iItem == infoPtr->iSelected)
1882         center_offset_v -= infoPtr->uVItemPadding / 2;
1883     else
1884         center_offset_v += infoPtr->uVItemPadding / 2;
1885 
1886     if (center_offset_v < 0)
1887       center_offset_v = 0;
1888 
1889     if(infoPtr->dwStyle & TCS_VERTICAL)
1890       drawRect->left += center_offset_v;
1891     else
1892       drawRect->top += center_offset_v;
1893 
1894     /* Draw the text */
1895     if(infoPtr->dwStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1896     {
1897       LOGFONTW logfont;
1898       HFONT hFont;
1899       INT nEscapement = 900;
1900       INT nOrientation = 900;
1901 
1902       if(infoPtr->dwStyle & TCS_BOTTOM)
1903       {
1904         nEscapement = -900;
1905         nOrientation = -900;
1906       }
1907 
1908       /* to get a font with the escapement and orientation we are looking for, we need to */
1909       /* call CreateFontIndirect, which requires us to set the values of the logfont we pass in */
1910       if (!GetObjectW(infoPtr->hFont, sizeof(logfont), &logfont))
1911         GetObjectW(GetStockObject(DEFAULT_GUI_FONT), sizeof(logfont), &logfont);
1912 
1913       logfont.lfEscapement = nEscapement;
1914       logfont.lfOrientation = nOrientation;
1915       hFont = CreateFontIndirectW(&logfont);
1916       SelectObject(hdc, hFont);
1917 
1918       if (item->pszText)
1919       {
1920         ExtTextOutW(hdc,
1921         (infoPtr->dwStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1922         (!(infoPtr->dwStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1923         ETO_CLIPPED,
1924         drawRect,
1925         item->pszText,
1926         lstrlenW(item->pszText),
1927         0);
1928       }
1929 
1930       DeleteObject(hFont);
1931     }
1932     else
1933     {
1934       TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%s), textlen=%d\n",
1935 	  debugstr_w(item->pszText), center_offset_h, center_offset_v,
1936           wine_dbgstr_rect(drawRect), (rcText.right-rcText.left));
1937 #ifdef __REACTOS__
1938       if (theme && item->pszText)
1939       {
1940           int partIndex = iItem == infoPtr->iSelected ? TABP_TABITEM : TABP_TOPTABITEM;
1941           int stateId = TIS_NORMAL;
1942 
1943           if (iItem == infoPtr->iSelected)
1944               stateId = TIS_SELECTED;
1945           else if (iItem == infoPtr->iHotTracked)
1946               stateId = TIS_HOT;
1947           else if (iItem == infoPtr->uFocus)
1948               stateId = TIS_FOCUSED;
1949 
1950           DrawThemeText(theme,
1951                         hdc,
1952                         partIndex,
1953                         stateId,
1954                         item->pszText,
1955                         lstrlenW(item->pszText),
1956                         DT_LEFT | DT_SINGLELINE, 0, drawRect);
1957       }
1958       else
1959 #endif
1960       if (item->pszText)
1961       {
1962         DrawTextW
1963         (
1964           hdc,
1965           item->pszText,
1966           lstrlenW(item->pszText),
1967           drawRect,
1968           DT_LEFT | DT_SINGLELINE
1969         );
1970       }
1971     }
1972 
1973     *drawRect = rcTemp; /* restore drawRect */
1974   }
1975 
1976   /*
1977   * Cleanup
1978   */
1979   SelectObject(hdc, hOldFont);
1980   SetBkMode(hdc, oldBkMode);
1981   SelectObject(hdc, holdPen);
1982   DeleteObject( htextPen );
1983 }
1984 
1985 /******************************************************************************
1986  * TAB_DrawItem
1987  *
1988  * This method is used to draw a single tab into the tab control.
1989  */
1990 static void TAB_DrawItem(const TAB_INFO *infoPtr, HDC  hdc, INT  iItem)
1991 {
1992   RECT      itemRect;
1993   RECT      selectedRect;
1994   BOOL      isVisible;
1995   RECT      r, fillRect, r1;
1996   INT       clRight = 0;
1997   INT       clBottom = 0;
1998   COLORREF  bkgnd, corner;
1999   HTHEME    theme;
2000 
2001   /*
2002    * Get the rectangle for the item.
2003    */
2004   isVisible = TAB_InternalGetItemRect(infoPtr,
2005 				      iItem,
2006 				      &itemRect,
2007 				      &selectedRect);
2008 
2009   if (isVisible)
2010   {
2011     RECT rUD, rC;
2012 
2013     /* Clip UpDown control to not draw over it */
2014     if (infoPtr->needsScrolling)
2015     {
2016       GetWindowRect(infoPtr->hwnd, &rC);
2017       GetWindowRect(infoPtr->hwndUpDown, &rUD);
2018       ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
2019     }
2020 
2021     /* If you need to see what the control is doing,
2022      * then override these variables. They will change what
2023      * fill colors are used for filling the tabs, and the
2024      * corners when drawing the edge.
2025      */
2026     bkgnd = comctl32_color.clrBtnFace;
2027     corner = comctl32_color.clrBtnFace;
2028 
2029     if (infoPtr->dwStyle & TCS_BUTTONS)
2030     {
2031       /* Get item rectangle */
2032       r = itemRect;
2033 
2034       /* Separators between flat buttons */
2035       if ((infoPtr->dwStyle & TCS_FLATBUTTONS) && (infoPtr->exStyle & TCS_EX_FLATSEPARATORS))
2036       {
2037 	r1 = r;
2038 	r1.right += (FLAT_BTN_SPACINGX -2);
2039 	DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
2040       }
2041 
2042       if (iItem == infoPtr->iSelected)
2043       {
2044 	DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
2045 
2046 	OffsetRect(&r, 1, 1);
2047       }
2048       else  /* ! selected */
2049       {
2050         DWORD state = TAB_GetItem(infoPtr, iItem)->dwState;
2051 
2052         if ((state & TCIS_BUTTONPRESSED) || (iItem == infoPtr->uFocus))
2053           DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
2054         else
2055           if (!(infoPtr->dwStyle & TCS_FLATBUTTONS))
2056             DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
2057       }
2058     }
2059     else /* !TCS_BUTTONS */
2060     {
2061       /* We draw a rectangle of different sizes depending on the selection
2062        * state. */
2063       if (iItem == infoPtr->iSelected) {
2064 	RECT rect;
2065 	GetClientRect (infoPtr->hwnd, &rect);
2066 	clRight = rect.right;
2067 	clBottom = rect.bottom;
2068         r = selectedRect;
2069       }
2070       else
2071         r = itemRect;
2072 
2073       /*
2074        * Erase the background. (Delay it but setup rectangle.)
2075        * This is necessary when drawing the selected item since it is larger
2076        * than the others, it might overlap with stuff already drawn by the
2077        * other tabs
2078        */
2079       fillRect = r;
2080 
2081       /* Draw themed tabs - but only if they are at the top.
2082        * Windows draws even side or bottom tabs themed, with wacky results.
2083        * However, since in Wine apps may get themed that did not opt in via
2084        * a manifest avoid theming when we know the result will be wrong */
2085       if ((theme = GetWindowTheme (infoPtr->hwnd))
2086           && ((infoPtr->dwStyle & (TCS_VERTICAL | TCS_BOTTOM)) == 0))
2087       {
2088           static const int partIds[8] = {
2089               /* Normal item */
2090               TABP_TABITEM,
2091               TABP_TABITEMLEFTEDGE,
2092               TABP_TABITEMRIGHTEDGE,
2093               TABP_TABITEMBOTHEDGE,
2094               /* Selected tab */
2095               TABP_TOPTABITEM,
2096               TABP_TOPTABITEMLEFTEDGE,
2097               TABP_TOPTABITEMRIGHTEDGE,
2098               TABP_TOPTABITEMBOTHEDGE,
2099           };
2100           int partIndex = 0;
2101           int stateId = TIS_NORMAL;
2102 
2103           /* selected and unselected tabs have different parts */
2104           if (iItem == infoPtr->iSelected)
2105               partIndex += 4;
2106           /* The part also differs on the position of a tab on a line.
2107            * "Visually" determining the position works well enough. */
2108           GetClientRect(infoPtr->hwnd, &r1);
2109           if(selectedRect.left == 0)
2110               partIndex += 1;
2111           if(selectedRect.right == r1.right)
2112               partIndex += 2;
2113 
2114           if (iItem == infoPtr->iSelected)
2115               stateId = TIS_SELECTED;
2116           else if (iItem == infoPtr->iHotTracked)
2117               stateId = TIS_HOT;
2118           else if (iItem == infoPtr->uFocus)
2119               stateId = TIS_FOCUSED;
2120 
2121           /* Adjust rectangle for bottommost row */
2122           if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2123             r.bottom += 3;
2124 
2125           DrawThemeBackground (theme, hdc, partIds[partIndex], stateId, &r, NULL);
2126           GetThemeBackgroundContentRect (theme, hdc, partIds[partIndex], stateId, &r, &r);
2127       }
2128       else if(infoPtr->dwStyle & TCS_VERTICAL)
2129       {
2130 	/* These are for adjusting the drawing of a Selected tab      */
2131 	/* The initial values are for the normal case of non-Selected */
2132 	int ZZ = 1;   /* Do not stretch if selected */
2133 	if (iItem == infoPtr->iSelected) {
2134 	    ZZ = 0;
2135 
2136 	    /* if leftmost draw the line longer */
2137 	    if(selectedRect.top == 0)
2138 		fillRect.top += CONTROL_BORDER_SIZEY;
2139 	    /* if rightmost draw the line longer */
2140 	    if(selectedRect.bottom == clBottom)
2141 		fillRect.bottom -= CONTROL_BORDER_SIZEY;
2142 	}
2143 
2144         if (infoPtr->dwStyle & TCS_BOTTOM)
2145         {
2146 	  /* Adjust both rectangles to match native */
2147 	  r.left += (1-ZZ);
2148 
2149           TRACE("<right> item=%d, fill=(%s), edge=(%s)\n",
2150                 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r));
2151 
2152 	  /* Clear interior */
2153 	  SetBkColor(hdc, bkgnd);
2154 	  ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2155 
2156 	  /* Draw rectangular edge around tab */
2157 	  DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
2158 
2159 	  /* Now erase the top corner and draw diagonal edge */
2160 	  SetBkColor(hdc, corner);
2161 	  r1.left = r.right - ROUND_CORNER_SIZE - 1;
2162 	  r1.top = r.top;
2163 	  r1.right = r.right;
2164 	  r1.bottom = r1.top + ROUND_CORNER_SIZE;
2165 	  ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2166 	  r1.right--;
2167 	  DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2168 
2169 	  /* Now erase the bottom corner and draw diagonal edge */
2170 	  r1.left = r.right - ROUND_CORNER_SIZE - 1;
2171 	  r1.bottom = r.bottom;
2172 	  r1.right = r.right;
2173 	  r1.top = r1.bottom - ROUND_CORNER_SIZE;
2174 	  ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2175 	  r1.right--;
2176 	  DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2177 
2178 	  if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
2179 	      r1 = r;
2180 	      r1.right = r1.left;
2181 	      r1.left--;
2182 	      DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
2183 	  }
2184 
2185         }
2186         else
2187         {
2188           TRACE("<left> item=%d, fill=(%s), edge=(%s)\n",
2189                 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r));
2190 
2191 	  /* Clear interior */
2192 	  SetBkColor(hdc, bkgnd);
2193 	  ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2194 
2195 	  /* Draw rectangular edge around tab */
2196 	  DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2197 
2198 	  /* Now erase the top corner and draw diagonal edge */
2199 	  SetBkColor(hdc, corner);
2200 	  r1.left = r.left;
2201 	  r1.top = r.top;
2202 	  r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2203 	  r1.bottom = r1.top + ROUND_CORNER_SIZE;
2204 	  ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2205 	  r1.left++;
2206 	  DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2207 
2208 	  /* Now erase the bottom corner and draw diagonal edge */
2209 	  r1.left = r.left;
2210 	  r1.bottom = r.bottom;
2211 	  r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2212 	  r1.top = r1.bottom - ROUND_CORNER_SIZE;
2213 	  ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2214 	  r1.left++;
2215 	  DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2216         }
2217       }
2218       else  /* ! TCS_VERTICAL */
2219       {
2220 	/* These are for adjusting the drawing of a Selected tab      */
2221 	/* The initial values are for the normal case of non-Selected */
2222 	if (iItem == infoPtr->iSelected) {
2223 	    /* if leftmost draw the line longer */
2224 	    if(selectedRect.left == 0)
2225 		fillRect.left += CONTROL_BORDER_SIZEX;
2226 	    /* if rightmost draw the line longer */
2227 	    if(selectedRect.right == clRight)
2228 		fillRect.right -= CONTROL_BORDER_SIZEX;
2229 	}
2230 
2231         if (infoPtr->dwStyle & TCS_BOTTOM)
2232         {
2233 	  /* Adjust both rectangles for topmost row */
2234 	  if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2235 	  {
2236 	    fillRect.top -= 2;
2237 	    r.top -= 1;
2238 	  }
2239 
2240           TRACE("<bottom> item=%d, fill=(%s), edge=(%s)\n",
2241                 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r));
2242 
2243 	  /* Clear interior */
2244 	  SetBkColor(hdc, bkgnd);
2245 	  ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2246 
2247 	  /* Draw rectangular edge around tab */
2248 	  DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2249 
2250 	  /* Now erase the righthand corner and draw diagonal edge */
2251 	  SetBkColor(hdc, corner);
2252 	  r1.left = r.right - ROUND_CORNER_SIZE;
2253 	  r1.bottom = r.bottom;
2254 	  r1.right = r.right;
2255 	  r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2256 	  ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2257 	  r1.bottom--;
2258 	  DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2259 
2260 	  /* Now erase the lefthand corner and draw diagonal edge */
2261 	  r1.left = r.left;
2262 	  r1.bottom = r.bottom;
2263 	  r1.right = r1.left + ROUND_CORNER_SIZE;
2264 	  r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2265 	  ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2266 	  r1.bottom--;
2267 	  DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2268 
2269 	  if (iItem == infoPtr->iSelected)
2270 	  {
2271 	    r.top += 2;
2272 	    r.left += 1;
2273 	    if (selectedRect.left == 0)
2274 	    {
2275 	      r1 = r;
2276 	      r1.bottom = r1.top;
2277 	      r1.top--;
2278 	      DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2279 	    }
2280 	  }
2281 
2282         }
2283         else
2284         {
2285 	  /* Adjust both rectangles for bottommost row */
2286 	  if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2287 	  {
2288 	    fillRect.bottom += 3;
2289 	    r.bottom += 2;
2290 	  }
2291 
2292           TRACE("<top> item=%d, fill=(%s), edge=(%s)\n",
2293                 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r));
2294 
2295 	  /* Clear interior */
2296 	  SetBkColor(hdc, bkgnd);
2297 	  ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2298 
2299 	  /* Draw rectangular edge around tab */
2300 	  DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2301 
2302 	  /* Now erase the righthand corner and draw diagonal edge */
2303 	  SetBkColor(hdc, corner);
2304 	  r1.left = r.right - ROUND_CORNER_SIZE;
2305 	  r1.top = r.top;
2306 	  r1.right = r.right;
2307 	  r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2308 	  ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2309 	  r1.top++;
2310 	  DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2311 
2312 	  /* Now erase the lefthand corner and draw diagonal edge */
2313 	  r1.left = r.left;
2314 	  r1.top = r.top;
2315 	  r1.right = r1.left + ROUND_CORNER_SIZE;
2316 	  r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2317 	  ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2318 	  r1.top++;
2319 	  DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2320         }
2321       }
2322     }
2323 
2324     TAB_DumpItemInternal(infoPtr, iItem);
2325 
2326     /* This modifies r to be the text rectangle. */
2327     TAB_DrawItemInterior(infoPtr, hdc, iItem, &r);
2328   }
2329 }
2330 
2331 /******************************************************************************
2332  * TAB_DrawBorder
2333  *
2334  * This method is used to draw the raised border around the tab control
2335  * "content" area.
2336  */
2337 static void TAB_DrawBorder(const TAB_INFO *infoPtr, HDC hdc)
2338 {
2339   RECT rect;
2340   HTHEME theme = GetWindowTheme (infoPtr->hwnd);
2341 
2342   GetClientRect (infoPtr->hwnd, &rect);
2343 
2344   /*
2345    * Adjust for the style
2346    */
2347 
2348   if (infoPtr->uNumItem)
2349   {
2350     if ((infoPtr->dwStyle & TCS_BOTTOM) && !(infoPtr->dwStyle & TCS_VERTICAL))
2351       rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2352     else if((infoPtr->dwStyle & TCS_BOTTOM) && (infoPtr->dwStyle & TCS_VERTICAL))
2353       rect.right  -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2354     else if(infoPtr->dwStyle & TCS_VERTICAL)
2355       rect.left   += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2356     else /* not TCS_VERTICAL and not TCS_BOTTOM */
2357       rect.top    += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2358   }
2359 
2360   TRACE("border=(%s)\n", wine_dbgstr_rect(&rect));
2361 
2362   if (theme)
2363       DrawThemeBackground (theme, hdc, TABP_PANE, 0, &rect, NULL);
2364   else
2365       DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2366 }
2367 
2368 /******************************************************************************
2369  * TAB_Refresh
2370  *
2371  * This method repaints the tab control..
2372  */
2373 static void TAB_Refresh (const TAB_INFO *infoPtr, HDC hdc)
2374 {
2375   HFONT hOldFont;
2376   INT i;
2377 
2378   if (!infoPtr->DoRedraw)
2379     return;
2380 
2381   hOldFont = SelectObject (hdc, infoPtr->hFont);
2382 
2383   if (infoPtr->dwStyle & TCS_BUTTONS)
2384   {
2385     for (i = 0; i < infoPtr->uNumItem; i++)
2386       TAB_DrawItem (infoPtr, hdc, i);
2387   }
2388   else
2389   {
2390     /* Draw all the non selected item first */
2391     for (i = 0; i < infoPtr->uNumItem; i++)
2392     {
2393       if (i != infoPtr->iSelected)
2394 	TAB_DrawItem (infoPtr, hdc, i);
2395     }
2396 
2397     /* Now, draw the border, draw it before the selected item
2398      * since the selected item overwrites part of the border. */
2399     TAB_DrawBorder (infoPtr, hdc);
2400 
2401     /* Then, draw the selected item */
2402     TAB_DrawItem (infoPtr, hdc, infoPtr->iSelected);
2403   }
2404 
2405   SelectObject (hdc, hOldFont);
2406 }
2407 
2408 static inline DWORD TAB_GetRowCount (const TAB_INFO *infoPtr)
2409 {
2410   TRACE("(%p)\n", infoPtr);
2411   return infoPtr->uNumRows;
2412 }
2413 
2414 static inline LRESULT TAB_SetRedraw (TAB_INFO *infoPtr, BOOL doRedraw)
2415 {
2416   infoPtr->DoRedraw = doRedraw;
2417   return 0;
2418 }
2419 
2420 /******************************************************************************
2421  * TAB_EnsureSelectionVisible
2422  *
2423  * This method will make sure that the current selection is completely
2424  * visible by scrolling until it is.
2425  */
2426 static void TAB_EnsureSelectionVisible(
2427   TAB_INFO* infoPtr)
2428 {
2429   INT iSelected = infoPtr->iSelected;
2430   INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2431 
2432   if (iSelected < 0)
2433     return;
2434 
2435   /* set the items row to the bottommost row or topmost row depending on
2436    * style */
2437   if ((infoPtr->uNumRows > 1) && !(infoPtr->dwStyle & TCS_BUTTONS))
2438   {
2439       TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2440       INT newselected;
2441       INT iTargetRow;
2442 
2443       if(infoPtr->dwStyle & TCS_VERTICAL)
2444         newselected = selected->rect.left;
2445       else
2446         newselected = selected->rect.top;
2447 
2448       /* the target row is always (number of rows - 1)
2449          as row 0 is furthest from the clientRect */
2450       iTargetRow = infoPtr->uNumRows - 1;
2451 
2452       if (newselected != iTargetRow)
2453       {
2454          UINT i;
2455          if(infoPtr->dwStyle & TCS_VERTICAL)
2456          {
2457            for (i=0; i < infoPtr->uNumItem; i++)
2458            {
2459              /* move everything in the row of the selected item to the iTargetRow */
2460              TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2461 
2462              if (item->rect.left == newselected )
2463                  item->rect.left = iTargetRow;
2464              else
2465              {
2466                if (item->rect.left > newselected)
2467                  item->rect.left-=1;
2468              }
2469            }
2470          }
2471          else
2472          {
2473            for (i=0; i < infoPtr->uNumItem; i++)
2474            {
2475              TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2476 
2477              if (item->rect.top == newselected )
2478                  item->rect.top = iTargetRow;
2479              else
2480              {
2481                if (item->rect.top > newselected)
2482                  item->rect.top-=1;
2483              }
2484           }
2485         }
2486         TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2487       }
2488   }
2489 
2490   /*
2491    * Do the trivial cases first.
2492    */
2493   if ( (!infoPtr->needsScrolling) ||
2494        (infoPtr->hwndUpDown==0) || (infoPtr->dwStyle & TCS_VERTICAL))
2495     return;
2496 
2497   if (infoPtr->leftmostVisible >= iSelected)
2498   {
2499     infoPtr->leftmostVisible = iSelected;
2500   }
2501   else
2502   {
2503      TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2504      RECT r;
2505      INT width;
2506      UINT i;
2507 
2508      /* Calculate the part of the client area that is visible */
2509      GetClientRect(infoPtr->hwnd, &r);
2510      width = r.right;
2511 
2512      GetClientRect(infoPtr->hwndUpDown, &r);
2513      width -= r.right;
2514 
2515      if ((selected->rect.right -
2516           selected->rect.left) >= width )
2517      {
2518         /* Special case: width of selected item is greater than visible
2519          * part of control.
2520          */
2521         infoPtr->leftmostVisible = iSelected;
2522      }
2523      else
2524      {
2525         for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2526         {
2527            if ((selected->rect.right - TAB_GetItem(infoPtr, i)->rect.left) < width)
2528               break;
2529         }
2530         infoPtr->leftmostVisible = i;
2531      }
2532   }
2533 
2534   if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2535     TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2536 
2537   SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2538                MAKELONG(infoPtr->leftmostVisible, 0));
2539 }
2540 
2541 /******************************************************************************
2542  * TAB_InvalidateTabArea
2543  *
2544  * This method will invalidate the portion of the control that contains the
2545  * tabs. It is called when the state of the control changes and needs
2546  * to be redisplayed
2547  */
2548 static void TAB_InvalidateTabArea(const TAB_INFO *infoPtr)
2549 {
2550   RECT clientRect, rInvalidate, rAdjClient;
2551   INT lastRow = infoPtr->uNumRows - 1;
2552   RECT rect;
2553 
2554   if (lastRow < 0) return;
2555 
2556   GetClientRect(infoPtr->hwnd, &clientRect);
2557   rInvalidate = clientRect;
2558   rAdjClient = clientRect;
2559 
2560   TAB_AdjustRect(infoPtr, 0, &rAdjClient);
2561 
2562   TAB_InternalGetItemRect(infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2563   if ((infoPtr->dwStyle & TCS_BOTTOM) && (infoPtr->dwStyle & TCS_VERTICAL))
2564   {
2565     rInvalidate.left = rAdjClient.right;
2566     if (infoPtr->uNumRows == 1)
2567       rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2568   }
2569   else if(infoPtr->dwStyle & TCS_VERTICAL)
2570   {
2571     rInvalidate.right = rAdjClient.left;
2572     if (infoPtr->uNumRows == 1)
2573       rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2574   }
2575   else if (infoPtr->dwStyle & TCS_BOTTOM)
2576   {
2577     rInvalidate.top = rAdjClient.bottom;
2578     if (infoPtr->uNumRows == 1)
2579       rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2580   }
2581   else
2582   {
2583     rInvalidate.bottom = rAdjClient.top;
2584     if (infoPtr->uNumRows == 1)
2585       rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2586   }
2587 
2588   /* Punch out the updown control */
2589   if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2590     RECT r;
2591     GetClientRect(infoPtr->hwndUpDown, &r);
2592     if (rInvalidate.right > clientRect.right - r.left)
2593       rInvalidate.right = rInvalidate.right - (r.right - r.left);
2594     else
2595       rInvalidate.right = clientRect.right - r.left;
2596   }
2597 
2598   TRACE("invalidate (%s)\n", wine_dbgstr_rect(&rInvalidate));
2599 
2600   InvalidateRect(infoPtr->hwnd, &rInvalidate, TRUE);
2601 }
2602 
2603 static inline LRESULT TAB_Paint (TAB_INFO *infoPtr, HDC hdcPaint)
2604 {
2605   HDC hdc;
2606   PAINTSTRUCT ps;
2607 
2608   if (hdcPaint)
2609     hdc = hdcPaint;
2610   else
2611   {
2612     hdc = BeginPaint (infoPtr->hwnd, &ps);
2613     TRACE("erase %d, rect=(%s)\n", ps.fErase, wine_dbgstr_rect(&ps.rcPaint));
2614   }
2615 
2616   TAB_Refresh (infoPtr, hdc);
2617 
2618   if (!hdcPaint)
2619     EndPaint (infoPtr->hwnd, &ps);
2620 
2621   return 0;
2622 }
2623 
2624 static LRESULT
2625 TAB_InsertItemT (TAB_INFO *infoPtr, INT iItem, const TCITEMW *pti, BOOL bUnicode)
2626 {
2627   TAB_ITEM *item;
2628   RECT rect;
2629 
2630   GetClientRect (infoPtr->hwnd, &rect);
2631   TRACE("Rect: %p %s\n", infoPtr->hwnd, wine_dbgstr_rect(&rect));
2632 
2633   if (iItem < 0) return -1;
2634   if (iItem > infoPtr->uNumItem)
2635     iItem = infoPtr->uNumItem;
2636 
2637   TAB_DumpItemExternalT(pti, iItem, bUnicode);
2638 
2639   if (!(item = Alloc(TAB_ITEM_SIZE(infoPtr)))) return FALSE;
2640   if (DPA_InsertPtr(infoPtr->items, iItem, item) == -1)
2641   {
2642       Free(item);
2643       return FALSE;
2644   }
2645 
2646   if (infoPtr->uNumItem == 0)
2647       infoPtr->iSelected = 0;
2648   else if (iItem <= infoPtr->iSelected)
2649       infoPtr->iSelected++;
2650 
2651   infoPtr->uNumItem++;
2652 
2653   item->pszText = NULL;
2654   if (pti->mask & TCIF_TEXT)
2655   {
2656     if (bUnicode)
2657       Str_SetPtrW (&item->pszText, pti->pszText);
2658     else
2659       Str_SetPtrAtoW (&item->pszText, (LPSTR)pti->pszText);
2660   }
2661 
2662   if (pti->mask & TCIF_IMAGE)
2663     item->iImage = pti->iImage;
2664   else
2665     item->iImage = -1;
2666 
2667   if (pti->mask & TCIF_PARAM)
2668     memcpy(item->extra, &pti->lParam, EXTRA_ITEM_SIZE(infoPtr));
2669   else
2670     memset(item->extra, 0, EXTRA_ITEM_SIZE(infoPtr));
2671 
2672   TAB_SetItemBounds(infoPtr);
2673   if (infoPtr->uNumItem > 1)
2674     TAB_InvalidateTabArea(infoPtr);
2675   else
2676     InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2677 
2678   TRACE("[%p]: added item %d %s\n",
2679         infoPtr->hwnd, iItem, debugstr_w(item->pszText));
2680 
2681   /* If we haven't set the current focus yet, set it now. */
2682   if (infoPtr->uFocus == -1)
2683     TAB_SetCurFocus(infoPtr, iItem);
2684 
2685   return iItem;
2686 }
2687 
2688 static LRESULT
2689 TAB_SetItemSize (TAB_INFO *infoPtr, INT cx, INT cy)
2690 {
2691   LONG lResult = 0;
2692   BOOL bNeedPaint = FALSE;
2693 
2694   lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2695 
2696   /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2697   if (infoPtr->dwStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != cx))
2698   {
2699     infoPtr->tabWidth = cx;
2700     bNeedPaint = TRUE;
2701   }
2702 
2703   if (infoPtr->tabHeight != cy)
2704   {
2705     if ((infoPtr->fHeightSet = (cy != 0)))
2706       infoPtr->tabHeight = cy;
2707 
2708     bNeedPaint = TRUE;
2709   }
2710   TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2711        HIWORD(lResult), LOWORD(lResult),
2712        infoPtr->tabHeight, infoPtr->tabWidth);
2713 
2714   if (bNeedPaint)
2715   {
2716     TAB_SetItemBounds(infoPtr);
2717     RedrawWindow(infoPtr->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2718   }
2719 
2720   return lResult;
2721 }
2722 
2723 static inline LRESULT TAB_SetMinTabWidth (TAB_INFO *infoPtr, INT cx)
2724 {
2725   INT oldcx = 0;
2726 
2727   TRACE("(%p,%d)\n", infoPtr, cx);
2728 
2729   if (infoPtr->tabMinWidth < 0)
2730     oldcx = DEFAULT_MIN_TAB_WIDTH;
2731   else
2732     oldcx = infoPtr->tabMinWidth;
2733   infoPtr->tabMinWidth = cx;
2734   TAB_SetItemBounds(infoPtr);
2735   return oldcx;
2736 }
2737 
2738 static inline LRESULT
2739 TAB_HighlightItem (TAB_INFO *infoPtr, INT iItem, BOOL fHighlight)
2740 {
2741   LPDWORD lpState;
2742   DWORD oldState;
2743   RECT r;
2744 
2745   TRACE("(%p,%d,%s)\n", infoPtr, iItem, fHighlight ? "true" : "false");
2746 
2747   if (iItem < 0 || iItem >= infoPtr->uNumItem)
2748     return FALSE;
2749 
2750   lpState = &TAB_GetItem(infoPtr, iItem)->dwState;
2751   oldState = *lpState;
2752 
2753   if (fHighlight)
2754     *lpState |= TCIS_HIGHLIGHTED;
2755   else
2756     *lpState &= ~TCIS_HIGHLIGHTED;
2757 
2758   if ((oldState != *lpState) && TAB_InternalGetItemRect (infoPtr, iItem, &r, NULL))
2759     InvalidateRect (infoPtr->hwnd, &r, TRUE);
2760 
2761   return TRUE;
2762 }
2763 
2764 static LRESULT
2765 TAB_SetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2766 {
2767   TAB_ITEM *wineItem;
2768 
2769   TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2770 
2771   if (iItem < 0 || iItem >= infoPtr->uNumItem)
2772     return FALSE;
2773 
2774   TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2775 
2776   wineItem = TAB_GetItem(infoPtr, iItem);
2777 
2778   if (tabItem->mask & TCIF_IMAGE)
2779     wineItem->iImage = tabItem->iImage;
2780 
2781   if (tabItem->mask & TCIF_PARAM)
2782     memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2783 
2784   if (tabItem->mask & TCIF_RTLREADING)
2785     FIXME("TCIF_RTLREADING\n");
2786 
2787   if (tabItem->mask & TCIF_STATE)
2788     wineItem->dwState = (wineItem->dwState & ~tabItem->dwStateMask) |
2789                         ( tabItem->dwState &  tabItem->dwStateMask);
2790 
2791   if (tabItem->mask & TCIF_TEXT)
2792   {
2793     Free(wineItem->pszText);
2794     wineItem->pszText = NULL;
2795     if (bUnicode)
2796       Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2797     else
2798       Str_SetPtrAtoW(&wineItem->pszText, (LPSTR)tabItem->pszText);
2799   }
2800 
2801   /* Update and repaint tabs */
2802   TAB_SetItemBounds(infoPtr);
2803   TAB_InvalidateTabArea(infoPtr);
2804 
2805   return TRUE;
2806 }
2807 
2808 static inline LRESULT TAB_GetItemCount (const TAB_INFO *infoPtr)
2809 {
2810   TRACE("\n");
2811   return infoPtr->uNumItem;
2812 }
2813 
2814 
2815 static LRESULT
2816 TAB_GetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2817 {
2818   TAB_ITEM *wineItem;
2819 
2820   TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2821 
2822   if (!tabItem) return FALSE;
2823 
2824   if (iItem < 0 || iItem >= infoPtr->uNumItem)
2825   {
2826     /* init requested fields */
2827     if (tabItem->mask & TCIF_IMAGE) tabItem->iImage  = 0;
2828     if (tabItem->mask & TCIF_PARAM) tabItem->lParam  = 0;
2829     if (tabItem->mask & TCIF_STATE) tabItem->dwState = 0;
2830     return FALSE;
2831   }
2832 
2833   wineItem = TAB_GetItem(infoPtr, iItem);
2834 
2835   if (tabItem->mask & TCIF_IMAGE)
2836     tabItem->iImage = wineItem->iImage;
2837 
2838   if (tabItem->mask & TCIF_PARAM)
2839     memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2840 
2841   if (tabItem->mask & TCIF_RTLREADING)
2842     FIXME("TCIF_RTLREADING\n");
2843 
2844   if (tabItem->mask & TCIF_STATE)
2845     tabItem->dwState = wineItem->dwState & tabItem->dwStateMask;
2846 
2847   if (tabItem->mask & TCIF_TEXT)
2848   {
2849     if (bUnicode)
2850       Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2851     else
2852       Str_GetPtrWtoA (wineItem->pszText, (LPSTR)tabItem->pszText, tabItem->cchTextMax);
2853   }
2854 
2855   TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2856 
2857   return TRUE;
2858 }
2859 
2860 
2861 static LRESULT TAB_DeleteItem (TAB_INFO *infoPtr, INT iItem)
2862 {
2863     TAB_ITEM *item;
2864 
2865     TRACE("(%p, %d)\n", infoPtr, iItem);
2866 
2867     if (iItem < 0 || iItem >= infoPtr->uNumItem) return FALSE;
2868 
2869     TAB_InvalidateTabArea(infoPtr);
2870     item = TAB_GetItem(infoPtr, iItem);
2871     Free(item->pszText);
2872     Free(item);
2873     infoPtr->uNumItem--;
2874     DPA_DeletePtr(infoPtr->items, iItem);
2875 
2876     if (infoPtr->uNumItem == 0)
2877     {
2878         if (infoPtr->iHotTracked >= 0)
2879         {
2880             KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
2881             infoPtr->iHotTracked = -1;
2882         }
2883 
2884         infoPtr->iSelected = -1;
2885     }
2886     else
2887     {
2888         if (iItem <= infoPtr->iHotTracked)
2889         {
2890             /* When tabs move left/up, the hot track item may change */
2891             FIXME("Recalc hot track\n");
2892         }
2893     }
2894 
2895     /* adjust the selected index */
2896     if (iItem == infoPtr->iSelected)
2897         infoPtr->iSelected = -1;
2898     else if (iItem < infoPtr->iSelected)
2899         infoPtr->iSelected--;
2900 
2901     /* reposition and repaint tabs */
2902     TAB_SetItemBounds(infoPtr);
2903 
2904     return TRUE;
2905 }
2906 
2907 static inline LRESULT TAB_DeleteAllItems (TAB_INFO *infoPtr)
2908 {
2909     TRACE("(%p)\n", infoPtr);
2910     while (infoPtr->uNumItem)
2911       TAB_DeleteItem (infoPtr, 0);
2912     return TRUE;
2913 }
2914 
2915 
2916 static inline LRESULT TAB_GetFont (const TAB_INFO *infoPtr)
2917 {
2918   TRACE("(%p) returning %p\n", infoPtr, infoPtr->hFont);
2919   return (LRESULT)infoPtr->hFont;
2920 }
2921 
2922 static inline LRESULT TAB_SetFont (TAB_INFO *infoPtr, HFONT hNewFont)
2923 {
2924   TRACE("(%p,%p)\n", infoPtr, hNewFont);
2925 
2926   infoPtr->hFont = hNewFont;
2927 
2928   TAB_SetItemBounds(infoPtr);
2929 
2930   TAB_InvalidateTabArea(infoPtr);
2931 
2932   return 0;
2933 }
2934 
2935 
2936 static inline LRESULT TAB_GetImageList (const TAB_INFO *infoPtr)
2937 {
2938   TRACE("\n");
2939   return (LRESULT)infoPtr->himl;
2940 }
2941 
2942 static inline LRESULT TAB_SetImageList (TAB_INFO *infoPtr, HIMAGELIST himlNew)
2943 {
2944     HIMAGELIST himlPrev = infoPtr->himl;
2945     TRACE("himl=%p\n", himlNew);
2946     infoPtr->himl = himlNew;
2947     TAB_SetItemBounds(infoPtr);
2948     InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2949     return (LRESULT)himlPrev;
2950 }
2951 
2952 static inline LRESULT TAB_GetUnicodeFormat (const TAB_INFO *infoPtr)
2953 {
2954     TRACE("(%p)\n", infoPtr);
2955     return infoPtr->bUnicode;
2956 }
2957 
2958 static inline LRESULT TAB_SetUnicodeFormat (TAB_INFO *infoPtr, BOOL bUnicode)
2959 {
2960     BOOL bTemp = infoPtr->bUnicode;
2961 
2962     TRACE("(%p %d)\n", infoPtr, bUnicode);
2963     infoPtr->bUnicode = bUnicode;
2964 
2965     return bTemp;
2966 }
2967 
2968 static inline LRESULT TAB_Size (TAB_INFO *infoPtr)
2969 {
2970 /* I'm not really sure what the following code was meant to do.
2971    This is what it is doing:
2972    When WM_SIZE is sent with SIZE_RESTORED, the control
2973    gets positioned in the top left corner.
2974 
2975   RECT parent_rect;
2976   HWND parent;
2977   UINT uPosFlags,cx,cy;
2978 
2979   uPosFlags=0;
2980   if (!wParam) {
2981     parent = GetParent (hwnd);
2982     GetClientRect(parent, &parent_rect);
2983     cx=LOWORD (lParam);
2984     cy=HIWORD (lParam);
2985     if (GetWindowLongW(hwnd, GWL_STYLE) & CCS_NORESIZE)
2986         uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2987 
2988     SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2989             cx, cy, uPosFlags | SWP_NOZORDER);
2990   } else {
2991     FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2992   } */
2993 
2994   /* Recompute the size/position of the tabs. */
2995   TAB_SetItemBounds (infoPtr);
2996 
2997   /* Force a repaint of the control. */
2998   InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2999 
3000   return 0;
3001 }
3002 
3003 
3004 static LRESULT TAB_Create (HWND hwnd, LPARAM lParam)
3005 {
3006   TAB_INFO *infoPtr;
3007   TEXTMETRICW fontMetrics;
3008   HDC hdc;
3009   HFONT hOldFont;
3010   DWORD style;
3011 
3012   infoPtr = Alloc (sizeof(TAB_INFO));
3013 
3014   SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
3015 
3016   infoPtr->hwnd            = hwnd;
3017   infoPtr->hwndNotify      = ((LPCREATESTRUCTW)lParam)->hwndParent;
3018   infoPtr->uNumItem        = 0;
3019   infoPtr->uNumRows        = 0;
3020   infoPtr->uHItemPadding   = 6;
3021   infoPtr->uVItemPadding   = 3;
3022   infoPtr->uHItemPadding_s = 6;
3023   infoPtr->uVItemPadding_s = 3;
3024   infoPtr->hFont           = 0;
3025   infoPtr->items           = DPA_Create(8);
3026   infoPtr->hcurArrow       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3027   infoPtr->iSelected       = -1;
3028   infoPtr->iHotTracked     = -1;
3029   infoPtr->uFocus          = -1;
3030   infoPtr->hwndToolTip     = 0;
3031   infoPtr->DoRedraw        = TRUE;
3032   infoPtr->needsScrolling  = FALSE;
3033   infoPtr->hwndUpDown      = 0;
3034   infoPtr->leftmostVisible = 0;
3035   infoPtr->fHeightSet      = FALSE;
3036   infoPtr->bUnicode        = IsWindowUnicode (hwnd);
3037   infoPtr->cbInfo          = sizeof(LPARAM);
3038 
3039   TRACE("Created tab control, hwnd [%p]\n", hwnd);
3040 
3041   /* The tab control always has the WS_CLIPSIBLINGS style. Even
3042      if you don't specify it in CreateWindow. This is necessary in
3043      order for paint to work correctly. This follows windows behaviour. */
3044   style = GetWindowLongW(hwnd, GWL_STYLE);
3045   if (style & TCS_VERTICAL) style |= TCS_MULTILINE;
3046   style |= WS_CLIPSIBLINGS;
3047   SetWindowLongW(hwnd, GWL_STYLE, style);
3048 
3049   infoPtr->dwStyle = style;
3050   infoPtr->exStyle = (style & TCS_FLATBUTTONS) ? TCS_EX_FLATSEPARATORS : 0;
3051 
3052   if (infoPtr->dwStyle & TCS_TOOLTIPS) {
3053     /* Create tooltip control */
3054     infoPtr->hwndToolTip =
3055       CreateWindowExW (0, TOOLTIPS_CLASSW, NULL, WS_POPUP,
3056 		       CW_USEDEFAULT, CW_USEDEFAULT,
3057 		       CW_USEDEFAULT, CW_USEDEFAULT,
3058 		       hwnd, 0, 0, 0);
3059 
3060     /* Send NM_TOOLTIPSCREATED notification */
3061     if (infoPtr->hwndToolTip) {
3062       NMTOOLTIPSCREATED nmttc;
3063 
3064       nmttc.hdr.hwndFrom = hwnd;
3065       nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
3066       nmttc.hdr.code = NM_TOOLTIPSCREATED;
3067       nmttc.hwndToolTips = infoPtr->hwndToolTip;
3068 
3069       SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
3070                     GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc);
3071     }
3072   }
3073 
3074   OpenThemeData (infoPtr->hwnd, themeClass);
3075 
3076   /*
3077    * We need to get text information so we need a DC and we need to select
3078    * a font.
3079    */
3080   hdc = GetDC(hwnd);
3081   hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3082 
3083   /* Use the system font to determine the initial height of a tab. */
3084   GetTextMetricsW(hdc, &fontMetrics);
3085 
3086   /*
3087    * Make sure there is enough space for the letters + growing the
3088    * selected item + extra space for the selected item.
3089    */
3090   infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3091 	               ((infoPtr->dwStyle & TCS_BUTTONS) ? 2 : 1) *
3092                         infoPtr->uVItemPadding;
3093 
3094   /* Initialize the width of a tab. */
3095   if (infoPtr->dwStyle & TCS_FIXEDWIDTH)
3096     infoPtr->tabWidth = GetDeviceCaps(hdc, LOGPIXELSX);
3097 
3098   infoPtr->tabMinWidth = -1;
3099 
3100   TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3101 
3102   SelectObject (hdc, hOldFont);
3103   ReleaseDC(hwnd, hdc);
3104 
3105   return 0;
3106 }
3107 
3108 static LRESULT
3109 TAB_Destroy (TAB_INFO *infoPtr)
3110 {
3111   INT iItem;
3112 
3113   SetWindowLongPtrW(infoPtr->hwnd, 0, 0);
3114 
3115   for (iItem = infoPtr->uNumItem - 1; iItem >= 0; iItem--)
3116   {
3117       TAB_ITEM *tab = TAB_GetItem(infoPtr, iItem);
3118 
3119       DPA_DeletePtr(infoPtr->items, iItem);
3120       infoPtr->uNumItem--;
3121 
3122       Free(tab->pszText);
3123       Free(tab);
3124   }
3125   DPA_Destroy(infoPtr->items);
3126   infoPtr->items = NULL;
3127 
3128   if (infoPtr->hwndToolTip)
3129     DestroyWindow (infoPtr->hwndToolTip);
3130 
3131   if (infoPtr->hwndUpDown)
3132     DestroyWindow(infoPtr->hwndUpDown);
3133 
3134   if (infoPtr->iHotTracked >= 0)
3135     KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
3136 
3137   CloseThemeData (GetWindowTheme (infoPtr->hwnd));
3138 
3139   Free (infoPtr);
3140   return 0;
3141 }
3142 
3143 /* update theme after a WM_THEMECHANGED message */
3144 static LRESULT theme_changed(const TAB_INFO *infoPtr)
3145 {
3146     HTHEME theme = GetWindowTheme (infoPtr->hwnd);
3147     CloseThemeData (theme);
3148     OpenThemeData (infoPtr->hwnd, themeClass);
3149     return 0;
3150 }
3151 
3152 static LRESULT TAB_NCCalcSize(WPARAM wParam)
3153 {
3154   if (!wParam)
3155     return 0;
3156   return WVR_ALIGNTOP;
3157 }
3158 
3159 static inline LRESULT
3160 TAB_SetItemExtra (TAB_INFO *infoPtr, INT cbInfo)
3161 {
3162   TRACE("(%p %d)\n", infoPtr, cbInfo);
3163 
3164   if (cbInfo < 0 || infoPtr->uNumItem) return FALSE;
3165 
3166   infoPtr->cbInfo = cbInfo;
3167   return TRUE;
3168 }
3169 
3170 static LRESULT TAB_RemoveImage (TAB_INFO *infoPtr, INT image)
3171 {
3172   TRACE("%p %d\n", infoPtr, image);
3173 
3174   if (ImageList_Remove (infoPtr->himl, image))
3175   {
3176     INT i, *idx;
3177     RECT r;
3178 
3179     /* shift indices, repaint items if needed */
3180     for (i = 0; i < infoPtr->uNumItem; i++)
3181     {
3182       idx = &TAB_GetItem(infoPtr, i)->iImage;
3183       if (*idx >= image)
3184       {
3185         if (*idx == image)
3186           *idx = -1;
3187         else
3188           (*idx)--;
3189 
3190         /* repaint item */
3191         if (TAB_InternalGetItemRect (infoPtr, i, &r, NULL))
3192           InvalidateRect (infoPtr->hwnd, &r, TRUE);
3193       }
3194     }
3195   }
3196 
3197   return 0;
3198 }
3199 
3200 static LRESULT
3201 TAB_SetExtendedStyle (TAB_INFO *infoPtr, DWORD exMask, DWORD exStyle)
3202 {
3203   DWORD prevstyle = infoPtr->exStyle;
3204 
3205   /* zero mask means all styles */
3206   if (exMask == 0) exMask = ~0;
3207 
3208   if (exMask & TCS_EX_REGISTERDROP)
3209   {
3210     FIXME("TCS_EX_REGISTERDROP style unimplemented\n");
3211     exMask  &= ~TCS_EX_REGISTERDROP;
3212     exStyle &= ~TCS_EX_REGISTERDROP;
3213   }
3214 
3215   if (exMask & TCS_EX_FLATSEPARATORS)
3216   {
3217     if ((prevstyle ^ exStyle) & TCS_EX_FLATSEPARATORS)
3218     {
3219         infoPtr->exStyle ^= TCS_EX_FLATSEPARATORS;
3220         TAB_InvalidateTabArea(infoPtr);
3221     }
3222   }
3223 
3224   return prevstyle;
3225 }
3226 
3227 static inline LRESULT
3228 TAB_GetExtendedStyle (const TAB_INFO *infoPtr)
3229 {
3230   return infoPtr->exStyle;
3231 }
3232 
3233 static LRESULT
3234 TAB_DeselectAll (TAB_INFO *infoPtr, BOOL excludesel)
3235 {
3236   BOOL paint = FALSE;
3237   INT i, selected = infoPtr->iSelected;
3238 
3239   TRACE("(%p, %d)\n", infoPtr, excludesel);
3240 
3241   if (!(infoPtr->dwStyle & TCS_BUTTONS))
3242     return 0;
3243 
3244   for (i = 0; i < infoPtr->uNumItem; i++)
3245   {
3246     if ((TAB_GetItem(infoPtr, i)->dwState & TCIS_BUTTONPRESSED) &&
3247         (selected != i))
3248     {
3249       TAB_GetItem(infoPtr, i)->dwState &= ~TCIS_BUTTONPRESSED;
3250       paint = TRUE;
3251     }
3252   }
3253 
3254   if (!excludesel && (selected != -1))
3255   {
3256     TAB_GetItem(infoPtr, selected)->dwState &= ~TCIS_BUTTONPRESSED;
3257     infoPtr->iSelected = -1;
3258     paint = TRUE;
3259   }
3260 
3261   if (paint)
3262     TAB_InvalidateTabArea (infoPtr);
3263 
3264   return 0;
3265 }
3266 
3267 /***
3268  * DESCRIPTION:
3269  * Processes WM_STYLECHANGED messages.
3270  *
3271  * PARAMETER(S):
3272  * [I] infoPtr : valid pointer to the tab data structure
3273  * [I] wStyleType : window style type (normal or extended)
3274  * [I] lpss : window style information
3275  *
3276  * RETURN:
3277  * Zero
3278  */
3279 static INT TAB_StyleChanged(TAB_INFO *infoPtr, WPARAM wStyleType,
3280                             const STYLESTRUCT *lpss)
3281 {
3282     TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
3283           wStyleType, lpss->styleOld, lpss->styleNew);
3284 
3285     if (wStyleType != GWL_STYLE) return 0;
3286 
3287     infoPtr->dwStyle = lpss->styleNew;
3288 
3289     TAB_SetItemBounds (infoPtr);
3290     InvalidateRect(infoPtr->hwnd, NULL, TRUE);
3291 
3292     return 0;
3293 }
3294 
3295 static LRESULT WINAPI
3296 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3297 {
3298     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3299 
3300     TRACE("hwnd=%p msg=%x wParam=%lx lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3301     if (!infoPtr && (uMsg != WM_CREATE))
3302       return DefWindowProcW (hwnd, uMsg, wParam, lParam);
3303 
3304     switch (uMsg)
3305     {
3306     case TCM_GETIMAGELIST:
3307       return TAB_GetImageList (infoPtr);
3308 
3309     case TCM_SETIMAGELIST:
3310       return TAB_SetImageList (infoPtr, (HIMAGELIST)lParam);
3311 
3312     case TCM_GETITEMCOUNT:
3313       return TAB_GetItemCount (infoPtr);
3314 
3315     case TCM_GETITEMA:
3316     case TCM_GETITEMW:
3317       return TAB_GetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_GETITEMW);
3318 
3319     case TCM_SETITEMA:
3320     case TCM_SETITEMW:
3321       return TAB_SetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_SETITEMW);
3322 
3323     case TCM_DELETEITEM:
3324       return TAB_DeleteItem (infoPtr, (INT)wParam);
3325 
3326     case TCM_DELETEALLITEMS:
3327      return TAB_DeleteAllItems (infoPtr);
3328 
3329     case TCM_GETITEMRECT:
3330      return TAB_GetItemRect (infoPtr, (INT)wParam, (LPRECT)lParam);
3331 
3332     case TCM_GETCURSEL:
3333       return TAB_GetCurSel (infoPtr);
3334 
3335     case TCM_HITTEST:
3336       return TAB_HitTest (infoPtr, (LPTCHITTESTINFO)lParam);
3337 
3338     case TCM_SETCURSEL:
3339       return TAB_SetCurSel (infoPtr, (INT)wParam);
3340 
3341     case TCM_INSERTITEMA:
3342     case TCM_INSERTITEMW:
3343       return TAB_InsertItemT (infoPtr, (INT)wParam, (TCITEMW*)lParam, uMsg == TCM_INSERTITEMW);
3344 
3345     case TCM_SETITEMEXTRA:
3346       return TAB_SetItemExtra (infoPtr, (INT)wParam);
3347 
3348     case TCM_ADJUSTRECT:
3349       return TAB_AdjustRect (infoPtr, (BOOL)wParam, (LPRECT)lParam);
3350 
3351     case TCM_SETITEMSIZE:
3352       return TAB_SetItemSize (infoPtr, (INT)LOWORD(lParam), (INT)HIWORD(lParam));
3353 
3354     case TCM_REMOVEIMAGE:
3355       return TAB_RemoveImage (infoPtr, (INT)wParam);
3356 
3357     case TCM_SETPADDING:
3358       return TAB_SetPadding (infoPtr, lParam);
3359 
3360     case TCM_GETROWCOUNT:
3361       return TAB_GetRowCount(infoPtr);
3362 
3363     case TCM_GETUNICODEFORMAT:
3364       return TAB_GetUnicodeFormat (infoPtr);
3365 
3366     case TCM_SETUNICODEFORMAT:
3367       return TAB_SetUnicodeFormat (infoPtr, (BOOL)wParam);
3368 
3369     case TCM_HIGHLIGHTITEM:
3370       return TAB_HighlightItem (infoPtr, (INT)wParam, (BOOL)LOWORD(lParam));
3371 
3372     case TCM_GETTOOLTIPS:
3373       return TAB_GetToolTips (infoPtr);
3374 
3375     case TCM_SETTOOLTIPS:
3376       return TAB_SetToolTips (infoPtr, (HWND)wParam);
3377 
3378     case TCM_GETCURFOCUS:
3379       return TAB_GetCurFocus (infoPtr);
3380 
3381     case TCM_SETCURFOCUS:
3382       return TAB_SetCurFocus (infoPtr, (INT)wParam);
3383 
3384     case TCM_SETMINTABWIDTH:
3385       return TAB_SetMinTabWidth(infoPtr, (INT)lParam);
3386 
3387     case TCM_DESELECTALL:
3388       return TAB_DeselectAll (infoPtr, (BOOL)wParam);
3389 
3390     case TCM_GETEXTENDEDSTYLE:
3391       return TAB_GetExtendedStyle (infoPtr);
3392 
3393     case TCM_SETEXTENDEDSTYLE:
3394       return TAB_SetExtendedStyle (infoPtr, wParam, lParam);
3395 
3396     case WM_GETFONT:
3397       return TAB_GetFont (infoPtr);
3398 
3399     case WM_SETFONT:
3400       return TAB_SetFont (infoPtr, (HFONT)wParam);
3401 
3402     case WM_CREATE:
3403       return TAB_Create (hwnd, lParam);
3404 
3405     case WM_NCDESTROY:
3406       return TAB_Destroy (infoPtr);
3407 
3408     case WM_GETDLGCODE:
3409       return DLGC_WANTARROWS | DLGC_WANTCHARS;
3410 
3411     case WM_LBUTTONDOWN:
3412       return TAB_LButtonDown (infoPtr, wParam, lParam);
3413 
3414     case WM_LBUTTONUP:
3415       return TAB_LButtonUp (infoPtr);
3416 
3417     case WM_NOTIFY:
3418       return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3419 
3420     case WM_RBUTTONUP:
3421       TAB_RButtonUp (infoPtr);
3422       return DefWindowProcW (hwnd, uMsg, wParam, lParam);
3423 
3424     case WM_MOUSEMOVE:
3425       return TAB_MouseMove (infoPtr, wParam, lParam);
3426 
3427     case WM_PRINTCLIENT:
3428     case WM_PAINT:
3429       return TAB_Paint (infoPtr, (HDC)wParam);
3430 
3431     case WM_SIZE:
3432       return TAB_Size (infoPtr);
3433 
3434     case WM_SETREDRAW:
3435       return TAB_SetRedraw (infoPtr, (BOOL)wParam);
3436 
3437     case WM_HSCROLL:
3438       return TAB_OnHScroll(infoPtr, (int)LOWORD(wParam), (int)HIWORD(wParam));
3439 
3440     case WM_STYLECHANGED:
3441       return TAB_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
3442 
3443     case WM_SYSCOLORCHANGE:
3444       COMCTL32_RefreshSysColors();
3445       return 0;
3446 
3447     case WM_THEMECHANGED:
3448       return theme_changed (infoPtr);
3449 
3450     case WM_KILLFOCUS:
3451       TAB_KillFocus(infoPtr);
3452     case WM_SETFOCUS:
3453       TAB_FocusChanging(infoPtr);
3454       break;   /* Don't disturb normal focus behavior */
3455 
3456     case WM_KEYDOWN:
3457       return TAB_KeyDown(infoPtr, wParam, lParam);
3458 
3459     case WM_NCHITTEST:
3460       return TAB_NCHitTest(infoPtr, lParam);
3461 
3462     case WM_NCCALCSIZE:
3463       return TAB_NCCalcSize(wParam);
3464 
3465     default:
3466       if (uMsg >= WM_USER && uMsg < WM_APP && !COMCTL32_IsReflectedMessage(uMsg))
3467 	WARN("unknown msg %04x wp=%08lx lp=%08lx\n",
3468 	     uMsg, wParam, lParam);
3469       break;
3470     }
3471     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
3472 }
3473 
3474 
3475 void
3476 TAB_Register (void)
3477 {
3478   WNDCLASSW wndClass;
3479 
3480   ZeroMemory (&wndClass, sizeof(WNDCLASSW));
3481   wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3482   wndClass.lpfnWndProc   = TAB_WindowProc;
3483   wndClass.cbClsExtra    = 0;
3484   wndClass.cbWndExtra    = sizeof(TAB_INFO *);
3485   wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3486   wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
3487   wndClass.lpszClassName = WC_TABCONTROLW;
3488 
3489   RegisterClassW (&wndClass);
3490 }
3491 
3492 
3493 void
3494 TAB_Unregister (void)
3495 {
3496     UnregisterClassW (WC_TABCONTROLW, NULL);
3497 }
3498