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