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