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