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