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