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