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