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