hhctrl.ocx: Resize tab control in Child's WM_SIZE handler.
[wine] / dlls / comctl32 / tab.c
1 /*
2  * Tab control
3  *
4  * Copyright 1998 Anders Carlsson
5  * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6  * Copyright 1999 Francis Beaudet
7  * Copyright 2003 Vitaliy Margolen
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  *
23  * NOTES
24  *
25  * This code was audited for completeness against the documented features
26  * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
27  *
28  * Unless otherwise noted, we believe this code to be complete, as per
29  * the specification mentioned above.
30  * If you discover missing features, or bugs, please note them below.
31  *
32  * TODO:
33  *
34  *  Styles:
35  *   TCS_MULTISELECT
36  *   TCS_RIGHT
37  *   TCS_RIGHTJUSTIFY
38  *   TCS_SCROLLOPPOSITE
39  *   TCS_SINGLELINE
40  *   TCIF_RTLREADING
41  *
42  *  Extended Styles:
43  *   TCS_EX_FLATSEPARATORS
44  *   TCS_EX_REGISTERDROP
45  *
46  *  States:
47  *   TCIS_BUTTONPRESSED
48  *
49  *  Notifications:
50  *   NM_RELEASEDCAPTURE
51  *   TCN_FOCUSCHANGE
52  *   TCN_GETOBJECT
53  *   TCN_KEYDOWN
54  *
55  *  Messages:
56  *   TCM_REMOVEIMAGE
57  *   TCM_DESELECTALL
58  *   TCM_GETEXTENDEDSTYLE
59  *   TCM_SETEXTENDEDSTYLE
60  *
61  *  Macros:
62  *   TabCtrl_AdjustRect
63  *
64  */
65
66 #include <stdarg.h>
67 #include <string.h>
68
69 #include "windef.h"
70 #include "winbase.h"
71 #include "wingdi.h"
72 #include "winuser.h"
73 #include "winnls.h"
74 #include "commctrl.h"
75 #include "comctl32.h"
76 #include "uxtheme.h"
77 #include "tmschema.h"
78 #include "wine/debug.h"
79 #include <math.h>
80
81 WINE_DEFAULT_DEBUG_CHANNEL(tab);
82
83 typedef struct
84 {
85   DWORD  dwState;
86   LPWSTR pszText;
87   INT    iImage;
88   RECT   rect;      /* bounding rectangle of the item relative to the
89                      * leftmost item (the leftmost item, 0, would have a
90                      * "left" member of 0 in this rectangle)
91                      *
92                      * additionally the top member holds the row number
93                      * and bottom is unused and should be 0 */
94   BYTE   extra[1];  /* Space for caller supplied info, variable size */
95 } TAB_ITEM;
96
97 /* The size of a tab item depends on how much extra data is requested */
98 #define TAB_ITEM_SIZE(infoPtr) (FIELD_OFFSET(TAB_ITEM, extra[(infoPtr)->cbInfo]))
99
100 typedef struct
101 {
102   HWND       hwnd;            /* Tab control window */
103   HWND       hwndNotify;      /* notification window (parent) */
104   UINT       uNumItem;        /* number of tab items */
105   UINT       uNumRows;        /* number of tab rows */
106   INT        tabHeight;       /* height of the tab row */
107   INT        tabWidth;        /* width of tabs */
108   INT        tabMinWidth;     /* minimum width of items */
109   USHORT     uHItemPadding;   /* amount of horizontal padding, in pixels */
110   USHORT     uVItemPadding;   /* amount of vertical padding, in pixels */
111   USHORT     uHItemPadding_s; /* Set amount of horizontal padding, in pixels */
112   USHORT     uVItemPadding_s; /* Set amount of vertical padding, in pixels */
113   HFONT      hFont;           /* handle to the current font */
114   HCURSOR    hcurArrow;       /* handle to the current cursor */
115   HIMAGELIST himl;            /* handle to an image list (may be 0) */
116   HWND       hwndToolTip;     /* handle to tab's tooltip */
117   INT        leftmostVisible; /* Used for scrolling, this member contains
118                                * the index of the first visible item */
119   INT        iSelected;       /* the currently selected item */
120   INT        iHotTracked;     /* the highlighted item under the mouse */
121   INT        uFocus;          /* item which has the focus */
122   TAB_ITEM*  items;           /* pointer to an array of TAB_ITEM's */
123   BOOL       DoRedraw;        /* flag for redrawing when tab contents is changed*/
124   BOOL       needsScrolling;  /* TRUE if the size of the tabs is greater than
125                                * the size of the control */
126   BOOL       fHeightSet;      /* was the height of the tabs explicitly set? */
127   BOOL       bUnicode;        /* Unicode control? */
128   HWND       hwndUpDown;      /* Updown control used for scrolling */
129   INT        cbInfo;          /* Number of bytes of caller supplied info per tab */
130 } TAB_INFO;
131
132 /******************************************************************************
133  * Positioning constants
134  */
135 #define SELECTED_TAB_OFFSET     2
136 #define ROUND_CORNER_SIZE       2
137 #define DISPLAY_AREA_PADDINGX   2
138 #define DISPLAY_AREA_PADDINGY   2
139 #define CONTROL_BORDER_SIZEX    2
140 #define CONTROL_BORDER_SIZEY    2
141 #define BUTTON_SPACINGX         3
142 #define BUTTON_SPACINGY         3
143 #define FLAT_BTN_SPACINGX       8
144 #define DEFAULT_MIN_TAB_WIDTH   54
145 #define DEFAULT_TAB_WIDTH_FIXED 96
146 #define DEFAULT_PADDING_X       6
147 #define EXTRA_ICON_PADDING      3
148
149 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongPtrW(hwnd,0))
150 /* Since items are variable sized, cannot directly access them */
151 #define TAB_GetItem(info,i) \
152   ((TAB_ITEM*)((LPBYTE)info->items + (i) * TAB_ITEM_SIZE(info)))
153
154 #define GET_DEFAULT_MIN_TAB_WIDTH(infoPtr) (DEFAULT_MIN_TAB_WIDTH - (DEFAULT_PADDING_X - (infoPtr)->uHItemPadding) * 2)
155
156 /******************************************************************************
157  * Hot-tracking timer constants
158  */
159 #define TAB_HOTTRACK_TIMER            1
160 #define TAB_HOTTRACK_TIMER_INTERVAL   100   /* milliseconds */
161
162 static const WCHAR themeClass[] = { 'T','a','b',0 };
163
164 /******************************************************************************
165  * Prototypes
166  */
167 static void TAB_InvalidateTabArea(TAB_INFO *);
168 static void TAB_EnsureSelectionVisible(TAB_INFO *);
169 static void TAB_DrawItemInterior(TAB_INFO *, HDC, INT, RECT*);
170
171 static BOOL
172 TAB_SendSimpleNotify (const TAB_INFO *infoPtr, UINT code)
173 {
174     NMHDR nmhdr;
175
176     nmhdr.hwndFrom = infoPtr->hwnd;
177     nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID);
178     nmhdr.code = code;
179
180     return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
181             (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
182 }
183
184 static void
185 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
186             WPARAM wParam, LPARAM lParam)
187 {
188     MSG msg;
189
190     msg.hwnd = hwndMsg;
191     msg.message = uMsg;
192     msg.wParam = wParam;
193     msg.lParam = lParam;
194     msg.time = GetMessageTime ();
195     msg.pt.x = (short)LOWORD(GetMessagePos ());
196     msg.pt.y = (short)HIWORD(GetMessagePos ());
197
198     SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
199 }
200
201 static void
202 TAB_DumpItemExternalT(TCITEMW *pti, UINT iItem, BOOL isW)
203 {
204     if (TRACE_ON(tab)) {
205         TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
206               iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
207         TRACE("external tab %d,   iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
208               iItem, pti->iImage, pti->lParam, isW ? debugstr_w(pti->pszText) : debugstr_a((LPSTR)pti->pszText));
209     }
210 }
211
212 static void
213 TAB_DumpItemInternal(TAB_INFO *infoPtr, UINT iItem)
214 {
215     if (TRACE_ON(tab)) {
216         TAB_ITEM *ti;
217
218         ti = TAB_GetItem(infoPtr, iItem);
219         TRACE("tab %d, dwState=0x%08x, pszText=%s, iImage=%d\n",
220               iItem, ti->dwState, debugstr_w(ti->pszText), ti->iImage);
221         TRACE("tab %d, rect.left=%d, rect.top(row)=%d\n",
222               iItem, ti->rect.left, ti->rect.top);
223     }
224 }
225
226 /* RETURNS
227  *   the index of the selected tab, or -1 if no tab is selected. */
228 static inline LRESULT TAB_GetCurSel (const TAB_INFO *infoPtr)
229 {
230     return infoPtr->iSelected;
231 }
232
233 /* RETURNS
234  *   the index of the tab item that has the focus
235  * NOTE
236  *   we have not to return negative value
237  * TODO
238  *   test for windows */
239 static inline LRESULT
240 TAB_GetCurFocus (const TAB_INFO *infoPtr)
241 {
242     if (infoPtr->uFocus<0)
243     {
244         FIXME("we have not to return negative value\n");
245         return 0;
246     }
247     return infoPtr->uFocus;
248 }
249
250 static inline LRESULT TAB_GetToolTips (const TAB_INFO *infoPtr)
251 {
252     if (infoPtr == NULL) return 0;
253     return (LRESULT)infoPtr->hwndToolTip;
254 }
255
256 static inline LRESULT TAB_SetCurSel (TAB_INFO *infoPtr, INT iItem)
257 {
258   INT prevItem = -1;
259
260   if (iItem >= 0 && iItem < infoPtr->uNumItem) {
261       prevItem=infoPtr->iSelected;
262       if (infoPtr->iSelected != iItem) {
263           infoPtr->iSelected=iItem;
264           TAB_EnsureSelectionVisible(infoPtr);
265           TAB_InvalidateTabArea(infoPtr);
266       }
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=(%d,%d)-(%d,%d)\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 (%d,%d)-(%d,%d)\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 = (short)LOWORD(lParam);
591   pt.y = (short)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 = (short)LOWORD(lParam);
620   pt.y = (short)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 = (short)LOWORD(*pos);
792       pt.y = (short)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 (%d,%d)-(%d,%d)\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=%d, iconh=%d\n",
1147           infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1148   }
1149
1150   TRACE("client right=%d\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=%d,%d\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=%d,%d\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 %i, L %i, B %i, R %i\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=%d, cl=%d, cl.r=%d, 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=%d,%d, row=%d\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=%d,%d\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=%d,%d\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=(%d,%d)-(%d,%d)\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->iImage != -1)
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->left + (drawRect->right - drawRect->left) - cx) / 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->top + (drawRect->bottom - drawRect->top) - cy) / 2;
1754       }
1755
1756       /* if an item is selected, the icon is shifted up instead of down */
1757       if (iItem == infoPtr->iSelected)
1758         center_offset_v -= infoPtr->uVItemPadding / 2;
1759       else
1760         center_offset_v += infoPtr->uVItemPadding / 2;
1761
1762       if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1763         center_offset_h = infoPtr->uHItemPadding;
1764
1765       if (center_offset_h < 2)
1766         center_offset_h = 2;
1767         
1768       if (center_offset_v < 0)
1769         center_offset_v = 0;
1770         
1771       TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%d,%d)-(%d,%d), textlen=%d\n",
1772           debugstr_w(item->pszText), center_offset_h, center_offset_v,
1773           drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1774           (rcText.right-rcText.left));
1775
1776       if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1777       {
1778         rcImage.top = drawRect->top + center_offset_h;
1779         /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1780         /* right side of the tab, but the image still uses the left as its x position */
1781         /* this keeps the image always drawn off of the same side of the tab */
1782         rcImage.left = drawRect->right - cx - center_offset_v;
1783         drawRect->top += cy + infoPtr->uHItemPadding;
1784       }
1785       else if(lStyle & TCS_VERTICAL)
1786       {
1787         rcImage.top  = drawRect->bottom - cy - center_offset_h;
1788         rcImage.left = drawRect->left + center_offset_v;
1789         drawRect->bottom -= cy + infoPtr->uHItemPadding;
1790       }
1791       else /* normal style, whether TCS_BOTTOM or not */
1792       {
1793         rcImage.left = drawRect->left + center_offset_h;
1794         rcImage.top = drawRect->top + center_offset_v;
1795         drawRect->left += cx + infoPtr->uHItemPadding;
1796       }
1797
1798       TRACE("drawing image=%d, left=%d, top=%d\n",
1799             item->iImage, rcImage.left, rcImage.top-1);
1800       ImageList_Draw
1801         (
1802         infoPtr->himl,
1803         item->iImage,
1804         hdc,
1805         rcImage.left,
1806         rcImage.top,
1807         ILD_NORMAL
1808         );
1809     }
1810
1811     /* Now position text */
1812     if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT)
1813       center_offset_h = infoPtr->uHItemPadding;
1814     else
1815       if(lStyle & TCS_VERTICAL)
1816         center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1817       else
1818         center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1819
1820     if(lStyle & TCS_VERTICAL)
1821     {
1822       if(lStyle & TCS_BOTTOM)
1823         drawRect->top+=center_offset_h;
1824       else
1825         drawRect->bottom-=center_offset_h;
1826
1827       center_offset_v = ((drawRect->right - drawRect->left) - (rcText.bottom - rcText.top)) / 2;
1828     }
1829     else
1830     {
1831       drawRect->left += center_offset_h;
1832       center_offset_v = ((drawRect->bottom - drawRect->top) - (rcText.bottom - rcText.top)) / 2;
1833     }
1834
1835     /* if an item is selected, the text is shifted up instead of down */
1836     if (iItem == infoPtr->iSelected)
1837         center_offset_v -= infoPtr->uVItemPadding / 2;
1838     else
1839         center_offset_v += infoPtr->uVItemPadding / 2;
1840
1841     if (center_offset_v < 0)
1842       center_offset_v = 0;
1843
1844     if(lStyle & TCS_VERTICAL)
1845       drawRect->left += center_offset_v;
1846     else
1847       drawRect->top += center_offset_v;
1848
1849     /* Draw the text */
1850     if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1851     {
1852       static const WCHAR ArialW[] = { 'A','r','i','a','l',0 };
1853       LOGFONTW logfont;
1854       HFONT hFont = 0;
1855       INT nEscapement = 900;
1856       INT nOrientation = 900;
1857
1858       if(lStyle & TCS_BOTTOM)
1859       {
1860         nEscapement = -900;
1861         nOrientation = -900;
1862       }
1863
1864       /* to get a font with the escapement and orientation we are looking for, we need to */
1865       /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1866       if (!GetObjectW((infoPtr->hFont) ?
1867                 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1868                 sizeof(LOGFONTW),&logfont))
1869       {
1870         INT iPointSize = 9;
1871
1872         lstrcpyW(logfont.lfFaceName, ArialW);
1873         logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1874                                     72);
1875         logfont.lfWeight = FW_NORMAL;
1876         logfont.lfItalic = 0;
1877         logfont.lfUnderline = 0;
1878         logfont.lfStrikeOut = 0;
1879       }
1880
1881       logfont.lfEscapement = nEscapement;
1882       logfont.lfOrientation = nOrientation;
1883       hFont = CreateFontIndirectW(&logfont);
1884       SelectObject(hdc, hFont);
1885
1886       if (item->pszText)
1887       {
1888         ExtTextOutW(hdc,
1889         (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1890         (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1891         ETO_CLIPPED,
1892         drawRect,
1893         item->pszText,
1894         lstrlenW(item->pszText),
1895         0);
1896       }
1897
1898       DeleteObject(hFont);
1899     }
1900     else
1901     {
1902       TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%d,%d)-(%d,%d), textlen=%d\n",
1903           debugstr_w(item->pszText), center_offset_h, center_offset_v,
1904           drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1905           (rcText.right-rcText.left));
1906       if (item->pszText)
1907       {
1908         DrawTextW
1909         (
1910           hdc,
1911           item->pszText,
1912           lstrlenW(item->pszText),
1913           drawRect,
1914           DT_LEFT | DT_SINGLELINE
1915         );
1916       }
1917     }
1918
1919     *drawRect = rcTemp; /* restore drawRect */
1920   }
1921
1922   /*
1923   * Cleanup
1924   */
1925   SelectObject(hdc, hOldFont);
1926   SetBkMode(hdc, oldBkMode);
1927   SelectObject(hdc, holdPen);
1928   DeleteObject( htextPen );
1929 }
1930
1931 /******************************************************************************
1932  * TAB_DrawItem
1933  *
1934  * This method is used to draw a single tab into the tab control.
1935  */
1936 static void TAB_DrawItem(
1937   TAB_INFO *infoPtr,
1938   HDC  hdc,
1939   INT  iItem)
1940 {
1941   LONG      lStyle  = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1942   RECT      itemRect;
1943   RECT      selectedRect;
1944   BOOL      isVisible;
1945   RECT      r, fillRect, r1;
1946   INT       clRight = 0;
1947   INT       clBottom = 0;
1948   COLORREF  bkgnd, corner;
1949   HTHEME    theme;
1950
1951   /*
1952    * Get the rectangle for the item.
1953    */
1954   isVisible = TAB_InternalGetItemRect(infoPtr,
1955                                       iItem,
1956                                       &itemRect,
1957                                       &selectedRect);
1958
1959   if (isVisible)
1960   {
1961     RECT rUD, rC;
1962
1963     /* Clip UpDown control to not draw over it */
1964     if (infoPtr->needsScrolling)
1965     {
1966       GetWindowRect(infoPtr->hwnd, &rC);
1967       GetWindowRect(infoPtr->hwndUpDown, &rUD);
1968       ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
1969     }
1970
1971     /* If you need to see what the control is doing,
1972      * then override these variables. They will change what
1973      * fill colors are used for filling the tabs, and the
1974      * corners when drawing the edge.
1975      */
1976     bkgnd = comctl32_color.clrBtnFace;
1977     corner = comctl32_color.clrBtnFace;
1978
1979     if (lStyle & TCS_BUTTONS)
1980     {
1981       /* Get item rectangle */
1982       r = itemRect;
1983
1984       /* Separators between flat buttons */
1985       if (lStyle & TCS_FLATBUTTONS)
1986       {
1987         r1 = r;
1988         r1.right += (FLAT_BTN_SPACINGX -2);
1989         DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1990       }
1991
1992       if (iItem == infoPtr->iSelected)
1993       {
1994         DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1995         
1996         OffsetRect(&r, 1, 1);
1997       }
1998       else  /* ! selected */
1999       {
2000         if (!(lStyle & TCS_FLATBUTTONS))
2001           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
2002       }
2003     }
2004     else /* !TCS_BUTTONS */
2005     {
2006       /* We draw a rectangle of different sizes depending on the selection
2007        * state. */
2008       if (iItem == infoPtr->iSelected) {
2009         RECT rect;
2010         GetClientRect (infoPtr->hwnd, &rect);
2011         clRight = rect.right;
2012         clBottom = rect.bottom;
2013         r = selectedRect;
2014       }
2015       else
2016         r = itemRect;
2017
2018       /*
2019        * Erase the background. (Delay it but setup rectangle.)
2020        * This is necessary when drawing the selected item since it is larger
2021        * than the others, it might overlap with stuff already drawn by the
2022        * other tabs
2023        */
2024       fillRect = r;
2025
2026       /* Draw themed tabs - but only if they are at the top.
2027        * Windows draws even side or bottom tabs themed, with wacky results.
2028        * However, since in Wine apps may get themed that did not opt in via
2029        * a manifest avoid theming when we know the result will be wrong */
2030       if ((theme = GetWindowTheme (infoPtr->hwnd)) 
2031           && ((lStyle & (TCS_VERTICAL | TCS_BOTTOM)) == 0))
2032       {
2033           static const int partIds[8] = {
2034               /* Normal item */
2035               TABP_TABITEM,
2036               TABP_TABITEMLEFTEDGE,
2037               TABP_TABITEMRIGHTEDGE,
2038               TABP_TABITEMBOTHEDGE,
2039               /* Selected tab */
2040               TABP_TOPTABITEM,
2041               TABP_TOPTABITEMLEFTEDGE,
2042               TABP_TOPTABITEMRIGHTEDGE,
2043               TABP_TOPTABITEMBOTHEDGE,
2044           };
2045           int partIndex = 0;
2046           int stateId = TIS_NORMAL;
2047
2048           /* selected and unselected tabs have different parts */
2049           if (iItem == infoPtr->iSelected)
2050               partIndex += 4;
2051           /* The part also differs on the position of a tab on a line.
2052            * "Visually" determining the position works well enough. */
2053           if(selectedRect.left == 0)
2054               partIndex += 1;
2055           if(selectedRect.right == clRight)
2056               partIndex += 2;
2057
2058           if (iItem == infoPtr->iSelected)
2059               stateId = TIS_SELECTED;
2060           else if (iItem == infoPtr->iHotTracked)
2061               stateId = TIS_HOT;
2062           else if (iItem == infoPtr->uFocus)
2063               stateId = TIS_FOCUSED;
2064
2065           /* Adjust rectangle for bottommost row */
2066           if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2067             r.bottom += 3;
2068
2069           DrawThemeBackground (theme, hdc, partIds[partIndex], stateId, &r, NULL);
2070           GetThemeBackgroundContentRect (theme, hdc, partIds[partIndex], stateId, &r, &r);
2071       }
2072       else if(lStyle & TCS_VERTICAL)
2073       {
2074         /* These are for adjusting the drawing of a Selected tab      */
2075         /* The initial values are for the normal case of non-Selected */
2076         int ZZ = 1;   /* Do not strech if selected */
2077         if (iItem == infoPtr->iSelected) {
2078             ZZ = 0;
2079
2080             /* if leftmost draw the line longer */
2081             if(selectedRect.top == 0)
2082                 fillRect.top += CONTROL_BORDER_SIZEY;
2083             /* if rightmost draw the line longer */
2084             if(selectedRect.bottom == clBottom)
2085                 fillRect.bottom -= CONTROL_BORDER_SIZEY;
2086         }
2087
2088         if (lStyle & TCS_BOTTOM)
2089         {
2090           /* Adjust both rectangles to match native */
2091           r.left += (1-ZZ);
2092
2093           TRACE("<right> item=%d, fill=(%d,%d)-(%d,%d), edge=(%d,%d)-(%d,%d)\n",
2094                 iItem,
2095                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2096                 r.left,r.top,r.right,r.bottom);
2097
2098           /* Clear interior */
2099           SetBkColor(hdc, bkgnd);
2100           ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2101
2102           /* Draw rectangular edge around tab */
2103           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
2104
2105           /* Now erase the top corner and draw diagonal edge */
2106           SetBkColor(hdc, corner);
2107           r1.left = r.right - ROUND_CORNER_SIZE - 1;
2108           r1.top = r.top;
2109           r1.right = r.right;
2110           r1.bottom = r1.top + ROUND_CORNER_SIZE;
2111           ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2112           r1.right--;
2113           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2114
2115           /* Now erase the bottom corner and draw diagonal edge */
2116           r1.left = r.right - ROUND_CORNER_SIZE - 1;
2117           r1.bottom = r.bottom;
2118           r1.right = r.right;
2119           r1.top = r1.bottom - ROUND_CORNER_SIZE;
2120           ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2121           r1.right--;
2122           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2123
2124           if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
2125               r1 = r;
2126               r1.right = r1.left;
2127               r1.left--;
2128               DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
2129           }
2130
2131         }
2132         else
2133         {
2134           TRACE("<left> item=%d, fill=(%d,%d)-(%d,%d), edge=(%d,%d)-(%d,%d)\n",
2135                 iItem,
2136                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2137                 r.left,r.top,r.right,r.bottom);
2138
2139           /* Clear interior */
2140           SetBkColor(hdc, bkgnd);
2141           ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2142
2143           /* Draw rectangular edge around tab */
2144           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2145
2146           /* Now erase the top corner and draw diagonal edge */
2147           SetBkColor(hdc, corner);
2148           r1.left = r.left;
2149           r1.top = r.top;
2150           r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2151           r1.bottom = r1.top + ROUND_CORNER_SIZE;
2152           ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2153           r1.left++;
2154           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2155
2156           /* Now erase the bottom corner and draw diagonal edge */
2157           r1.left = r.left;
2158           r1.bottom = r.bottom;
2159           r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2160           r1.top = r1.bottom - ROUND_CORNER_SIZE;
2161           ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2162           r1.left++;
2163           DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2164         }
2165       }
2166       else  /* ! TCS_VERTICAL */
2167       {
2168         /* These are for adjusting the drawing of a Selected tab      */
2169         /* The initial values are for the normal case of non-Selected */
2170         if (iItem == infoPtr->iSelected) {
2171             /* if leftmost draw the line longer */
2172             if(selectedRect.left == 0)
2173                 fillRect.left += CONTROL_BORDER_SIZEX;
2174             /* if rightmost draw the line longer */
2175             if(selectedRect.right == clRight)
2176                 fillRect.right -= CONTROL_BORDER_SIZEX;
2177         }
2178
2179         if (lStyle & TCS_BOTTOM)
2180         {
2181           /* Adjust both rectangles for topmost row */
2182           if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2183           {
2184             fillRect.top -= 2;
2185             r.top -= 1;
2186           }
2187
2188           TRACE("<bottom> item=%d, fill=(%d,%d)-(%d,%d), edge=(%d,%d)-(%d,%d)\n",
2189                 iItem,
2190                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2191                 r.left,r.top,r.right,r.bottom);
2192
2193           /* Clear interior */
2194           SetBkColor(hdc, bkgnd);
2195           ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2196
2197           /* Draw rectangular edge around tab */
2198           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2199
2200           /* Now erase the righthand corner and draw diagonal edge */
2201           SetBkColor(hdc, corner);
2202           r1.left = r.right - ROUND_CORNER_SIZE;
2203           r1.bottom = r.bottom;
2204           r1.right = r.right;
2205           r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2206           ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2207           r1.bottom--;
2208           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2209
2210           /* Now erase the lefthand corner and draw diagonal edge */
2211           r1.left = r.left;
2212           r1.bottom = r.bottom;
2213           r1.right = r1.left + ROUND_CORNER_SIZE;
2214           r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2215           ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2216           r1.bottom--;
2217           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2218
2219           if (iItem == infoPtr->iSelected)
2220           {
2221             r.top += 2;
2222             r.left += 1;
2223             if (selectedRect.left == 0)
2224             {
2225               r1 = r;
2226               r1.bottom = r1.top;
2227               r1.top--;
2228               DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2229             }
2230           }
2231
2232         }
2233         else
2234         {
2235           /* Adjust both rectangles for bottommost row */
2236           if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2237           {
2238             fillRect.bottom += 3;
2239             r.bottom += 2;
2240           }
2241
2242           TRACE("<top> item=%d, fill=(%d,%d)-(%d,%d), edge=(%d,%d)-(%d,%d)\n",
2243                 iItem,
2244                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2245                 r.left,r.top,r.right,r.bottom);
2246
2247           /* Clear interior */
2248           SetBkColor(hdc, bkgnd);
2249           ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2250
2251           /* Draw rectangular edge around tab */
2252           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2253
2254           /* Now erase the righthand corner and draw diagonal edge */
2255           SetBkColor(hdc, corner);
2256           r1.left = r.right - ROUND_CORNER_SIZE;
2257           r1.top = r.top;
2258           r1.right = r.right;
2259           r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2260           ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2261           r1.top++;
2262           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2263
2264           /* Now erase the lefthand corner and draw diagonal edge */
2265           r1.left = r.left;
2266           r1.top = r.top;
2267           r1.right = r1.left + ROUND_CORNER_SIZE;
2268           r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2269           ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2270           r1.top++;
2271           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2272         }
2273       }
2274     }
2275
2276     TAB_DumpItemInternal(infoPtr, iItem);
2277
2278     /* This modifies r to be the text rectangle. */
2279     TAB_DrawItemInterior(infoPtr, hdc, iItem, &r);
2280   }
2281 }
2282
2283 /******************************************************************************
2284  * TAB_DrawBorder
2285  *
2286  * This method is used to draw the raised border around the tab control
2287  * "content" area.
2288  */
2289 static void TAB_DrawBorder (TAB_INFO *infoPtr, HDC hdc)
2290 {
2291   RECT rect;
2292   DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2293   HTHEME theme = GetWindowTheme (infoPtr->hwnd);
2294
2295   GetClientRect (infoPtr->hwnd, &rect);
2296
2297   /*
2298    * Adjust for the style
2299    */
2300
2301   if (infoPtr->uNumItem)
2302   {
2303     if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2304       rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2305     else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2306       rect.right  -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2307     else if(lStyle & TCS_VERTICAL)
2308       rect.left   += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2309     else /* not TCS_VERTICAL and not TCS_BOTTOM */
2310       rect.top    += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2311   }
2312
2313   TRACE("border=(%d,%d)-(%d,%d)\n",
2314         rect.left, rect.top, rect.right, rect.bottom);
2315
2316   if (theme)
2317       DrawThemeBackground (theme, hdc, TABP_PANE, 0, &rect, NULL);
2318   else
2319       DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2320 }
2321
2322 /******************************************************************************
2323  * TAB_Refresh
2324  *
2325  * This method repaints the tab control..
2326  */
2327 static void TAB_Refresh (TAB_INFO *infoPtr, HDC hdc)
2328 {
2329   HFONT hOldFont;
2330   INT i;
2331
2332   if (!infoPtr->DoRedraw)
2333     return;
2334
2335   hOldFont = SelectObject (hdc, infoPtr->hFont);
2336
2337   if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS)
2338   {
2339     for (i = 0; i < infoPtr->uNumItem; i++)
2340       TAB_DrawItem (infoPtr, hdc, i);
2341   }
2342   else
2343   {
2344     /* Draw all the non selected item first */
2345     for (i = 0; i < infoPtr->uNumItem; i++)
2346     {
2347       if (i != infoPtr->iSelected)
2348         TAB_DrawItem (infoPtr, hdc, i);
2349     }
2350
2351     /* Now, draw the border, draw it before the selected item
2352      * since the selected item overwrites part of the border. */
2353     TAB_DrawBorder (infoPtr, hdc);
2354
2355     /* Then, draw the selected item */
2356     TAB_DrawItem (infoPtr, hdc, infoPtr->iSelected);
2357
2358     /* If we haven't set the current focus yet, set it now.
2359      * Only happens when we first paint the tab controls */
2360     if (infoPtr->uFocus == -1)
2361       TAB_SetCurFocus(infoPtr, infoPtr->iSelected);
2362   }
2363
2364   SelectObject (hdc, hOldFont);
2365 }
2366
2367 static inline DWORD TAB_GetRowCount (const TAB_INFO *infoPtr)
2368 {
2369   return infoPtr->uNumRows;
2370 }
2371
2372 static inline LRESULT TAB_SetRedraw (TAB_INFO *infoPtr, BOOL doRedraw)
2373 {
2374   infoPtr->DoRedraw = doRedraw;
2375   return 0;
2376 }
2377
2378 /******************************************************************************
2379  * TAB_EnsureSelectionVisible
2380  *
2381  * This method will make sure that the current selection is completely
2382  * visible by scrolling until it is.
2383  */
2384 static void TAB_EnsureSelectionVisible(
2385   TAB_INFO* infoPtr)
2386 {
2387   INT iSelected = infoPtr->iSelected;
2388   LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2389   INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2390
2391   /* set the items row to the bottommost row or topmost row depending on
2392    * style */
2393   if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2394   {
2395       TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2396       INT newselected;
2397       INT iTargetRow;
2398
2399       if(lStyle & TCS_VERTICAL)
2400         newselected = selected->rect.left;
2401       else
2402         newselected = selected->rect.top;
2403
2404       /* the target row is always (number of rows - 1)
2405          as row 0 is furthest from the clientRect */
2406       iTargetRow = infoPtr->uNumRows - 1;
2407
2408       if (newselected != iTargetRow)
2409       {
2410          UINT i;
2411          if(lStyle & TCS_VERTICAL)
2412          {
2413            for (i=0; i < infoPtr->uNumItem; i++)
2414            {
2415              /* move everything in the row of the selected item to the iTargetRow */
2416              TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2417
2418              if (item->rect.left == newselected )
2419                  item->rect.left = iTargetRow;
2420              else
2421              {
2422                if (item->rect.left > newselected)
2423                  item->rect.left-=1;
2424              }
2425            }
2426          }
2427          else
2428          {
2429            for (i=0; i < infoPtr->uNumItem; i++)
2430            {
2431              TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2432
2433              if (item->rect.top == newselected )
2434                  item->rect.top = iTargetRow;
2435              else
2436              {
2437                if (item->rect.top > newselected)
2438                  item->rect.top-=1;
2439              }
2440           }
2441         }
2442         TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2443       }
2444   }
2445
2446   /*
2447    * Do the trivial cases first.
2448    */
2449   if ( (!infoPtr->needsScrolling) ||
2450        (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2451     return;
2452
2453   if (infoPtr->leftmostVisible >= iSelected)
2454   {
2455     infoPtr->leftmostVisible = iSelected;
2456   }
2457   else
2458   {
2459      TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2460      RECT r;
2461      INT width;
2462      UINT i;
2463
2464      /* Calculate the part of the client area that is visible */
2465      GetClientRect(infoPtr->hwnd, &r);
2466      width = r.right;
2467
2468      GetClientRect(infoPtr->hwndUpDown, &r);
2469      width -= r.right;
2470
2471      if ((selected->rect.right -
2472           selected->rect.left) >= width )
2473      {
2474         /* Special case: width of selected item is greater than visible
2475          * part of control.
2476          */
2477         infoPtr->leftmostVisible = iSelected;
2478      }
2479      else
2480      {
2481         for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2482         {
2483            if ((selected->rect.right - TAB_GetItem(infoPtr, i)->rect.left) < width)
2484               break;
2485         }
2486         infoPtr->leftmostVisible = i;
2487      }
2488   }
2489
2490   if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2491     TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2492
2493   SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2494                MAKELONG(infoPtr->leftmostVisible, 0));
2495 }
2496
2497 /******************************************************************************
2498  * TAB_InvalidateTabArea
2499  *
2500  * This method will invalidate the portion of the control that contains the
2501  * tabs. It is called when the state of the control changes and needs
2502  * to be redisplayed
2503  */
2504 static void TAB_InvalidateTabArea(TAB_INFO* infoPtr)
2505 {
2506   RECT clientRect, rInvalidate, rAdjClient;
2507   DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2508   INT lastRow = infoPtr->uNumRows - 1;
2509   RECT rect;
2510
2511   if (lastRow < 0) return;
2512
2513   GetClientRect(infoPtr->hwnd, &clientRect);
2514   rInvalidate = clientRect;
2515   rAdjClient = clientRect;
2516
2517   TAB_AdjustRect(infoPtr, 0, &rAdjClient);
2518
2519   TAB_InternalGetItemRect(infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2520   if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2521   {
2522     rInvalidate.left = rAdjClient.right;
2523     if (infoPtr->uNumRows == 1)
2524       rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2525   }
2526   else if(lStyle & TCS_VERTICAL)
2527   {
2528     rInvalidate.right = rAdjClient.left;
2529     if (infoPtr->uNumRows == 1)
2530       rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2531   }
2532   else if (lStyle & TCS_BOTTOM)
2533   {
2534     rInvalidate.top = rAdjClient.bottom;
2535     if (infoPtr->uNumRows == 1)
2536       rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2537   }
2538   else 
2539   {
2540     rInvalidate.bottom = rAdjClient.top;
2541     if (infoPtr->uNumRows == 1)
2542       rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2543   }
2544   
2545   /* Punch out the updown control */
2546   if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2547     RECT r;
2548     GetClientRect(infoPtr->hwndUpDown, &r);
2549     if (rInvalidate.right > clientRect.right - r.left)
2550       rInvalidate.right = rInvalidate.right - (r.right - r.left);
2551     else
2552       rInvalidate.right = clientRect.right - r.left;
2553   }
2554   
2555   TRACE("invalidate (%d,%d)-(%d,%d)\n",
2556         rInvalidate.left,  rInvalidate.top,
2557         rInvalidate.right, rInvalidate.bottom);
2558  
2559   InvalidateRect(infoPtr->hwnd, &rInvalidate, TRUE);
2560 }
2561
2562 static inline LRESULT TAB_Paint (TAB_INFO *infoPtr, HDC hdcPaint)
2563 {
2564   HDC hdc;
2565   PAINTSTRUCT ps;
2566
2567   if (hdcPaint)
2568     hdc = hdcPaint;
2569   else
2570   {
2571     hdc = BeginPaint (infoPtr->hwnd, &ps);
2572     TRACE("erase %d, rect=(%d,%d)-(%d,%d)\n",
2573          ps.fErase,
2574          ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2575   }
2576
2577   TAB_Refresh (infoPtr, hdc);
2578
2579   if (!hdcPaint)
2580     EndPaint (infoPtr->hwnd, &ps);
2581
2582   return 0;
2583 }
2584
2585 static LRESULT
2586 TAB_InsertItemT (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2587 {
2588   TAB_ITEM *item;
2589   TCITEMW *pti;
2590   INT iItem;
2591   RECT rect;
2592
2593   GetClientRect (infoPtr->hwnd, &rect);
2594   TRACE("Rect: %p T %i, L %i, B %i, R %i\n", infoPtr->hwnd,
2595         rect.top, rect.left, rect.bottom, rect.right);
2596
2597   pti = (TCITEMW *)lParam;
2598   iItem = (INT)wParam;
2599
2600   if (iItem < 0) return -1;
2601   if (iItem > infoPtr->uNumItem)
2602     iItem = infoPtr->uNumItem;
2603
2604   TAB_DumpItemExternalT(pti, iItem, bUnicode);
2605
2606
2607   if (infoPtr->uNumItem == 0) {
2608     infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2609     infoPtr->uNumItem++;
2610     infoPtr->iSelected = 0;
2611   }
2612   else {
2613     LPBYTE oldItems = (LPBYTE)infoPtr->items;
2614
2615     infoPtr->uNumItem++;
2616     infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2617
2618     /* pre insert copy */
2619     if (iItem > 0) {
2620       memcpy (infoPtr->items, oldItems,
2621               iItem * TAB_ITEM_SIZE(infoPtr));
2622     }
2623
2624     /* post insert copy */
2625     if (iItem < infoPtr->uNumItem - 1) {
2626       memcpy (TAB_GetItem(infoPtr, iItem + 1),
2627               oldItems + iItem * TAB_ITEM_SIZE(infoPtr),
2628               (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2629
2630     }
2631
2632     if (iItem <= infoPtr->iSelected)
2633       infoPtr->iSelected++;
2634
2635     Free (oldItems);
2636   }
2637
2638   item = TAB_GetItem(infoPtr, iItem);
2639
2640   item->pszText = NULL;
2641
2642   if (pti->mask & TCIF_TEXT)
2643   {
2644     if (bUnicode)
2645       Str_SetPtrW (&item->pszText, pti->pszText);
2646     else
2647       Str_SetPtrAtoW (&item->pszText, (LPSTR)pti->pszText);
2648   }
2649
2650   if (pti->mask & TCIF_IMAGE)
2651     item->iImage = pti->iImage;
2652   else
2653     item->iImage = -1;
2654
2655   if (pti->mask & TCIF_PARAM)
2656     memcpy(item->extra, &pti->lParam, infoPtr->cbInfo);
2657   else
2658     memset(item->extra, 0, infoPtr->cbInfo);
2659   
2660   TAB_SetItemBounds(infoPtr);
2661   if (infoPtr->uNumItem > 1)
2662     TAB_InvalidateTabArea(infoPtr);
2663   else
2664     InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2665
2666   TRACE("[%p]: added item %d %s\n",
2667         infoPtr->hwnd, iItem, debugstr_w(item->pszText));
2668
2669   return iItem;
2670 }
2671
2672 static LRESULT
2673 TAB_SetItemSize (TAB_INFO *infoPtr, LPARAM lParam)
2674 {
2675   LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2676   LONG lResult = 0;
2677   BOOL bNeedPaint = FALSE;
2678
2679   lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2680
2681   /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2682   if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2683   {
2684     infoPtr->tabWidth = (INT)LOWORD(lParam);
2685     bNeedPaint = TRUE;
2686   }
2687
2688   if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2689   {
2690     if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2691       infoPtr->tabHeight = (INT)HIWORD(lParam);
2692
2693     bNeedPaint = TRUE;
2694   }
2695   TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2696        HIWORD(lResult), LOWORD(lResult),
2697        infoPtr->tabHeight, infoPtr->tabWidth);
2698
2699   if (bNeedPaint)
2700   {
2701     TAB_SetItemBounds(infoPtr);
2702     RedrawWindow(infoPtr->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2703   }
2704
2705   return lResult;
2706 }
2707
2708 static inline LRESULT TAB_SetMinTabWidth (TAB_INFO *infoPtr, INT cx)
2709 {
2710   INT oldcx = 0;
2711
2712   TRACE("(%p,%d)\n", infoPtr, cx);
2713
2714   if (infoPtr) {
2715     oldcx = infoPtr->tabMinWidth;
2716     infoPtr->tabMinWidth = cx;
2717   }
2718   TAB_SetItemBounds(infoPtr);
2719
2720   return oldcx;
2721 }
2722
2723 static inline LRESULT 
2724 TAB_HighlightItem (TAB_INFO *infoPtr, INT iItem, BOOL fHighlight)
2725 {
2726   LPDWORD lpState;
2727
2728   TRACE("(%p,%d,%s)\n", infoPtr, iItem, fHighlight ? "true" : "false");
2729
2730   if (!infoPtr || iItem < 0 || iItem >= infoPtr->uNumItem)
2731     return FALSE;
2732   
2733   lpState = &TAB_GetItem(infoPtr, iItem)->dwState;
2734
2735   if (fHighlight)
2736     *lpState |= TCIS_HIGHLIGHTED;
2737   else
2738     *lpState &= ~TCIS_HIGHLIGHTED;
2739
2740   return TRUE;
2741 }
2742
2743 static LRESULT
2744 TAB_SetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2745 {
2746   TAB_ITEM *wineItem;
2747
2748   TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2749
2750   if (iItem < 0 || iItem >= infoPtr->uNumItem)
2751     return FALSE;
2752
2753   TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2754
2755   wineItem = TAB_GetItem(infoPtr, iItem);
2756
2757   if (tabItem->mask & TCIF_IMAGE)
2758     wineItem->iImage = tabItem->iImage;
2759
2760   if (tabItem->mask & TCIF_PARAM)
2761     memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2762
2763   if (tabItem->mask & TCIF_RTLREADING)
2764     FIXME("TCIF_RTLREADING\n");
2765
2766   if (tabItem->mask & TCIF_STATE)
2767     wineItem->dwState = tabItem->dwState;
2768
2769   if (tabItem->mask & TCIF_TEXT)
2770   {
2771     if (wineItem->pszText)
2772     {
2773       Free(wineItem->pszText);
2774       wineItem->pszText = NULL;
2775     }
2776     if (bUnicode)
2777       Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2778     else
2779       Str_SetPtrAtoW(&wineItem->pszText, (LPSTR)tabItem->pszText);
2780   }
2781
2782   /* Update and repaint tabs */
2783   TAB_SetItemBounds(infoPtr);
2784   TAB_InvalidateTabArea(infoPtr);
2785
2786   return TRUE;
2787 }
2788
2789 static inline LRESULT TAB_GetItemCount (const TAB_INFO *infoPtr)
2790 {
2791    return infoPtr->uNumItem;
2792 }
2793
2794
2795 static LRESULT
2796 TAB_GetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2797 {
2798   TAB_ITEM *wineItem;
2799
2800   TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2801
2802   if (iItem < 0 || iItem >= infoPtr->uNumItem)
2803     return FALSE;
2804
2805   wineItem = TAB_GetItem(infoPtr, iItem);
2806
2807   if (tabItem->mask & TCIF_IMAGE)
2808     tabItem->iImage = wineItem->iImage;
2809
2810   if (tabItem->mask & TCIF_PARAM)
2811     memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2812
2813   if (tabItem->mask & TCIF_RTLREADING)
2814     FIXME("TCIF_RTLREADING\n");
2815
2816   if (tabItem->mask & TCIF_STATE)
2817     tabItem->dwState = wineItem->dwState;
2818
2819   if (tabItem->mask & TCIF_TEXT)
2820   {
2821     if (bUnicode)
2822       Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2823     else
2824       Str_GetPtrWtoA (wineItem->pszText, (LPSTR)tabItem->pszText, tabItem->cchTextMax);
2825   }
2826
2827   TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2828
2829   return TRUE;
2830 }
2831
2832
2833 static LRESULT TAB_DeleteItem (TAB_INFO *infoPtr, INT iItem)
2834 {
2835     BOOL bResult = FALSE;
2836
2837     TRACE("(%p, %d)\n", infoPtr, iItem);
2838
2839     if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2840     {
2841         TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
2842         LPBYTE oldItems = (LPBYTE)infoPtr->items;
2843
2844         TAB_InvalidateTabArea(infoPtr);
2845         Free(item->pszText);
2846         infoPtr->uNumItem--;
2847
2848         if (!infoPtr->uNumItem)
2849         {
2850             infoPtr->items = NULL;
2851             if (infoPtr->iHotTracked >= 0)
2852             {
2853                 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
2854                 infoPtr->iHotTracked = -1;
2855             }
2856         }
2857         else
2858         {
2859             infoPtr->items = Alloc(TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2860
2861             if (iItem > 0)
2862                 memcpy(infoPtr->items, oldItems, iItem * TAB_ITEM_SIZE(infoPtr));
2863
2864             if (iItem < infoPtr->uNumItem)
2865                 memcpy(TAB_GetItem(infoPtr, iItem),
2866                        oldItems + (iItem + 1) * TAB_ITEM_SIZE(infoPtr),
2867                        (infoPtr->uNumItem - iItem) * TAB_ITEM_SIZE(infoPtr));
2868
2869             if (iItem <= infoPtr->iHotTracked)
2870             {
2871                 /* When tabs move left/up, the hot track item may change */
2872                 FIXME("Recalc hot track\n");
2873             }
2874         }
2875         Free(oldItems);
2876
2877         /* Readjust the selected index */
2878         if ((iItem == infoPtr->iSelected) && (iItem > 0))
2879             infoPtr->iSelected--;
2880
2881         if (iItem < infoPtr->iSelected)
2882             infoPtr->iSelected--;
2883
2884         if (infoPtr->uNumItem == 0)
2885             infoPtr->iSelected = -1;
2886
2887         /* Reposition and repaint tabs */
2888         TAB_SetItemBounds(infoPtr);
2889
2890         bResult = TRUE;
2891     }
2892
2893     return bResult;
2894 }
2895
2896 static inline LRESULT TAB_DeleteAllItems (TAB_INFO *infoPtr)
2897 {
2898     TRACE("(%p)\n", infoPtr);
2899     while (infoPtr->uNumItem)
2900       TAB_DeleteItem (infoPtr, 0);
2901     return TRUE;
2902 }
2903
2904
2905 static inline LRESULT TAB_GetFont (const TAB_INFO *infoPtr)
2906 {
2907   TRACE("(%p) returning %p\n", infoPtr, infoPtr->hFont);
2908   return (LRESULT)infoPtr->hFont;
2909 }
2910
2911 static inline LRESULT TAB_SetFont (TAB_INFO *infoPtr, HFONT hNewFont)
2912 {
2913   TRACE("(%p,%p)\n", infoPtr, hNewFont);
2914
2915   infoPtr->hFont = hNewFont;
2916
2917   TAB_SetItemBounds(infoPtr);
2918
2919   TAB_InvalidateTabArea(infoPtr);
2920
2921   return 0;
2922 }
2923
2924
2925 static inline LRESULT TAB_GetImageList (const TAB_INFO *infoPtr)
2926 {
2927   TRACE("\n");
2928   return (LRESULT)infoPtr->himl;
2929 }
2930
2931 static inline LRESULT TAB_SetImageList (TAB_INFO *infoPtr, HIMAGELIST himlNew)
2932 {
2933     HIMAGELIST himlPrev = infoPtr->himl;
2934     TRACE("\n");
2935     infoPtr->himl = himlNew;
2936     return (LRESULT)himlPrev;
2937 }
2938
2939 static inline LRESULT TAB_GetUnicodeFormat (const TAB_INFO *infoPtr)
2940 {
2941     return infoPtr->bUnicode;
2942 }
2943
2944 static inline LRESULT TAB_SetUnicodeFormat (TAB_INFO *infoPtr, BOOL bUnicode)
2945 {
2946     BOOL bTemp = infoPtr->bUnicode;
2947
2948     infoPtr->bUnicode = bUnicode;
2949
2950     return bTemp;
2951 }
2952
2953 static inline LRESULT TAB_Size (TAB_INFO *infoPtr)
2954 {
2955 /* I'm not really sure what the following code was meant to do.
2956    This is what it is doing:
2957    When WM_SIZE is sent with SIZE_RESTORED, the control
2958    gets positioned in the top left corner.
2959
2960   RECT parent_rect;
2961   HWND parent;
2962   UINT uPosFlags,cx,cy;
2963
2964   uPosFlags=0;
2965   if (!wParam) {
2966     parent = GetParent (hwnd);
2967     GetClientRect(parent, &parent_rect);
2968     cx=LOWORD (lParam);
2969     cy=HIWORD (lParam);
2970     if (GetWindowLongW(hwnd, GWL_STYLE) & CCS_NORESIZE)
2971         uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2972
2973     SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2974             cx, cy, uPosFlags | SWP_NOZORDER);
2975   } else {
2976     FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2977   } */
2978
2979   /* Recompute the size/position of the tabs. */
2980   TAB_SetItemBounds (infoPtr);
2981
2982   /* Force a repaint of the control. */
2983   InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2984
2985   return 0;
2986 }
2987
2988
2989 static LRESULT TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2990 {
2991   TAB_INFO *infoPtr;
2992   TEXTMETRICW fontMetrics;
2993   HDC hdc;
2994   HFONT hOldFont;
2995   DWORD dwStyle;
2996
2997   infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
2998
2999   SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
3000
3001   infoPtr->hwnd            = hwnd;
3002   infoPtr->hwndNotify      = ((LPCREATESTRUCTW)lParam)->hwndParent;
3003   infoPtr->uNumItem        = 0;
3004   infoPtr->uNumRows        = 0;
3005   infoPtr->uHItemPadding   = 6;
3006   infoPtr->uVItemPadding   = 3;
3007   infoPtr->uHItemPadding_s = 6;
3008   infoPtr->uVItemPadding_s = 3;
3009   infoPtr->hFont           = 0;
3010   infoPtr->items           = 0;
3011   infoPtr->hcurArrow       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3012   infoPtr->iSelected       = -1;
3013   infoPtr->iHotTracked     = -1;
3014   infoPtr->uFocus          = -1;
3015   infoPtr->hwndToolTip     = 0;
3016   infoPtr->DoRedraw        = TRUE;
3017   infoPtr->needsScrolling  = FALSE;
3018   infoPtr->hwndUpDown      = 0;
3019   infoPtr->leftmostVisible = 0;
3020   infoPtr->fHeightSet      = FALSE;
3021   infoPtr->bUnicode        = IsWindowUnicode (hwnd);
3022   infoPtr->cbInfo          = sizeof(LPARAM);
3023
3024   TRACE("Created tab control, hwnd [%p]\n", hwnd);
3025
3026   /* The tab control always has the WS_CLIPSIBLINGS style. Even
3027      if you don't specify it in CreateWindow. This is necessary in
3028      order for paint to work correctly. This follows windows behaviour. */
3029   dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
3030   SetWindowLongW(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
3031
3032   if (dwStyle & TCS_TOOLTIPS) {
3033     /* Create tooltip control */
3034     infoPtr->hwndToolTip =
3035       CreateWindowExW (0, TOOLTIPS_CLASSW, NULL, WS_POPUP,
3036                        CW_USEDEFAULT, CW_USEDEFAULT,
3037                        CW_USEDEFAULT, CW_USEDEFAULT,
3038                        hwnd, 0, 0, 0);
3039
3040     /* Send NM_TOOLTIPSCREATED notification */
3041     if (infoPtr->hwndToolTip) {
3042       NMTOOLTIPSCREATED nmttc;
3043
3044       nmttc.hdr.hwndFrom = hwnd;
3045       nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
3046       nmttc.hdr.code = NM_TOOLTIPSCREATED;
3047       nmttc.hwndToolTips = infoPtr->hwndToolTip;
3048
3049       SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
3050                     (WPARAM)GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc);
3051     }
3052   }
3053
3054   OpenThemeData (infoPtr->hwnd, themeClass);
3055   
3056   /*
3057    * We need to get text information so we need a DC and we need to select
3058    * a font.
3059    */
3060   hdc = GetDC(hwnd);
3061   hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3062
3063   /* Use the system font to determine the initial height of a tab. */
3064   GetTextMetricsW(hdc, &fontMetrics);
3065
3066   /*
3067    * Make sure there is enough space for the letters + growing the
3068    * selected item + extra space for the selected item.
3069    */
3070   infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3071                        ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
3072                         infoPtr->uVItemPadding;
3073
3074   /* Initialize the width of a tab. */
3075   if (dwStyle & TCS_FIXEDWIDTH)
3076     infoPtr->tabWidth = DEFAULT_TAB_WIDTH_FIXED;
3077
3078   infoPtr->tabMinWidth = -1;
3079
3080   TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3081
3082   SelectObject (hdc, hOldFont);
3083   ReleaseDC(hwnd, hdc);
3084
3085   return 0;
3086 }
3087
3088 static LRESULT
3089 TAB_Destroy (TAB_INFO *infoPtr)
3090 {
3091   UINT iItem;
3092
3093   if (!infoPtr)
3094       return 0;
3095
3096   SetWindowLongPtrW(infoPtr->hwnd, 0, 0);
3097
3098   if (infoPtr->items) {
3099     for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3100       if (TAB_GetItem(infoPtr, iItem)->pszText)
3101         Free (TAB_GetItem(infoPtr, iItem)->pszText);
3102     }
3103     Free (infoPtr->items);
3104   }
3105
3106   if (infoPtr->hwndToolTip)
3107     DestroyWindow (infoPtr->hwndToolTip);
3108
3109   if (infoPtr->hwndUpDown)
3110     DestroyWindow(infoPtr->hwndUpDown);
3111
3112   if (infoPtr->iHotTracked >= 0)
3113     KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
3114
3115   CloseThemeData (GetWindowTheme (infoPtr->hwnd));
3116   
3117   Free (infoPtr);
3118   return 0;
3119 }
3120
3121 /* update theme after a WM_THEMECHANGED message */
3122 static LRESULT theme_changed (TAB_INFO* infoPtr)
3123 {
3124     HTHEME theme = GetWindowTheme (infoPtr->hwnd);
3125     CloseThemeData (theme);
3126     OpenThemeData (infoPtr->hwnd, themeClass);
3127     return 0;
3128 }
3129
3130 static LRESULT TAB_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
3131 {
3132   if (!wParam)
3133     return 0;
3134   return WVR_ALIGNTOP;
3135 }
3136
3137 static inline LRESULT
3138 TAB_SetItemExtra (TAB_INFO *infoPtr, INT cbInfo)
3139 {
3140   if (!infoPtr || cbInfo <= 0)
3141     return FALSE;
3142
3143   if (infoPtr->uNumItem)
3144   {
3145     /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
3146     return FALSE;
3147   }
3148     
3149   infoPtr->cbInfo = cbInfo;
3150   return TRUE;
3151 }
3152
3153 static LRESULT WINAPI
3154 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3155 {
3156     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3157
3158     TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3159     if (!infoPtr && (uMsg != WM_CREATE))
3160       return DefWindowProcW (hwnd, uMsg, wParam, lParam);
3161
3162     switch (uMsg)
3163     {
3164     case TCM_GETIMAGELIST:
3165       return TAB_GetImageList (infoPtr);
3166
3167     case TCM_SETIMAGELIST:
3168       return TAB_SetImageList (infoPtr, (HIMAGELIST)lParam);
3169
3170     case TCM_GETITEMCOUNT:
3171       return TAB_GetItemCount (infoPtr);
3172
3173     case TCM_GETITEMA:
3174     case TCM_GETITEMW:
3175       return TAB_GetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_GETITEMW);
3176
3177     case TCM_SETITEMA:
3178     case TCM_SETITEMW:
3179       return TAB_SetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_SETITEMW);
3180
3181     case TCM_DELETEITEM:
3182       return TAB_DeleteItem (infoPtr, (INT)wParam);
3183
3184     case TCM_DELETEALLITEMS:
3185      return TAB_DeleteAllItems (infoPtr);
3186
3187     case TCM_GETITEMRECT:
3188      return TAB_GetItemRect (infoPtr, wParam, lParam);
3189
3190     case TCM_GETCURSEL:
3191       return TAB_GetCurSel (infoPtr);
3192
3193     case TCM_HITTEST:
3194       return TAB_HitTest (infoPtr, (LPTCHITTESTINFO)lParam);
3195
3196     case TCM_SETCURSEL:
3197       return TAB_SetCurSel (infoPtr, (INT)wParam);
3198
3199     case TCM_INSERTITEMA:
3200     case TCM_INSERTITEMW:
3201       return TAB_InsertItemT (infoPtr, wParam, lParam, uMsg == TCM_INSERTITEMW);
3202
3203     case TCM_SETITEMEXTRA:
3204       return TAB_SetItemExtra (infoPtr, (int)wParam);
3205
3206     case TCM_ADJUSTRECT:
3207       return TAB_AdjustRect (infoPtr, (BOOL)wParam, (LPRECT)lParam);
3208
3209     case TCM_SETITEMSIZE:
3210       return TAB_SetItemSize (infoPtr, lParam);
3211
3212     case TCM_REMOVEIMAGE:
3213       FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3214       return 0;
3215
3216     case TCM_SETPADDING:
3217       return TAB_SetPadding (infoPtr, lParam);
3218
3219     case TCM_GETROWCOUNT:
3220       return TAB_GetRowCount(infoPtr);
3221
3222     case TCM_GETUNICODEFORMAT:
3223       return TAB_GetUnicodeFormat (infoPtr);
3224
3225     case TCM_SETUNICODEFORMAT:
3226       return TAB_SetUnicodeFormat (infoPtr, (BOOL)wParam);
3227
3228     case TCM_HIGHLIGHTITEM:
3229       return TAB_HighlightItem (infoPtr, (INT)wParam, (BOOL)LOWORD(lParam));
3230
3231     case TCM_GETTOOLTIPS:
3232       return TAB_GetToolTips (infoPtr);
3233
3234     case TCM_SETTOOLTIPS:
3235       return TAB_SetToolTips (infoPtr, (HWND)wParam);
3236
3237     case TCM_GETCURFOCUS:
3238       return TAB_GetCurFocus (infoPtr);
3239
3240     case TCM_SETCURFOCUS:
3241       return TAB_SetCurFocus (infoPtr, (INT)wParam);
3242
3243     case TCM_SETMINTABWIDTH:
3244       return TAB_SetMinTabWidth(infoPtr, (INT)lParam);
3245
3246     case TCM_DESELECTALL:
3247       FIXME("Unimplemented msg TCM_DESELECTALL\n");
3248       return 0;
3249
3250     case TCM_GETEXTENDEDSTYLE:
3251       FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3252       return 0;
3253
3254     case TCM_SETEXTENDEDSTYLE:
3255       FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3256       return 0;
3257
3258     case WM_GETFONT:
3259       return TAB_GetFont (infoPtr);
3260
3261     case WM_SETFONT:
3262       return TAB_SetFont (infoPtr, (HFONT)wParam);
3263
3264     case WM_CREATE:
3265       return TAB_Create (hwnd, wParam, lParam);
3266
3267     case WM_NCDESTROY:
3268       return TAB_Destroy (infoPtr);
3269
3270     case WM_GETDLGCODE:
3271       return DLGC_WANTARROWS | DLGC_WANTCHARS;
3272
3273     case WM_LBUTTONDOWN:
3274       return TAB_LButtonDown (infoPtr, wParam, lParam);
3275
3276     case WM_LBUTTONUP:
3277       return TAB_LButtonUp (infoPtr);
3278
3279     case WM_NOTIFY:
3280       return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3281
3282     case WM_RBUTTONDOWN:
3283       return TAB_RButtonDown (infoPtr);
3284
3285     case WM_MOUSEMOVE:
3286       return TAB_MouseMove (infoPtr, wParam, lParam);
3287
3288     case WM_PRINTCLIENT:
3289     case WM_PAINT:
3290       return TAB_Paint (infoPtr, (HDC)wParam);
3291
3292     case WM_SIZE:
3293       return TAB_Size (infoPtr);
3294
3295     case WM_SETREDRAW:
3296       return TAB_SetRedraw (infoPtr, (BOOL)wParam);
3297
3298     case WM_HSCROLL:
3299       return TAB_OnHScroll(infoPtr, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3300
3301     case WM_STYLECHANGED:
3302       TAB_SetItemBounds (infoPtr);
3303       InvalidateRect(hwnd, NULL, TRUE);
3304       return 0;
3305
3306     case WM_SYSCOLORCHANGE:
3307       COMCTL32_RefreshSysColors();
3308       return 0;
3309
3310     case WM_THEMECHANGED:
3311       return theme_changed (infoPtr);
3312
3313     case WM_KILLFOCUS:
3314     case WM_SETFOCUS:
3315       TAB_FocusChanging(infoPtr);
3316       break;   /* Don't disturb normal focus behavior */
3317
3318     case WM_KEYUP:
3319       return TAB_KeyUp(infoPtr, wParam);
3320     case WM_NCHITTEST:
3321       return TAB_NCHitTest(infoPtr, lParam);
3322
3323     case WM_NCCALCSIZE:
3324       return TAB_NCCalcSize(hwnd, wParam, lParam);
3325
3326     default:
3327       if (uMsg >= WM_USER && uMsg < WM_APP)
3328         WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3329              uMsg, wParam, lParam);
3330       break;
3331     }
3332     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
3333 }
3334
3335
3336 void
3337 TAB_Register (void)
3338 {
3339   WNDCLASSW wndClass;
3340
3341   ZeroMemory (&wndClass, sizeof(WNDCLASSW));
3342   wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3343   wndClass.lpfnWndProc   = TAB_WindowProc;
3344   wndClass.cbClsExtra    = 0;
3345   wndClass.cbWndExtra    = sizeof(TAB_INFO *);
3346   wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3347   wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
3348   wndClass.lpszClassName = WC_TABCONTROLW;
3349
3350   RegisterClassW (&wndClass);
3351 }
3352
3353
3354 void
3355 TAB_Unregister (void)
3356 {
3357     UnregisterClassW (WC_TABCONTROLW, NULL);
3358 }