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