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