The "colors" parameter of SetDIBColorTable should be CONST.
[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 = 0;
1663     memcpy( &dis.itemData, TAB_GetItem(infoPtr, iItem)->extra, min(sizeof(dis.itemData),infoPtr->cbInfo) );
1664
1665     /*
1666      * send the draw message
1667      */
1668     SendMessageW( infoPtr->hwndNotify, WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1669   }
1670   else
1671   {
1672     TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
1673     RECT rcTemp;
1674     RECT rcImage;
1675
1676     /* used to center the icon and text in the tab */
1677     RECT rcText;
1678     INT center_offset_h, center_offset_v;
1679
1680     /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1681     rcImage = *drawRect;
1682
1683     rcTemp = *drawRect;
1684
1685     rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1686
1687     /* get the rectangle that the text fits in */
1688     if (item->pszText)
1689     {
1690       DrawTextW(hdc, item->pszText, -1, &rcText, DT_CALCRECT);
1691     }
1692     /*
1693      * If not owner draw, then do the drawing ourselves.
1694      *
1695      * Draw the icon.
1696      */
1697     if (infoPtr->himl && (item->mask & TCIF_IMAGE))
1698     {
1699       INT cx;
1700       INT cy;
1701       
1702       ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1703
1704       if(lStyle & TCS_VERTICAL)
1705       {
1706         center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right  - rcText.left))) / 2;
1707         center_offset_v = ((drawRect->right - drawRect->left) - (cx + infoPtr->uVItemPadding)) / 2;
1708       }
1709       else
1710       {
1711         center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right  - rcText.left))) / 2;
1712         center_offset_v = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uVItemPadding)) / 2;
1713       }
1714
1715       if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1716         center_offset_h = infoPtr->uHItemPadding;
1717
1718       if (center_offset_h < 2)
1719         center_offset_h = 2;
1720         
1721       if (center_offset_v < 0)
1722         center_offset_v = 0;
1723         
1724       TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1725           debugstr_w(item->pszText), center_offset_h, center_offset_v,
1726           drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1727           (rcText.right-rcText.left));
1728
1729       if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1730       {
1731         rcImage.top = drawRect->top + center_offset_h;
1732         /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1733         /* right side of the tab, but the image still uses the left as its x position */
1734         /* this keeps the image always drawn off of the same side of the tab */
1735         rcImage.left = drawRect->right - cx - center_offset_v;
1736         drawRect->top += cy + infoPtr->uHItemPadding;
1737       }
1738       else if(lStyle & TCS_VERTICAL)
1739       {
1740         rcImage.top  = drawRect->bottom - cy - center_offset_h;
1741         rcImage.left = drawRect->left + center_offset_v;
1742         drawRect->bottom -= cy + infoPtr->uHItemPadding;
1743       }
1744       else /* normal style, whether TCS_BOTTOM or not */
1745       {
1746         rcImage.left = drawRect->left + center_offset_h;
1747         rcImage.top = drawRect->top + center_offset_v;
1748         drawRect->left += cx + infoPtr->uHItemPadding;
1749       }
1750
1751       TRACE("drawing image=%d, left=%ld, top=%ld\n",
1752             item->iImage, rcImage.left, rcImage.top-1);
1753       ImageList_Draw
1754         (
1755         infoPtr->himl,
1756         item->iImage,
1757         hdc,
1758         rcImage.left,
1759         rcImage.top,
1760         ILD_NORMAL
1761         );
1762     }
1763
1764     /* Now position text */
1765     if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT)
1766       center_offset_h = infoPtr->uHItemPadding;
1767     else
1768       if(lStyle & TCS_VERTICAL)
1769         center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1770       else
1771         center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1772
1773     if(lStyle & TCS_VERTICAL)
1774     {
1775       if(lStyle & TCS_BOTTOM)
1776         drawRect->top+=center_offset_h;
1777       else
1778         drawRect->bottom-=center_offset_h;
1779
1780       center_offset_v = ((drawRect->right - drawRect->left) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1781     }
1782     else
1783     {
1784       drawRect->left += center_offset_h;
1785       center_offset_v = ((drawRect->bottom - drawRect->top) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1786     }
1787
1788     if (center_offset_v < 0)
1789       center_offset_v = 0;
1790
1791     if(lStyle & TCS_VERTICAL)
1792       drawRect->left += center_offset_v;
1793     else
1794       drawRect->top += center_offset_v;
1795
1796     /* Draw the text */
1797     if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1798     {
1799       LOGFONTA logfont;
1800       HFONT hFont = 0;
1801       INT nEscapement = 900;
1802       INT nOrientation = 900;
1803
1804       if(lStyle & TCS_BOTTOM)
1805       {
1806         nEscapement = -900;
1807         nOrientation = -900;
1808       }
1809
1810       /* to get a font with the escapement and orientation we are looking for, we need to */
1811       /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1812       if (!GetObjectA((infoPtr->hFont) ?
1813                 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1814                 sizeof(LOGFONTA),&logfont))
1815       {
1816         INT iPointSize = 9;
1817
1818         lstrcpyA(logfont.lfFaceName, "Arial");
1819         logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1820                                     72);
1821         logfont.lfWeight = FW_NORMAL;
1822         logfont.lfItalic = 0;
1823         logfont.lfUnderline = 0;
1824         logfont.lfStrikeOut = 0;
1825       }
1826
1827       logfont.lfEscapement = nEscapement;
1828       logfont.lfOrientation = nOrientation;
1829       hFont = CreateFontIndirectA(&logfont);
1830       SelectObject(hdc, hFont);
1831
1832       if (item->pszText)
1833       {
1834         ExtTextOutW(hdc,
1835         (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1836         (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1837         ETO_CLIPPED,
1838         drawRect,
1839         item->pszText,
1840         lstrlenW(item->pszText),
1841         0);
1842       }
1843
1844       DeleteObject(hFont);
1845     }
1846     else
1847     {
1848       TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1849           debugstr_w(item->pszText), center_offset_h, center_offset_v,
1850           drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1851           (rcText.right-rcText.left));
1852       if (item->pszText)
1853       {
1854         DrawTextW
1855         (
1856           hdc,
1857           item->pszText,
1858           lstrlenW(item->pszText),
1859           drawRect,
1860           DT_LEFT | DT_SINGLELINE
1861         );
1862       }
1863     }
1864
1865     *drawRect = rcTemp; /* restore drawRect */
1866   }
1867
1868   /*
1869   * Cleanup
1870   */
1871   SelectObject(hdc, hOldFont);
1872   SetBkMode(hdc, oldBkMode);
1873   SelectObject(hdc, holdPen);
1874   DeleteObject( htextPen );
1875 }
1876
1877 /******************************************************************************
1878  * TAB_DrawItem
1879  *
1880  * This method is used to draw a single tab into the tab control.
1881  */
1882 static void TAB_DrawItem(
1883   HWND hwnd,
1884   HDC  hdc,
1885   INT  iItem)
1886 {
1887   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1888   LONG      lStyle  = GetWindowLongA(hwnd, GWL_STYLE);
1889   RECT      itemRect;
1890   RECT      selectedRect;
1891   BOOL      isVisible;
1892   RECT      r, fillRect, r1;
1893   INT       clRight = 0;
1894   INT       clBottom = 0;
1895   COLORREF  bkgnd, corner;
1896
1897   /*
1898    * Get the rectangle for the item.
1899    */
1900   isVisible = TAB_InternalGetItemRect(hwnd,
1901                                       infoPtr,
1902                                       iItem,
1903                                       &itemRect,
1904                                       &selectedRect);
1905
1906   if (isVisible)
1907   {
1908     RECT rUD, rC;
1909
1910     /* Clip UpDown control to not draw over it */
1911     if (infoPtr->needsScrolling)
1912     {
1913       GetWindowRect(hwnd, &rC);
1914       GetWindowRect(infoPtr->hwndUpDown, &rUD);
1915       ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
1916     }
1917
1918     /* If you need to see what the control is doing,
1919      * then override these variables. They will change what
1920      * fill colors are used for filling the tabs, and the
1921      * corners when drawing the edge.
1922      */
1923     bkgnd = comctl32_color.clrBtnFace;
1924     corner = comctl32_color.clrBtnFace;
1925
1926     if (lStyle & TCS_BUTTONS)
1927     {
1928       /* Get item rectangle */
1929       r = itemRect;
1930
1931       /* Separators between flat buttons */
1932       if (lStyle & TCS_FLATBUTTONS)
1933       {
1934         r1 = r;
1935         r1.right += (FLAT_BTN_SPACINGX -2);
1936         DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1937       }
1938
1939       if (iItem == infoPtr->iSelected)
1940       {
1941         DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1942         
1943         OffsetRect(&r, 1, 1);
1944       }
1945       else  /* ! selected */
1946       {
1947         if (!(lStyle & TCS_FLATBUTTONS))
1948           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1949       }
1950     }
1951     else /* !TCS_BUTTONS */
1952     {
1953       /* We draw a rectangle of different sizes depending on the selection
1954        * state. */
1955       if (iItem == infoPtr->iSelected) {
1956         RECT rect;
1957         GetClientRect (hwnd, &rect);
1958         clRight = rect.right;
1959         clBottom = rect.bottom;
1960         r = selectedRect;
1961       }
1962       else
1963         r = itemRect;
1964
1965       /*
1966        * Erase the background. (Delay it but setup rectangle.)
1967        * This is necessary when drawing the selected item since it is larger
1968        * than the others, it might overlap with stuff already drawn by the
1969        * other tabs
1970        */
1971       fillRect = r;
1972
1973       if(lStyle & TCS_VERTICAL)
1974       {
1975         /* These are for adjusting the drawing of a Selected tab      */
1976         /* The initial values are for the normal case of non-Selected */
1977         int ZZ = 1;   /* Do not strech if selected */
1978         if (iItem == infoPtr->iSelected) {
1979             ZZ = 0;
1980
1981             /* if leftmost draw the line longer */
1982             if(selectedRect.top == 0)
1983                 fillRect.top += CONTROL_BORDER_SIZEY;
1984             /* if rightmost draw the line longer */
1985             if(selectedRect.bottom == clBottom)
1986                 fillRect.bottom -= CONTROL_BORDER_SIZEY;
1987         }
1988
1989         if (lStyle & TCS_BOTTOM)
1990         {
1991           /* Adjust both rectangles to match native */
1992           r.left += (1-ZZ);
1993
1994           TRACE("<right> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1995                 iItem,
1996                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1997                 r.left,r.top,r.right,r.bottom);
1998
1999           /* Clear interior */
2000           SetBkColor(hdc, bkgnd);
2001           ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2002
2003           /* Draw rectangular edge around tab */
2004           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
2005
2006           /* Now erase the top corner and draw diagonal edge */
2007           SetBkColor(hdc, corner);
2008           r1.left = r.right - ROUND_CORNER_SIZE - 1;
2009           r1.top = r.top;
2010           r1.right = r.right;
2011           r1.bottom = r1.top + ROUND_CORNER_SIZE;
2012           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2013           r1.right--;
2014           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2015
2016           /* Now erase the bottom corner and draw diagonal edge */
2017           r1.left = r.right - ROUND_CORNER_SIZE - 1;
2018           r1.bottom = r.bottom;
2019           r1.right = r.right;
2020           r1.top = r1.bottom - ROUND_CORNER_SIZE;
2021           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2022           r1.right--;
2023           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2024
2025           if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
2026               r1 = r;
2027               r1.right = r1.left;
2028               r1.left--;
2029               DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
2030           }
2031
2032         }
2033         else
2034         {
2035           TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2036                 iItem,
2037                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2038                 r.left,r.top,r.right,r.bottom);
2039
2040           /* Clear interior */
2041           SetBkColor(hdc, bkgnd);
2042           ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2043
2044           /* Draw rectangular edge around tab */
2045           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2046
2047           /* Now erase the top corner and draw diagonal edge */
2048           SetBkColor(hdc, corner);
2049           r1.left = r.left;
2050           r1.top = r.top;
2051           r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2052           r1.bottom = r1.top + ROUND_CORNER_SIZE;
2053           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2054           r1.left++;
2055           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2056
2057           /* Now erase the bottom corner and draw diagonal edge */
2058           r1.left = r.left;
2059           r1.bottom = r.bottom;
2060           r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2061           r1.top = r1.bottom - ROUND_CORNER_SIZE;
2062           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2063           r1.left++;
2064           DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2065         }
2066       }
2067       else  /* ! TCS_VERTICAL */
2068       {
2069         /* These are for adjusting the drawing of a Selected tab      */
2070         /* The initial values are for the normal case of non-Selected */
2071         if (iItem == infoPtr->iSelected) {
2072             /* if leftmost draw the line longer */
2073             if(selectedRect.left == 0)
2074                 fillRect.left += CONTROL_BORDER_SIZEX;
2075             /* if rightmost draw the line longer */
2076             if(selectedRect.right == clRight)
2077                 fillRect.right -= CONTROL_BORDER_SIZEX;
2078         }
2079
2080         if (lStyle & TCS_BOTTOM)
2081         {
2082           /* Adjust both rectangles for topmost row */
2083           if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2084           {
2085             fillRect.top -= 2;
2086             r.top -= 1;
2087           }
2088
2089           TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2090                 iItem,
2091                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2092                 r.left,r.top,r.right,r.bottom);
2093
2094           /* Clear interior */
2095           SetBkColor(hdc, bkgnd);
2096           ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2097
2098           /* Draw rectangular edge around tab */
2099           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2100
2101           /* Now erase the righthand corner and draw diagonal edge */
2102           SetBkColor(hdc, corner);
2103           r1.left = r.right - ROUND_CORNER_SIZE;
2104           r1.bottom = r.bottom;
2105           r1.right = r.right;
2106           r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2107           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2108           r1.bottom--;
2109           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2110
2111           /* Now erase the lefthand corner and draw diagonal edge */
2112           r1.left = r.left;
2113           r1.bottom = r.bottom;
2114           r1.right = r1.left + ROUND_CORNER_SIZE;
2115           r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2116           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2117           r1.bottom--;
2118           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2119
2120           if (iItem == infoPtr->iSelected)
2121           {
2122             r.top += 2;
2123             r.left += 1;
2124             if (selectedRect.left == 0)
2125             {
2126               r1 = r;
2127               r1.bottom = r1.top;
2128               r1.top--;
2129               DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2130             }
2131           }
2132
2133         }
2134         else
2135         {
2136           /* Adjust both rectangles for bottommost row */
2137           if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2138           {
2139             fillRect.bottom += 3;
2140             r.bottom += 2;
2141           }
2142
2143           TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2144                 iItem,
2145                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2146                 r.left,r.top,r.right,r.bottom);
2147
2148           /* Clear interior */
2149           SetBkColor(hdc, bkgnd);
2150           ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2151
2152           /* Draw rectangular edge around tab */
2153           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2154
2155           /* Now erase the righthand corner and draw diagonal edge */
2156           SetBkColor(hdc, corner);
2157           r1.left = r.right - ROUND_CORNER_SIZE;
2158           r1.top = r.top;
2159           r1.right = r.right;
2160           r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2161           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2162           r1.top++;
2163           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2164
2165           /* Now erase the lefthand corner and draw diagonal edge */
2166           r1.left = r.left;
2167           r1.top = r.top;
2168           r1.right = r1.left + ROUND_CORNER_SIZE;
2169           r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2170           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2171           r1.top++;
2172           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2173         }
2174       }
2175     }
2176
2177     TAB_DumpItemInternal(infoPtr, iItem);
2178
2179     /* This modifies r to be the text rectangle. */
2180     TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
2181   }
2182 }
2183
2184 /******************************************************************************
2185  * TAB_DrawBorder
2186  *
2187  * This method is used to draw the raised border around the tab control
2188  * "content" area.
2189  */
2190 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
2191 {
2192   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2193   RECT rect;
2194   DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2195
2196   GetClientRect (hwnd, &rect);
2197
2198   /*
2199    * Adjust for the style
2200    */
2201
2202   if (infoPtr->uNumItem)
2203   {
2204     if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2205       rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2206     else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2207       rect.right  -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2208     else if(lStyle & TCS_VERTICAL)
2209       rect.left   += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2210     else /* not TCS_VERTICAL and not TCS_BOTTOM */
2211       rect.top    += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2212   }
2213
2214   TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2215         rect.left, rect.top, rect.right, rect.bottom);
2216
2217   DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2218 }
2219
2220 /******************************************************************************
2221  * TAB_Refresh
2222  *
2223  * This method repaints the tab control..
2224  */
2225 static void TAB_Refresh (HWND hwnd, HDC hdc)
2226 {
2227   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2228   HFONT hOldFont;
2229   INT i;
2230
2231   if (!infoPtr->DoRedraw)
2232     return;
2233
2234   hOldFont = SelectObject (hdc, infoPtr->hFont);
2235
2236   if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
2237   {
2238     for (i = 0; i < infoPtr->uNumItem; i++)
2239       TAB_DrawItem (hwnd, hdc, i);
2240   }
2241   else
2242   {
2243     /* Draw all the non selected item first */
2244     for (i = 0; i < infoPtr->uNumItem; i++)
2245     {
2246       if (i != infoPtr->iSelected)
2247         TAB_DrawItem (hwnd, hdc, i);
2248     }
2249
2250     /* Now, draw the border, draw it before the selected item
2251      * since the selected item overwrites part of the border. */
2252     TAB_DrawBorder (hwnd, hdc);
2253
2254     /* Then, draw the selected item */
2255     TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
2256
2257     /* If we haven't set the current focus yet, set it now.
2258      * Only happens when we first paint the tab controls */
2259     if (infoPtr->uFocus == -1)
2260       TAB_SetCurFocus(hwnd, infoPtr->iSelected);
2261   }
2262
2263   SelectObject (hdc, hOldFont);
2264 }
2265
2266 static DWORD
2267 TAB_GetRowCount (HWND hwnd )
2268 {
2269   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2270
2271   return infoPtr->uNumRows;
2272 }
2273
2274 static LRESULT
2275 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
2276 {
2277     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2278
2279   infoPtr->DoRedraw=(BOOL) wParam;
2280   return 0;
2281 }
2282
2283 /******************************************************************************
2284  * TAB_EnsureSelectionVisible
2285  *
2286  * This method will make sure that the current selection is completely
2287  * visible by scrolling until it is.
2288  */
2289 static void TAB_EnsureSelectionVisible(
2290   HWND      hwnd,
2291   TAB_INFO* infoPtr)
2292 {
2293   INT iSelected = infoPtr->iSelected;
2294   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2295   INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2296
2297   /* set the items row to the bottommost row or topmost row depending on
2298    * style */
2299   if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2300   {
2301       TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2302       INT newselected;
2303       INT iTargetRow;
2304
2305       if(lStyle & TCS_VERTICAL)
2306         newselected = selected->rect.left;
2307       else
2308         newselected = selected->rect.top;
2309
2310       /* the target row is always (number of rows - 1)
2311          as row 0 is furthest from the clientRect */
2312       iTargetRow = infoPtr->uNumRows - 1;
2313
2314       if (newselected != iTargetRow)
2315       {
2316          UINT i;
2317          if(lStyle & TCS_VERTICAL)
2318          {
2319            for (i=0; i < infoPtr->uNumItem; i++)
2320            {
2321              /* move everything in the row of the selected item to the iTargetRow */
2322              TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2323
2324              if (item->rect.left == newselected )
2325                  item->rect.left = iTargetRow;
2326              else
2327              {
2328                if (item->rect.left > newselected)
2329                  item->rect.left-=1;
2330              }
2331            }
2332          }
2333          else
2334          {
2335            for (i=0; i < infoPtr->uNumItem; i++)
2336            {
2337              TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2338
2339              if (item->rect.top == newselected )
2340                  item->rect.top = iTargetRow;
2341              else
2342              {
2343                if (item->rect.top > newselected)
2344                  item->rect.top-=1;
2345              }
2346           }
2347         }
2348         TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2349       }
2350   }
2351
2352   /*
2353    * Do the trivial cases first.
2354    */
2355   if ( (!infoPtr->needsScrolling) ||
2356        (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2357     return;
2358
2359   if (infoPtr->leftmostVisible >= iSelected)
2360   {
2361     infoPtr->leftmostVisible = iSelected;
2362   }
2363   else
2364   {
2365      TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2366      RECT r;
2367      INT width;
2368      UINT i;
2369
2370      /* Calculate the part of the client area that is visible */
2371      GetClientRect(hwnd, &r);
2372      width = r.right;
2373
2374      GetClientRect(infoPtr->hwndUpDown, &r);
2375      width -= r.right;
2376
2377      if ((selected->rect.right -
2378           selected->rect.left) >= width )
2379      {
2380         /* Special case: width of selected item is greater than visible
2381          * part of control.
2382          */
2383         infoPtr->leftmostVisible = iSelected;
2384      }
2385      else
2386      {
2387         for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2388         {
2389            if ((selected->rect.right - TAB_GetItem(infoPtr, i)->rect.left) < width)
2390               break;
2391         }
2392         infoPtr->leftmostVisible = i;
2393      }
2394   }
2395
2396   if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2397     TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2398
2399   SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2400                MAKELONG(infoPtr->leftmostVisible, 0));
2401 }
2402
2403 /******************************************************************************
2404  * TAB_InvalidateTabArea
2405  *
2406  * This method will invalidate the portion of the control that contains the
2407  * tabs. It is called when the state of the control changes and needs
2408  * to be redisplayed
2409  */
2410 static void TAB_InvalidateTabArea(
2411   HWND      hwnd,
2412   TAB_INFO* infoPtr)
2413 {
2414   RECT clientRect, rInvalidate, rAdjClient;
2415   DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2416   INT lastRow = infoPtr->uNumRows - 1;
2417   RECT rect;
2418
2419   if (lastRow < 0) return;
2420
2421   GetClientRect(hwnd, &clientRect);
2422   rInvalidate = clientRect;
2423   rAdjClient = clientRect;
2424
2425   TAB_AdjustRect(hwnd, 0, &rAdjClient);
2426
2427   TAB_InternalGetItemRect(hwnd, infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2428   if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2429   {
2430     rInvalidate.left = rAdjClient.right;
2431     if (infoPtr->uNumRows == 1)
2432       rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2433   }
2434   else if(lStyle & TCS_VERTICAL)
2435   {
2436     rInvalidate.right = rAdjClient.left;
2437     if (infoPtr->uNumRows == 1)
2438       rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2439   }
2440   else if (lStyle & TCS_BOTTOM)
2441   {
2442     rInvalidate.top = rAdjClient.bottom;
2443     if (infoPtr->uNumRows == 1)
2444       rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2445   }
2446   else 
2447   {
2448     rInvalidate.bottom = rAdjClient.top;
2449     if (infoPtr->uNumRows == 1)
2450       rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2451   }
2452   
2453   /* Punch out the updown control */
2454   if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2455     RECT r;
2456     GetClientRect(infoPtr->hwndUpDown, &r);
2457     if (rInvalidate.right > clientRect.right - r.left)
2458       rInvalidate.right = rInvalidate.right - (r.right - r.left);
2459     else
2460       rInvalidate.right = clientRect.right - r.left;
2461   }
2462   
2463   TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2464         rInvalidate.left,  rInvalidate.top,
2465         rInvalidate.right, rInvalidate.bottom);
2466  
2467   InvalidateRect(hwnd, &rInvalidate, TRUE);
2468 }
2469
2470 static LRESULT
2471 TAB_Paint (HWND hwnd, WPARAM wParam)
2472 {
2473   HDC hdc;
2474   PAINTSTRUCT ps;
2475
2476   if (wParam == 0)
2477   {
2478     hdc = BeginPaint (hwnd, &ps);
2479     TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2480          ps.fErase,
2481          ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2482   } else {
2483     hdc = (HDC)wParam;
2484   }
2485     
2486   TAB_Refresh (hwnd, hdc);
2487
2488   if(!wParam)
2489     EndPaint (hwnd, &ps);
2490
2491   return 0;
2492 }
2493
2494 static LRESULT
2495 TAB_InsertItemAW (HWND hwnd, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2496 {
2497   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2498   TAB_ITEM *item;
2499   TCITEMA *pti;
2500   INT iItem;
2501   RECT rect;
2502
2503   GetClientRect (hwnd, &rect);
2504   TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2505         rect.top, rect.left, rect.bottom, rect.right);
2506
2507   pti = (TCITEMA *)lParam;
2508   iItem = (INT)wParam;
2509
2510   if (iItem < 0) return -1;
2511   if (iItem > infoPtr->uNumItem)
2512     iItem = infoPtr->uNumItem;
2513
2514   if (bUnicode)
2515     TAB_DumpItemExternalW((TCITEMW*)pti, iItem);
2516   else
2517     TAB_DumpItemExternalA(pti, iItem);
2518
2519
2520   if (infoPtr->uNumItem == 0) {
2521     infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2522     infoPtr->uNumItem++;
2523     infoPtr->iSelected = 0;
2524   }
2525   else {
2526     LPBYTE oldItems = (LPBYTE)infoPtr->items;
2527
2528     infoPtr->uNumItem++;
2529     infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2530
2531     /* pre insert copy */
2532     if (iItem > 0) {
2533       memcpy (infoPtr->items, oldItems,
2534               iItem * TAB_ITEM_SIZE(infoPtr));
2535     }
2536
2537     /* post insert copy */
2538     if (iItem < infoPtr->uNumItem - 1) {
2539       memcpy (TAB_GetItem(infoPtr, iItem + 1),
2540               oldItems + iItem * TAB_ITEM_SIZE(infoPtr),
2541               (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2542
2543     }
2544
2545     if (iItem <= infoPtr->iSelected)
2546       infoPtr->iSelected++;
2547
2548     Free (oldItems);
2549   }
2550
2551   item = TAB_GetItem(infoPtr, iItem);
2552
2553   item->mask = pti->mask;
2554   item->pszText = NULL;
2555
2556   if (pti->mask & TCIF_TEXT)
2557   {
2558     if (bUnicode)
2559       Str_SetPtrW (&item->pszText, (WCHAR*)pti->pszText);
2560     else
2561       Str_SetPtrAtoW (&item->pszText, pti->pszText);
2562   }
2563
2564   if (pti->mask & TCIF_IMAGE)
2565     item->iImage = pti->iImage;
2566   else
2567     item->iImage = -1;
2568
2569   if (pti->mask & TCIF_PARAM)
2570     memcpy(item->extra, &pti->lParam, infoPtr->cbInfo);
2571   else
2572     memset(item->extra, 0, infoPtr->cbInfo);
2573   
2574   TAB_SetItemBounds(hwnd);
2575   if (infoPtr->uNumItem > 1)
2576     TAB_InvalidateTabArea(hwnd, infoPtr);
2577   else
2578     InvalidateRect(hwnd, NULL, TRUE);
2579
2580   TRACE("[%p]: added item %d %s\n",
2581         hwnd, iItem, debugstr_w(item->pszText));
2582
2583   return iItem;
2584 }
2585
2586 static LRESULT
2587 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2588 {
2589   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2590   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2591   LONG lResult = 0;
2592   BOOL bNeedPaint = FALSE;
2593
2594   lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2595
2596   /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2597   if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2598   {
2599     infoPtr->tabWidth = max((INT)LOWORD(lParam), infoPtr->tabMinWidth);
2600     bNeedPaint = TRUE;
2601   }
2602
2603   if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2604   {
2605     if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2606       infoPtr->tabHeight = (INT)HIWORD(lParam);
2607
2608     bNeedPaint = TRUE;
2609   }
2610   TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2611        HIWORD(lResult), LOWORD(lResult),
2612        infoPtr->tabHeight, infoPtr->tabWidth);
2613
2614   if (bNeedPaint)
2615   {
2616     TAB_SetItemBounds(hwnd);
2617     RedrawWindow(hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2618   }
2619     
2620   return lResult;
2621 }
2622
2623 static LRESULT
2624 TAB_SetMinTabWidth (HWND hwnd, LPARAM lParam)
2625 {
2626   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2627   INT cx = (INT)lParam;
2628   INT oldcx;
2629
2630   if (infoPtr) {
2631     oldcx = infoPtr->tabMinWidth;
2632     infoPtr->tabMinWidth = (cx==-1)?DEFAULT_TAB_WIDTH:cx;
2633   } else
2634     return 0;
2635
2636   return oldcx;
2637 }
2638
2639 static LRESULT 
2640 TAB_HighlightItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2641 {
2642   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2643   INT iItem = (INT)wParam;
2644   BOOL fHighlight = (BOOL)LOWORD(lParam);
2645
2646   if ((infoPtr) && (iItem>=0) && (iItem<infoPtr->uNumItem)) {
2647     if (fHighlight)
2648       TAB_GetItem(infoPtr, iItem)->dwState |= TCIS_HIGHLIGHTED;
2649     else
2650       TAB_GetItem(infoPtr, iItem)->dwState &= ~TCIS_HIGHLIGHTED;
2651   } else
2652     return FALSE;
2653
2654   return TRUE;
2655 }
2656
2657 static LRESULT
2658 TAB_SetItemAW (HWND hwnd, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2659 {
2660   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2661   TCITEMA *tabItem;
2662   TAB_ITEM *wineItem;
2663   INT    iItem;
2664
2665   iItem = (INT)wParam;
2666   tabItem = (LPTCITEMA)lParam;
2667
2668   TRACE("%d %p\n", iItem, tabItem);
2669   if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2670
2671   if (bUnicode)
2672     TAB_DumpItemExternalW((TCITEMW *)tabItem, iItem);
2673   else
2674     TAB_DumpItemExternalA(tabItem, iItem);
2675
2676   wineItem = TAB_GetItem(infoPtr, iItem);
2677
2678   if (tabItem->mask & TCIF_IMAGE)
2679     wineItem->iImage = tabItem->iImage;
2680
2681   if (tabItem->mask & TCIF_PARAM)
2682     memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2683
2684   if (tabItem->mask & TCIF_RTLREADING)
2685     FIXME("TCIF_RTLREADING\n");
2686
2687   if (tabItem->mask & TCIF_STATE)
2688     wineItem->dwState = tabItem->dwState;
2689
2690   if (tabItem->mask & TCIF_TEXT)
2691   {
2692     if (wineItem->pszText)
2693     {
2694       Free(wineItem->pszText);
2695       wineItem->pszText = NULL;
2696     }
2697     if (bUnicode)
2698       Str_SetPtrW(&wineItem->pszText, (WCHAR*)tabItem->pszText);
2699     else
2700       Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2701   }
2702
2703   /* Update and repaint tabs */
2704   TAB_SetItemBounds(hwnd);
2705   TAB_InvalidateTabArea(hwnd,infoPtr);
2706
2707   return TRUE;
2708 }
2709
2710 static LRESULT
2711 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2712 {
2713    TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2714
2715    return infoPtr->uNumItem;
2716 }
2717
2718
2719 static LRESULT
2720 TAB_GetItemAW (HWND hwnd, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2721 {
2722    TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2723    TCITEMA *tabItem;
2724    TAB_ITEM *wineItem;
2725    INT    iItem;
2726
2727   iItem = (INT)wParam;
2728   tabItem = (LPTCITEMA)lParam;
2729   TRACE("\n");
2730   if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2731     return FALSE;
2732
2733   wineItem = TAB_GetItem(infoPtr, iItem);
2734
2735   if (tabItem->mask & TCIF_IMAGE)
2736     tabItem->iImage = wineItem->iImage;
2737
2738   if (tabItem->mask & TCIF_PARAM)
2739     memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2740
2741   if (tabItem->mask & TCIF_RTLREADING)
2742     FIXME("TCIF_RTLREADING\n");
2743
2744   if (tabItem->mask & TCIF_STATE)
2745     tabItem->dwState = wineItem->dwState;
2746
2747   if (tabItem->mask & TCIF_TEXT)
2748   {
2749     if (bUnicode)
2750       Str_GetPtrW (wineItem->pszText, (WCHAR*)tabItem->pszText, tabItem->cchTextMax);
2751     else
2752       Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2753   }
2754
2755   if (bUnicode)
2756     TAB_DumpItemExternalW((TCITEMW*)tabItem, iItem);
2757   else
2758     TAB_DumpItemExternalA(tabItem, iItem);
2759
2760   return TRUE;
2761 }
2762
2763
2764 static LRESULT
2765 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2766 {
2767     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2768     INT iItem = (INT) wParam;
2769     BOOL bResult = FALSE;
2770
2771     if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2772     {
2773         TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
2774         LPBYTE oldItems = (LPBYTE)infoPtr->items;
2775         
2776         TAB_InvalidateTabArea(hwnd, infoPtr);
2777
2778         if ((item->mask & TCIF_TEXT) && item->pszText)
2779             Free(item->pszText);
2780
2781         infoPtr->uNumItem--;
2782
2783         if (!infoPtr->uNumItem)
2784         {
2785             infoPtr->items = NULL;
2786             if (infoPtr->iHotTracked >= 0)
2787             {
2788                 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2789                 infoPtr->iHotTracked = -1;
2790             }
2791         }
2792         else
2793         {
2794             infoPtr->items = Alloc(TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2795
2796             if (iItem > 0)
2797                 memcpy(infoPtr->items, oldItems, iItem * TAB_ITEM_SIZE(infoPtr));
2798
2799             if (iItem < infoPtr->uNumItem)
2800                 memcpy(TAB_GetItem(infoPtr, iItem),
2801                        oldItems + (iItem + 1) * TAB_ITEM_SIZE(infoPtr),
2802                        (infoPtr->uNumItem - iItem) * TAB_ITEM_SIZE(infoPtr));
2803
2804             if (iItem <= infoPtr->iHotTracked)
2805             {
2806                 /* When tabs move left/up, the hot track item may change */
2807                 FIXME("Recalc hot track");
2808             }
2809         }
2810         Free(oldItems);
2811
2812         /* Readjust the selected index */
2813         if ((iItem == infoPtr->iSelected) && (iItem > 0))
2814             infoPtr->iSelected--;
2815
2816         if (iItem < infoPtr->iSelected)
2817             infoPtr->iSelected--;
2818
2819         if (infoPtr->uNumItem == 0)
2820             infoPtr->iSelected = -1;
2821
2822         /* Reposition and repaint tabs */
2823         TAB_SetItemBounds(hwnd);
2824
2825         bResult = TRUE;
2826     }
2827
2828     return bResult;
2829 }
2830
2831 static LRESULT
2832 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2833 {
2834     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2835  
2836     while (infoPtr->uNumItem)
2837       TAB_DeleteItem (hwnd, 0, 0);
2838     return TRUE;
2839 }
2840
2841
2842 static LRESULT
2843 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2844 {
2845   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2846
2847   TRACE("\n");
2848   return (LRESULT)infoPtr->hFont;
2849 }
2850
2851 static LRESULT
2852 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2853
2854 {
2855   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2856
2857   TRACE("%x %lx\n",wParam, lParam);
2858
2859   infoPtr->hFont = (HFONT)wParam;
2860
2861   TAB_SetItemBounds(hwnd);
2862
2863   TAB_InvalidateTabArea(hwnd, infoPtr);
2864
2865   return 0;
2866 }
2867
2868
2869 static LRESULT
2870 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2871 {
2872   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2873
2874   TRACE("\n");
2875   return (LRESULT)infoPtr->himl;
2876 }
2877
2878 static LRESULT
2879 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2880 {
2881     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2882     HIMAGELIST himlPrev;
2883
2884     TRACE("\n");
2885     himlPrev = infoPtr->himl;
2886     infoPtr->himl= (HIMAGELIST)lParam;
2887     return (LRESULT)himlPrev;
2888 }
2889
2890 static LRESULT
2891 TAB_GetUnicodeFormat (HWND hwnd)
2892 {
2893     TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2894     return infoPtr->bUnicode;
2895 }
2896
2897 static LRESULT
2898 TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
2899 {
2900     TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2901     BOOL bTemp = infoPtr->bUnicode;
2902
2903     infoPtr->bUnicode = (BOOL)wParam;
2904
2905     return bTemp;
2906 }
2907
2908 static LRESULT
2909 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2910
2911 {
2912 /* I'm not really sure what the following code was meant to do.
2913    This is what it is doing:
2914    When WM_SIZE is sent with SIZE_RESTORED, the control
2915    gets positioned in the top left corner.
2916
2917   RECT parent_rect;
2918   HWND parent;
2919   UINT uPosFlags,cx,cy;
2920
2921   uPosFlags=0;
2922   if (!wParam) {
2923     parent = GetParent (hwnd);
2924     GetClientRect(parent, &parent_rect);
2925     cx=LOWORD (lParam);
2926     cy=HIWORD (lParam);
2927     if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2928         uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2929
2930     SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2931             cx, cy, uPosFlags | SWP_NOZORDER);
2932   } else {
2933     FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2934   } */
2935
2936   /* Recompute the size/position of the tabs. */
2937   TAB_SetItemBounds (hwnd);
2938
2939   /* Force a repaint of the control. */
2940   InvalidateRect(hwnd, NULL, TRUE);
2941
2942   return 0;
2943 }
2944
2945
2946 static LRESULT
2947 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2948 {
2949   TAB_INFO *infoPtr;
2950   TEXTMETRICA fontMetrics;
2951   HDC hdc;
2952   HFONT hOldFont;
2953   DWORD dwStyle;
2954
2955   infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
2956
2957   SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
2958
2959   infoPtr->hwndNotify      = ((LPCREATESTRUCTW)lParam)->hwndParent;
2960   infoPtr->uNumItem        = 0;
2961   infoPtr->uNumRows        = 0;
2962   infoPtr->uHItemPadding   = 6;
2963   infoPtr->uVItemPadding   = 3;
2964   infoPtr->uHItemPadding_s = 6;
2965   infoPtr->uVItemPadding_s = 3;
2966   infoPtr->hFont           = 0;
2967   infoPtr->items           = 0;
2968   infoPtr->hcurArrow       = LoadCursorA (0, (LPSTR)IDC_ARROW);
2969   infoPtr->iSelected       = -1;
2970   infoPtr->iHotTracked     = -1;
2971   infoPtr->uFocus          = -1;
2972   infoPtr->hwndToolTip     = 0;
2973   infoPtr->DoRedraw        = TRUE;
2974   infoPtr->needsScrolling  = FALSE;
2975   infoPtr->hwndUpDown      = 0;
2976   infoPtr->leftmostVisible = 0;
2977   infoPtr->fHeightSet      = FALSE;
2978   infoPtr->bUnicode        = IsWindowUnicode (hwnd);
2979   infoPtr->cbInfo          = sizeof(LPARAM);
2980
2981   TRACE("Created tab control, hwnd [%p]\n", hwnd);
2982
2983   /* The tab control always has the WS_CLIPSIBLINGS style. Even
2984      if you don't specify it in CreateWindow. This is necessary in
2985      order for paint to work correctly. This follows windows behaviour. */
2986   dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
2987   SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2988
2989   if (dwStyle & TCS_TOOLTIPS) {
2990     /* Create tooltip control */
2991     infoPtr->hwndToolTip =
2992       CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2993                        CW_USEDEFAULT, CW_USEDEFAULT,
2994                        CW_USEDEFAULT, CW_USEDEFAULT,
2995                        hwnd, 0, 0, 0);
2996
2997     /* Send NM_TOOLTIPSCREATED notification */
2998     if (infoPtr->hwndToolTip) {
2999       NMTOOLTIPSCREATED nmttc;
3000
3001       nmttc.hdr.hwndFrom = hwnd;
3002       nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
3003       nmttc.hdr.code = NM_TOOLTIPSCREATED;
3004       nmttc.hwndToolTips = infoPtr->hwndToolTip;
3005
3006       SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
3007                     (WPARAM)GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc);
3008     }
3009   }
3010
3011   /*
3012    * We need to get text information so we need a DC and we need to select
3013    * a font.
3014    */
3015   hdc = GetDC(hwnd);
3016   hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3017
3018   /* Use the system font to determine the initial height of a tab. */
3019   GetTextMetricsA(hdc, &fontMetrics);
3020
3021   /*
3022    * Make sure there is enough space for the letters + growing the
3023    * selected item + extra space for the selected item.
3024    */
3025   infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3026                        ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
3027                         infoPtr->uVItemPadding;
3028
3029   /* Initialize the width of a tab. */
3030   infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
3031   infoPtr->tabMinWidth = 0;
3032
3033   TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3034
3035   SelectObject (hdc, hOldFont);
3036   ReleaseDC(hwnd, hdc);
3037
3038   return 0;
3039 }
3040
3041 static LRESULT
3042 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
3043 {
3044   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3045   UINT iItem;
3046
3047   if (!infoPtr)
3048       return 0;
3049
3050   if (infoPtr->items) {
3051     for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3052       if (TAB_GetItem(infoPtr, iItem)->pszText)
3053         Free (TAB_GetItem(infoPtr, iItem)->pszText);
3054     }
3055     Free (infoPtr->items);
3056   }
3057
3058   if (infoPtr->hwndToolTip)
3059     DestroyWindow (infoPtr->hwndToolTip);
3060
3061   if (infoPtr->hwndUpDown)
3062     DestroyWindow(infoPtr->hwndUpDown);
3063
3064   if (infoPtr->iHotTracked >= 0)
3065     KillTimer(hwnd, TAB_HOTTRACK_TIMER);
3066
3067   Free (infoPtr);
3068   SetWindowLongA(hwnd, 0, 0);
3069   return 0;
3070 }
3071
3072 static LRESULT
3073 TAB_SetItemExtra (HWND hwnd, WPARAM wParam, LPARAM lParam)
3074 {
3075   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3076   INT cbInfo = wParam;
3077    
3078   if (!infoPtr || cbInfo <= 0)
3079     return FALSE;
3080
3081   if (infoPtr->uNumItem)
3082   {
3083     /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
3084     return FALSE;
3085   }
3086     
3087   infoPtr->cbInfo = cbInfo;
3088   return TRUE;
3089 }
3090
3091 static LRESULT WINAPI
3092 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3093 {
3094     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3095
3096     TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3097     if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
3098       return DefWindowProcW (hwnd, uMsg, wParam, lParam);
3099
3100     switch (uMsg)
3101     {
3102     case TCM_GETIMAGELIST:
3103       return TAB_GetImageList (hwnd, wParam, lParam);
3104
3105     case TCM_SETIMAGELIST:
3106       return TAB_SetImageList (hwnd, wParam, lParam);
3107
3108     case TCM_GETITEMCOUNT:
3109       return TAB_GetItemCount (hwnd, wParam, lParam);
3110
3111     case TCM_GETITEMA:
3112     case TCM_GETITEMW:
3113       return TAB_GetItemAW (hwnd, wParam, lParam, uMsg == TCM_GETITEMW);
3114
3115     case TCM_SETITEMA:
3116     case TCM_SETITEMW:
3117       return TAB_SetItemAW (hwnd, wParam, lParam, uMsg == TCM_SETITEMW);
3118
3119     case TCM_DELETEITEM:
3120       return TAB_DeleteItem (hwnd, wParam, lParam);
3121
3122     case TCM_DELETEALLITEMS:
3123      return TAB_DeleteAllItems (hwnd, wParam, lParam);
3124
3125     case TCM_GETITEMRECT:
3126      return TAB_GetItemRect (hwnd, wParam, lParam);
3127
3128     case TCM_GETCURSEL:
3129       return TAB_GetCurSel (hwnd);
3130
3131     case TCM_HITTEST:
3132       return TAB_HitTest (hwnd, wParam, lParam);
3133
3134     case TCM_SETCURSEL:
3135       return TAB_SetCurSel (hwnd, wParam);
3136
3137     case TCM_INSERTITEMA:
3138     case TCM_INSERTITEMW:
3139       return TAB_InsertItemAW (hwnd, wParam, lParam, uMsg == TCM_INSERTITEMW);
3140
3141     case TCM_SETITEMEXTRA:
3142       return TAB_SetItemExtra (hwnd, wParam, lParam);
3143
3144     case TCM_ADJUSTRECT:
3145       return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
3146
3147     case TCM_SETITEMSIZE:
3148       return TAB_SetItemSize (hwnd, wParam, lParam);
3149
3150     case TCM_REMOVEIMAGE:
3151       FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3152       return 0;
3153
3154     case TCM_SETPADDING:
3155       return TAB_SetPadding (hwnd, wParam, lParam);
3156
3157     case TCM_GETROWCOUNT:
3158       return TAB_GetRowCount(hwnd);
3159
3160     case TCM_GETUNICODEFORMAT:
3161       return TAB_GetUnicodeFormat (hwnd);
3162
3163     case TCM_SETUNICODEFORMAT:
3164       return TAB_SetUnicodeFormat (hwnd, wParam);
3165
3166     case TCM_HIGHLIGHTITEM:
3167       return TAB_HighlightItem (hwnd, wParam, lParam);
3168
3169     case TCM_GETTOOLTIPS:
3170       return TAB_GetToolTips (hwnd, wParam, lParam);
3171
3172     case TCM_SETTOOLTIPS:
3173       return TAB_SetToolTips (hwnd, wParam, lParam);
3174
3175     case TCM_GETCURFOCUS:
3176       return TAB_GetCurFocus (hwnd);
3177
3178     case TCM_SETCURFOCUS:
3179       return TAB_SetCurFocus (hwnd, wParam);
3180
3181     case TCM_SETMINTABWIDTH:
3182       return TAB_SetMinTabWidth(hwnd, lParam);
3183
3184     case TCM_DESELECTALL:
3185       FIXME("Unimplemented msg TCM_DESELECTALL\n");
3186       return 0;
3187
3188     case TCM_GETEXTENDEDSTYLE:
3189       FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3190       return 0;
3191
3192     case TCM_SETEXTENDEDSTYLE:
3193       FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3194       return 0;
3195
3196     case WM_GETFONT:
3197       return TAB_GetFont (hwnd, wParam, lParam);
3198
3199     case WM_SETFONT:
3200       return TAB_SetFont (hwnd, wParam, lParam);
3201
3202     case WM_CREATE:
3203       return TAB_Create (hwnd, wParam, lParam);
3204
3205     case WM_NCDESTROY:
3206       return TAB_Destroy (hwnd, wParam, lParam);
3207
3208     case WM_GETDLGCODE:
3209       return DLGC_WANTARROWS | DLGC_WANTCHARS;
3210
3211     case WM_LBUTTONDOWN:
3212       return TAB_LButtonDown (hwnd, wParam, lParam);
3213
3214     case WM_LBUTTONUP:
3215       return TAB_LButtonUp (hwnd, wParam, lParam);
3216
3217     case WM_NOTIFY:
3218       return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3219
3220     case WM_RBUTTONDOWN:
3221       return TAB_RButtonDown (hwnd, wParam, lParam);
3222
3223     case WM_MOUSEMOVE:
3224       return TAB_MouseMove (hwnd, wParam, lParam);
3225
3226     case WM_PAINT:
3227       return TAB_Paint (hwnd, wParam);
3228
3229     case WM_SIZE:
3230       return TAB_Size (hwnd, wParam, lParam);
3231
3232     case WM_SETREDRAW:
3233       return TAB_SetRedraw (hwnd, wParam);
3234
3235     case WM_HSCROLL:
3236       return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3237
3238     case WM_STYLECHANGED:
3239       TAB_SetItemBounds (hwnd);
3240       InvalidateRect(hwnd, NULL, TRUE);
3241       return 0;
3242
3243     case WM_SYSCOLORCHANGE:
3244       COMCTL32_RefreshSysColors();
3245       return 0;
3246
3247     case WM_KILLFOCUS:
3248     case WM_SETFOCUS:
3249       return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
3250
3251     case WM_KEYUP:
3252       return TAB_KeyUp(hwnd, wParam);
3253     case WM_NCHITTEST:
3254       return TAB_NCHitTest(hwnd, lParam);
3255
3256     default:
3257       if ((uMsg >= WM_USER) && (uMsg < WM_APP))
3258         WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3259              uMsg, wParam, lParam);
3260       return DefWindowProcW(hwnd, uMsg, wParam, lParam);
3261     }
3262
3263     return 0;
3264 }
3265
3266
3267 VOID
3268 TAB_Register (void)
3269 {
3270   WNDCLASSW wndClass;
3271
3272   ZeroMemory (&wndClass, sizeof(WNDCLASSW));
3273   wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3274   wndClass.lpfnWndProc   = TAB_WindowProc;
3275   wndClass.cbClsExtra    = 0;
3276   wndClass.cbWndExtra    = sizeof(TAB_INFO *);
3277   wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3278   wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
3279   wndClass.lpszClassName = WC_TABCONTROLW;
3280
3281   RegisterClassW (&wndClass);
3282 }
3283
3284
3285 VOID
3286 TAB_Unregister (void)
3287 {
3288     UnregisterClassW (WC_TABCONTROLW, NULL);
3289 }