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