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