Added the ability to use multiline tab controls to wine.
[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  *
8  * TODO:
9  *  Image list support
10  *  Multiline support
11  *  Unicode support
12  */
13
14 #include <string.h>
15
16 #include "winbase.h"
17 #include "commctrl.h"
18 #include "tab.h"
19 #include "debugtools.h"
20 #include "cache.h"
21 #include "win.h"
22
23 DEFAULT_DEBUG_CHANNEL(tab)
24
25 /******************************************************************************
26  * Positioning constants
27  */
28 #define SELECTED_TAB_OFFSET     2
29 #define HORIZONTAL_ITEM_PADDING 5
30 #define VERTICAL_ITEM_PADDING   3
31 #define ROUND_CORNER_SIZE       2
32 #define FOCUS_RECT_HOFFSET      2
33 #define FOCUS_RECT_VOFFSET      1
34 #define DISPLAY_AREA_PADDINGX   2
35 #define DISPLAY_AREA_PADDINGY   2
36 #define CONTROL_BORDER_SIZEX    2
37 #define CONTROL_BORDER_SIZEY    2
38 #define BUTTON_SPACINGX         10
39 #define DEFAULT_TAB_WIDTH       96
40
41 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
42
43 /******************************************************************************
44  * Prototypes
45  */
46 static void TAB_Refresh (HWND hwnd, HDC hdc);
47 static void TAB_InvalidateTabArea(HWND      hwnd, TAB_INFO* infoPtr);
48 static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
49
50 static BOOL
51 TAB_SendSimpleNotify (HWND hwnd, UINT code)
52 {
53     NMHDR nmhdr;
54
55     nmhdr.hwndFrom = hwnd;
56     nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
57     nmhdr.code = code;
58
59     return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
60             (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
61 }
62
63
64 static VOID
65 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
66             WPARAM wParam, LPARAM lParam)
67 {
68     MSG msg;
69
70     msg.hwnd = hwndMsg;
71     msg.message = uMsg;
72     msg.wParam = wParam;
73     msg.lParam = lParam;
74     msg.time = GetMessageTime ();
75     msg.pt.x = LOWORD(GetMessagePos ());
76     msg.pt.y = HIWORD(GetMessagePos ());
77
78     SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
79 }
80
81
82
83 static LRESULT
84 TAB_GetCurSel (HWND hwnd)
85 {
86     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
87  
88     return infoPtr->iSelected;
89 }
90
91 static LRESULT
92 TAB_GetCurFocus (HWND hwnd)
93 {
94     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
95  
96     return infoPtr->uFocus;
97 }
98
99 static LRESULT
100 TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
101 {
102     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
103
104     if (infoPtr == NULL) return 0;
105     return infoPtr->hwndToolTip;
106 }
107
108
109 static LRESULT
110 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
111 {
112     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
113   INT iItem=(INT) wParam;
114   INT prevItem;
115  
116   prevItem=-1;
117   if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
118     prevItem=infoPtr->iSelected;
119       infoPtr->iSelected=iItem;
120       TAB_EnsureSelectionVisible(hwnd, infoPtr);
121       TAB_InvalidateTabArea(hwnd, infoPtr);
122   }
123   return prevItem;
124 }
125
126 static LRESULT
127 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
128 {
129   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
130   INT iItem=(INT) wParam;
131  
132   if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
133
134   if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
135     FIXME("Should set input focus\n");
136   } else { 
137     if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) {
138       infoPtr->uFocus=iItem;
139       if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE)  {
140         infoPtr->iSelected = iItem;
141         TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
142
143         TAB_EnsureSelectionVisible(hwnd, infoPtr);
144         TAB_InvalidateTabArea(hwnd, infoPtr);
145       }
146     }
147   }
148   return 0;
149 }
150
151 static LRESULT
152 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
153 {
154     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
155
156     if (infoPtr == NULL) return 0;
157     infoPtr->hwndToolTip = (HWND)wParam;
158     return 0;
159 }
160
161 /******************************************************************************
162  * TAB_InternalGetItemRect
163  *
164  * This method will calculate the rectangle representing a given tab item in
165  * client coordinates. This method takes scrolling into account.
166  *
167  * This method returns TRUE if the item is visible in the window and FALSE
168  * if it is completely outside the client area.
169  */
170 static BOOL TAB_InternalGetItemRect(
171   HWND        hwnd,
172   TAB_INFO*   infoPtr,
173   INT         itemIndex,
174   RECT*       itemRect,
175   RECT*       selectedRect)
176 {
177   RECT tmpItemRect,clientRect;
178   LONG        lStyle  = GetWindowLongA(hwnd, GWL_STYLE);
179   
180   /*
181    * Perform a sanity check and a trivial visibility check.
182    */
183   if ( (infoPtr->uNumItem <=0) || 
184        (itemIndex >= infoPtr->uNumItem) ||
185        (!(lStyle &TCS_MULTILINE) && (itemIndex < infoPtr->leftmostVisible)) )
186     return FALSE;
187
188   /*
189    * Avoid special cases in this procedure by assigning the "out"
190    * parameters if the caller didn't supply them
191    */
192   if (itemRect==NULL)
193     itemRect = &tmpItemRect;
194   
195   /*
196    * Retrieve the unmodified item rect.
197    */
198   *itemRect = infoPtr->items[itemIndex].rect;
199
200   /*
201    * calculate the times bottom and top based on the row
202    */
203   GetClientRect(hwnd, &clientRect);
204
205   if (lStyle & TCS_BOTTOM) 
206   {
207     itemRect->bottom = clientRect.bottom - 
208                       SELECTED_TAB_OFFSET - 
209                       itemRect->top * (infoPtr->tabHeight - 2);
210
211     itemRect->top = clientRect.bottom - 
212                    infoPtr->tabHeight -
213                    itemRect->top * ( infoPtr->tabHeight - 2);
214   }
215   else 
216   {
217     itemRect->bottom = clientRect.top + 
218                       infoPtr->tabHeight +
219                       itemRect->top * (infoPtr->tabHeight - 2);
220     itemRect->top = clientRect.top + 
221                    SELECTED_TAB_OFFSET+
222                    itemRect->top * (infoPtr->tabHeight - 2);
223  }
224
225   /*
226    * "scroll" it to make sure the item at the very left of the 
227    * tab control is the leftmost visible tab.
228    */
229   OffsetRect(itemRect,
230              -infoPtr->items[infoPtr->leftmostVisible].rect.left, 
231              0);
232
233   /*
234    * Move the rectangle so the first item is slightly offset from 
235    * the left of the tab control.
236    */
237   OffsetRect(itemRect,
238              SELECTED_TAB_OFFSET,
239              0);
240
241
242   /*
243    * Now, calculate the position of the item as if it were selected.
244    */
245   if (selectedRect!=NULL)
246   {
247     CopyRect(selectedRect, itemRect);
248
249     /*
250      * The rectangle of a selected item is a bit wider.
251      */
252     InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
253
254     /*
255      * If it also a bit higher.
256      */
257     if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM) 
258     {      
259       selectedRect->top    -=2; /* the border is thicker on the bottom */
260       selectedRect->bottom +=SELECTED_TAB_OFFSET;
261     }
262     else
263     {
264       selectedRect->top   -=SELECTED_TAB_OFFSET;
265       selectedRect->bottom+=1;
266     }
267   }
268
269   return TRUE;
270 }
271
272 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
273 {
274   return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam, 
275                                  (LPRECT)lParam, (LPRECT)NULL);
276 }
277
278 /******************************************************************************
279  * TAB_KeyUp
280  *
281  * This method is called to handle keyboard input
282  */
283 static LRESULT TAB_KeyUp(
284   HWND   hwnd, 
285   WPARAM keyCode)
286 {
287   TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
288   int       newItem = -1;
289
290   switch (keyCode)
291   {
292     case VK_LEFT:
293       newItem = infoPtr->uFocus-1;
294       break;
295     case VK_RIGHT:
296       newItem = infoPtr->uFocus+1;
297       break;
298   }
299   
300   /*
301    * If we changed to a valid item, change the selection
302    */
303   if ( (newItem >= 0) &&
304        (newItem < infoPtr->uNumItem) &&
305        (infoPtr->uFocus != newItem) )
306   {
307     if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
308     {
309       infoPtr->iSelected = newItem;
310       infoPtr->uFocus    = newItem;
311       TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
312
313       TAB_EnsureSelectionVisible(hwnd, infoPtr);
314       TAB_InvalidateTabArea(hwnd, infoPtr);
315     }
316   }
317
318   return 0;
319 }
320
321 /******************************************************************************
322  * TAB_FocusChanging
323  *
324  * This method is called whenever the focus goes in or out of this control
325  * it is used to update the visual state of the control.
326  */
327 static LRESULT TAB_FocusChanging(
328   HWND   hwnd, 
329   UINT   uMsg, 
330   WPARAM wParam, 
331   LPARAM lParam)
332 {
333   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
334   RECT      selectedRect;
335   BOOL      isVisible;
336
337   /*
338    * Get the rectangle for the item.
339    */
340   isVisible = TAB_InternalGetItemRect(hwnd,
341                                       infoPtr,
342                                       infoPtr->uFocus,
343                                       NULL,
344                                       &selectedRect);
345   
346   /*
347    * If the rectangle is not completely invisible, invalidate that
348    * portion of the window.
349    */
350   if (isVisible)
351   {
352     InvalidateRect(hwnd, &selectedRect, TRUE);
353   }
354
355   /*
356    * Don't otherwise disturb normal behavior.
357    */
358   return DefWindowProcA (hwnd, uMsg, wParam, lParam);
359 }
360
361 static HWND TAB_InternalHitTest (
362   HWND      hwnd,
363   TAB_INFO* infoPtr, 
364   POINT     pt, 
365   UINT*     flags)
366
367 {
368   RECT rect;
369   int iCount; 
370   
371   for (iCount = 0; iCount < infoPtr->uNumItem; iCount++) 
372   {
373     TAB_InternalGetItemRect(hwnd,
374                             infoPtr, 
375                             iCount,
376                             &rect,
377                             NULL);
378
379     if (PtInRect (&rect, pt))
380     {
381       *flags = TCHT_ONITEM;
382       return iCount;
383     }
384   }
385
386   *flags=TCHT_NOWHERE;
387   return -1;
388 }
389
390 static LRESULT
391 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
392 {
393   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
394   LPTCHITTESTINFO lptest=(LPTCHITTESTINFO) lParam;
395   
396   return TAB_InternalHitTest (hwnd, infoPtr,lptest->pt,&lptest->flags);
397 }
398
399
400 static LRESULT
401 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
402 {
403   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
404   POINT pt;
405   INT newItem,dummy;
406
407   if (infoPtr->hwndToolTip)
408     TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
409                     WM_LBUTTONDOWN, wParam, lParam);
410
411   if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
412     SetFocus (hwnd);
413   }
414
415   if (infoPtr->hwndToolTip)
416     TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
417                     WM_LBUTTONDOWN, wParam, lParam);
418   
419   pt.x = (INT)LOWORD(lParam);
420   pt.y = (INT)HIWORD(lParam);
421   
422   newItem=TAB_InternalHitTest (hwnd, infoPtr,pt,&dummy);
423   
424   TRACE("On Tab, item %d\n", newItem);
425     
426   if ( (newItem!=-1) &&
427        (infoPtr->iSelected != newItem) )
428   {
429     if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE)
430     {
431       infoPtr->iSelected = newItem;
432       infoPtr->uFocus    = newItem;
433       TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
434
435       TAB_EnsureSelectionVisible(hwnd, infoPtr);
436
437       TAB_InvalidateTabArea(hwnd, infoPtr);
438     }
439   }
440   return 0;
441 }
442
443 static LRESULT
444 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
445 {
446   TAB_SendSimpleNotify(hwnd, NM_CLICK);
447
448   return 0;
449 }
450
451 static LRESULT
452 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
453 {
454   TAB_SendSimpleNotify(hwnd, NM_RCLICK);
455   return 0;
456 }
457
458 static LRESULT
459 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
460 {
461   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
462
463   if (infoPtr->hwndToolTip)
464     TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
465                     WM_LBUTTONDOWN, wParam, lParam);
466   return 0;
467 }
468
469 /******************************************************************************
470  * TAB_AdjustRect
471  *
472  * Calculates the tab control's display area given the windows rectangle or
473  * the window rectangle given the requested display rectangle.
474  */
475 static LRESULT TAB_AdjustRect(
476   HWND   hwnd, 
477   WPARAM fLarger, 
478   LPRECT prc)
479 {
480   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
481
482   if (fLarger) 
483   {
484     /*
485      * Go from display rectangle
486      */
487
488     /*
489      * Add the height of the tabs.
490      */
491     if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM) 
492       prc->bottom += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
493     else
494       prc->top -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
495
496     /*
497      * Inflate the rectangle for the padding
498      */
499     InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
500
501     /*
502      * Inflate for the border
503      */
504     InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
505   }
506   else 
507   {
508     /*
509      * Go from window rectangle.
510      */
511   
512     /*
513      * Deflate the rectangle for the border
514      */
515     InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
516
517     /*
518      * Deflate the rectangle for the padding
519      */
520     InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
521
522     /*
523      * Remove the height of the tabs.
524      */
525     if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM) 
526       prc->bottom -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
527     else
528       prc->top += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
529
530   }
531   
532   return 0;
533 }
534
535 /******************************************************************************
536  * TAB_OnHScroll
537  *
538  * This method will handle the notification from the scroll control and
539  * perform the scrolling operation on the tab control.
540  */
541 static LRESULT TAB_OnHScroll(
542   HWND    hwnd, 
543   int     nScrollCode,
544   int     nPos,
545   HWND    hwndScroll)
546 {
547   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
548
549   if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
550   {
551      if(nPos < infoPtr->leftmostVisible)
552         infoPtr->leftmostVisible--;
553      else
554         infoPtr->leftmostVisible++;
555
556      TAB_InvalidateTabArea(hwnd, infoPtr);
557      SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
558                    MAKELONG(infoPtr->leftmostVisible, 0));
559    }
560
561    return 0;
562 }
563
564 /******************************************************************************
565  * TAB_SetupScroling
566  *
567  * This method will check the current scrolling state and make sure the 
568  * scrolling control is displayed (or not).
569  */
570 static void TAB_SetupScrolling(
571   HWND        hwnd,
572   TAB_INFO*   infoPtr,
573   const RECT* clientRect)
574 {
575   INT maxRange = 0;
576   if (infoPtr->needsScrolling)
577   {
578     RECT controlPos;
579     INT vsize, tabwidth;
580
581     /*
582      * Calculate the position of the scroll control.
583      */
584     controlPos.right = clientRect->right;
585     controlPos.left  = controlPos.right - 2*GetSystemMetrics(SM_CXHSCROLL);
586
587     if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM) 
588     {
589       controlPos.top    = clientRect->bottom - infoPtr->tabHeight;
590       controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
591     }
592     else
593     {
594       controlPos.bottom = clientRect->top + infoPtr->tabHeight;
595       controlPos.top    = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
596     }
597
598     /*
599      * If we don't have a scroll control yet, we want to create one.
600      * If we have one, we want to make sure it's positioned right.
601      */
602     if (infoPtr->hwndUpDown==0)
603     {
604       infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
605                                           "",
606                                           WS_VISIBLE | WS_CHILD | UDS_HORZ,
607                                           controlPos.left, controlPos.top,
608                                           controlPos.right - controlPos.left,
609                                           controlPos.bottom - controlPos.top,
610                                           hwnd,
611                                           (HMENU)NULL, 
612                                           (HINSTANCE)NULL, 
613                                           NULL);        
614     }
615     else
616     {
617       SetWindowPos(infoPtr->hwndUpDown, 
618                    (HWND)NULL,
619                    controlPos.left, controlPos.top,
620                    controlPos.right - controlPos.left,
621                    controlPos.bottom - controlPos.top,
622                    SWP_SHOWWINDOW | SWP_NOZORDER);                 
623     }
624
625     /* Now calculate upper limit of the updown control range.
626      * We do this by calculating how many tabs will be offscreen when the
627      * last tab is visible.
628      */
629     if(infoPtr->uNumItem)
630     {
631        vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
632        maxRange = infoPtr->uNumItem;
633        tabwidth = infoPtr->items[maxRange-1].rect.right;
634
635        for(; maxRange > 0; maxRange--)
636        {
637           if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
638              break;
639        }
640
641        if(maxRange == infoPtr->uNumItem)
642           maxRange--;
643     }
644   }
645   else
646   {
647     /*
648      * If we once had a scroll control... hide it.
649      */
650     if (infoPtr->hwndUpDown!=0)
651     {
652       ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
653     }
654   }
655   if (infoPtr->hwndUpDown)
656      SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
657 }
658
659 /******************************************************************************
660  * TAB_SetItemBounds
661  *
662  * This method will calculate the position rectangles of all the items in the
663  * control. The rectangle calculated starts at 0 for the first item in the
664  * list and ignores scrolling and selection.
665  * It also uses the current font to determine the height of the tab row and
666  * it checks if all the tabs fit in the client area of the window. If they
667  * dont, a scrolling control is added.
668  */
669 static void TAB_SetItemBounds (HWND hwnd)
670 {
671   TAB_INFO*   infoPtr = TAB_GetInfoPtr(hwnd);
672   LONG        lStyle  = GetWindowLongA(hwnd, GWL_STYLE);
673   TEXTMETRICA fontMetrics;
674   INT         curItem;
675   INT         curItemLeftPos;
676   INT         curItemRowCount;
677   HFONT       hFont, hOldFont;
678   HDC         hdc;
679   RECT        clientRect;
680   SIZE        size;
681
682   /*
683    * We need to get text information so we need a DC and we need to select
684    * a font.
685    */
686   hdc = GetDC(hwnd); 
687     
688   hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
689   hOldFont = SelectObject (hdc, hFont);
690
691   /*
692    * We will base the rectangle calculations on the client rectangle
693    * of the control.
694    */
695   GetClientRect(hwnd, &clientRect);
696   
697   /*
698    * The leftmost item will be "0" aligned
699    */
700   curItemLeftPos = 0;
701   curItemRowCount = 0;
702
703   if ( !(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet) )
704   {
705     int item_height;
706     int icon_height = 0;
707
708     /*
709      * Use the current font to determine the height of a tab.
710      */
711     GetTextMetricsA(hdc, &fontMetrics);
712
713     /*
714      * Get the icon height
715      */
716     if (infoPtr->himl)
717       ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
718
719     /*
720      * Take the highest between font or icon
721      */
722     if (fontMetrics.tmHeight > icon_height)
723       item_height = fontMetrics.tmHeight;
724     else
725       item_height = icon_height;
726
727     /*
728      * Make sure there is enough space for the letters + icon + growing the 
729      * selected item + extra space for the selected item.   
730      */
731     infoPtr->tabHeight = item_height + 2*VERTICAL_ITEM_PADDING +  
732       SELECTED_TAB_OFFSET;
733   }
734
735   for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
736   {
737     /*
738      * Set the leftmost position of the tab.
739      */
740     infoPtr->items[curItem].rect.left = curItemLeftPos;
741
742     if ( (lStyle & TCS_FIXEDWIDTH) || ((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
743     {
744       infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
745                                            infoPtr->tabWidth +
746                                            2*HORIZONTAL_ITEM_PADDING;
747     }
748     else
749     {
750       int icon_width  = 0;
751       int num = 2;
752
753       /*
754        * Calculate how wide the tab is depending on the text it contains
755        */
756       GetTextExtentPoint32A(hdc, infoPtr->items[curItem].pszText, 
757                             lstrlenA(infoPtr->items[curItem].pszText), &size);
758
759       /*
760        * Add the icon width
761        */
762       if (infoPtr->himl)
763       {
764         ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
765         num++;
766       }
767
768       infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
769                                            size.cx + icon_width + 
770                                            num*HORIZONTAL_ITEM_PADDING;
771     }
772
773     /*
774      * Check if this is a multiline tab control and if so
775      * check to see if we should wrap the tabs
776      *
777      * Because we are going to arange all these tabs evenly
778      * really we are basically just counting rows at this point
779      *
780      */
781
782     if ((lStyle & TCS_MULTILINE)&&
783         (infoPtr->items[curItem].rect.right > clientRect.right))
784     {
785         infoPtr->items[curItem].rect.right -= 
786                                       infoPtr->items[curItem].rect.left;
787         infoPtr->items[curItem].rect.left = 0;
788         curItemRowCount ++;
789     }
790
791     infoPtr->items[curItem].rect.bottom = 0;
792     infoPtr->items[curItem].rect.top = curItemRowCount;
793
794     TRACE("TextSize: %i\n ", size.cx);
795     TRACE("Rect: T %i, L %i, B %i, R %i\n", 
796           infoPtr->items[curItem].rect.top,
797           infoPtr->items[curItem].rect.left,
798           infoPtr->items[curItem].rect.bottom,
799           infoPtr->items[curItem].rect.right);  
800
801     /*
802      * The leftmost position of the next item is the rightmost position
803      * of this one.
804      */
805     if (lStyle & TCS_BUTTONS)
806       curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX; 
807     else
808       curItemLeftPos = infoPtr->items[curItem].rect.right;
809   }
810
811   if (!(lStyle & TCS_MULTILINE))
812   {
813     /*
814      * Check if we need a scrolling control.
815      */
816     infoPtr->needsScrolling = (curItemLeftPos + (2*SELECTED_TAB_OFFSET) > 
817                                clientRect.right);
818
819     TAB_SetupScrolling(hwnd, infoPtr, &clientRect);      
820   }
821
822   /*
823    * Set the number of rows
824    */
825
826   infoPtr->uNumRows = curItemRowCount;
827
828    if ((lStyle & TCS_MULTILINE)&&(infoPtr->uNumItem > 0))
829    {
830       INT widthDiff,remainder;
831       INT tabPerRow,remTab;
832       INT iRow,iItm;
833       INT iIndexStart=0,iIndexEnd=0, iCount=0;
834
835       /*
836        * Ok Microsoft trys to even out the rows. place the same
837        * number of tabs in each row. So lets give that a shot
838        *
839        */
840
841       tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows + 1);
842       remTab = infoPtr->uNumItem % (infoPtr->uNumRows + 1);
843
844       for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
845            iItm<infoPtr->uNumItem;
846            iItm++,iCount++)
847       {
848           if (iCount >= ((iRow<remTab)?tabPerRow+1:tabPerRow))
849           {
850               iRow++;
851               curItemLeftPos = 0;
852               iCount = 0;
853           }
854           /*
855            * normalize the current rect
856            */
857           infoPtr->items[iItm].rect.right -= 
858             infoPtr->items[iItm].rect.left;
859           infoPtr->items[iItm].rect.left = 0;
860
861           infoPtr->items[iItm].rect.top = iRow;
862           infoPtr->items[iItm].rect.left +=curItemLeftPos;
863           infoPtr->items[iItm].rect.right +=curItemLeftPos; 
864           if (lStyle & TCS_BUTTONS)
865             curItemLeftPos = infoPtr->items[iItm].rect.right +
866                              BUTTON_SPACINGX;
867           else
868             curItemLeftPos = infoPtr->items[iItm].rect.right;
869       }
870           
871       /*
872        * Justify the rows
873        *
874        */
875       {
876          while(iIndexStart < infoPtr->uNumItem)
877         {
878         /* 
879          * find the indexs of the row
880          */
881         for (iIndexEnd=iIndexStart;
882              (iIndexEnd < infoPtr->uNumItem) && 
883                (infoPtr->items[iIndexEnd].rect.top ==
884                 infoPtr->items[iIndexStart].rect.top) ;
885             iIndexEnd++)
886         /* intentionaly blank */;
887
888         /* 
889          * we need to justify these tabs so they fill the whole given
890          * client area
891          *
892          */
893         widthDiff = clientRect.right - (2*SELECTED_TAB_OFFSET) -
894                             infoPtr->items[iIndexEnd-1].rect.right;
895        
896         iCount = iIndexEnd-iIndexStart; 
897
898         if (iCount)
899         {
900            INT iIndex;
901            remainder = widthDiff % iCount;
902            widthDiff = widthDiff / iCount;
903            for (iIndex=iIndexStart,iCount=0; iIndex < iIndexEnd; 
904                 iIndex++,iCount++)
905            {
906               infoPtr->items[iIndex].rect.left +=iCount*widthDiff;
907               infoPtr->items[iIndex].rect.right +=(iCount+1)*widthDiff;
908            }
909            infoPtr->items[iIndex-1].rect.right += remainder;
910         }
911
912         iIndexStart=iIndexEnd;
913         }
914       }
915   }
916
917   TAB_EnsureSelectionVisible(hwnd,infoPtr);
918   /*
919    * Cleanup
920    */
921   SelectObject (hdc, hOldFont);
922   ReleaseDC (hwnd, hdc);
923 }
924
925 /******************************************************************************
926  * TAB_DrawItem
927  *
928  * This method is used to draw a single tab into the tab control.
929  */         
930 static void TAB_DrawItem(
931   HWND hwnd, 
932   HDC  hdc, 
933   INT  iItem)
934 {
935   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
936   LONG      lStyle  = GetWindowLongA(hwnd, GWL_STYLE);
937   RECT      itemRect;
938   RECT      selectedRect;
939   BOOL      isVisible;
940   RECT      r;
941
942   /*
943    * Get the rectangle for the item.
944    */
945   isVisible = TAB_InternalGetItemRect(hwnd,
946                                       infoPtr,
947                                       iItem,
948                                       &itemRect,
949                                       &selectedRect);
950
951   if (isVisible)
952   {
953     HBRUSH hbr       = CreateSolidBrush (GetSysColor(COLOR_BTNFACE));    
954     HPEN   hwPen     = GetSysColorPen (COLOR_3DHILIGHT);
955     HPEN   hbPen     = GetSysColorPen (COLOR_BTNSHADOW);
956     HPEN   hsdPen    = GetSysColorPen (COLOR_BTNTEXT);
957     HPEN   hfocusPen = CreatePen(PS_DOT, 1, GetSysColor(COLOR_BTNTEXT));
958     HPEN   holdPen;
959     INT    oldBkMode;
960     INT    cx,cy; 
961     BOOL   deleteBrush = TRUE;
962
963     if (lStyle & TCS_BUTTONS)
964     {
965       /* 
966        * Get item rectangle.
967        */
968       r = itemRect;
969
970       holdPen = SelectObject (hdc, hwPen);
971
972       if (iItem == infoPtr->iSelected)
973       {
974         /* 
975          * Background color. 
976          */
977         if (!((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
978         {
979               COLORREF bk = GetSysColor(COLOR_3DHILIGHT);
980           DeleteObject(hbr);
981               hbr = GetSysColorBrush(COLOR_SCROLLBAR);
982               SetTextColor(hdc, GetSysColor(COLOR_3DFACE));
983               SetBkColor(hdc, bk);
984
985               /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
986                * we better use 0x55aa bitmap brush to make scrollbar's background
987                * look different from the window background.
988                */
989               if (bk == GetSysColor(COLOR_WINDOW))
990                   hbr = CACHE_GetPattern55AABrush();
991
992               deleteBrush = FALSE;
993         }
994
995         /*
996          * Erase the background.
997          */     
998         FillRect(hdc, &r, hbr);
999
1000         /*
1001          * Draw the tab now.
1002          * The rectangles calculated exclude the right and bottom
1003          * borders of the rectangle. To simply the following code, those
1004          * borders are shaved-off beforehand.
1005          */
1006         r.right--;
1007         r.bottom--;
1008
1009         /* highlight */
1010         MoveToEx (hdc, r.left, r.bottom, NULL);
1011         LineTo   (hdc, r.right, r.bottom);
1012         LineTo   (hdc, r.right, r.top);
1013         
1014         /* shadow */
1015         SelectObject(hdc, hbPen);
1016         LineTo  (hdc, r.left, r.top);
1017         LineTo  (hdc, r.left, r.bottom);
1018       }
1019       else
1020       {
1021         /*
1022          * Erase the background.
1023          */     
1024         FillRect(hdc, &r, hbr);
1025
1026         /* highlight */
1027         MoveToEx (hdc, r.left, r.bottom, NULL);
1028         LineTo   (hdc, r.left, r.top);
1029         LineTo   (hdc, r.right, r.top);
1030         
1031         /* shadow */
1032         SelectObject(hdc, hbPen);
1033         LineTo  (hdc, r.right, r.bottom);
1034         LineTo  (hdc, r.left, r.bottom);
1035       }
1036     }
1037     else
1038     {
1039       /* 
1040        * Background color. 
1041        */
1042       DeleteObject(hbr);
1043       hbr = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));    
1044
1045       /*
1046        * We draw a rectangle of different sizes depending on the selection
1047        * state.
1048        */
1049       if (iItem == infoPtr->iSelected)
1050         r = selectedRect;
1051       else
1052         r = itemRect;
1053
1054       /*
1055        * Erase the background.
1056        * This is necessary when drawing the selected item since it is larger 
1057        * than the others, it might overlap with stuff already drawn by the 
1058        * other tabs
1059        */     
1060       FillRect(hdc, &r, hbr);
1061
1062       /*
1063        * Draw the tab now.
1064        * The rectangles calculated exclude the right and bottom
1065        * borders of the rectangle. To simply the following code, those
1066        * borders are shaved-off beforehand.
1067        */
1068       r.right--;
1069       r.bottom--;
1070       
1071       holdPen = SelectObject (hdc, hwPen);
1072
1073       if (lStyle & TCS_BOTTOM) 
1074       {
1075         /* highlight */
1076         MoveToEx (hdc, r.left, r.top, NULL);
1077         LineTo   (hdc, r.left, r.bottom - ROUND_CORNER_SIZE);
1078         LineTo   (hdc, r.left + ROUND_CORNER_SIZE, r.bottom);
1079         
1080         /* shadow */
1081         SelectObject(hdc, hbPen);
1082         LineTo  (hdc, r.right - ROUND_CORNER_SIZE, r.bottom);
1083         LineTo  (hdc, r.right, r.bottom - ROUND_CORNER_SIZE);
1084         LineTo  (hdc, r.right, r.top);
1085       }
1086       else 
1087       {
1088         /* highlight */
1089         MoveToEx (hdc, r.left, r.bottom, NULL);
1090         LineTo   (hdc, r.left, r.top + ROUND_CORNER_SIZE);
1091         LineTo   (hdc, r.left + ROUND_CORNER_SIZE, r.top);
1092         LineTo   (hdc, r.right - ROUND_CORNER_SIZE, r.top);
1093         
1094         /* shadow */
1095         SelectObject(hdc, hbPen);
1096         LineTo (hdc, r.right,  r.top + ROUND_CORNER_SIZE);
1097         LineTo (hdc, r.right,  r.bottom);
1098       }
1099     }
1100   
1101     /*
1102      * Text pen
1103      */
1104     SelectObject(hdc, hsdPen); 
1105
1106     oldBkMode = SetBkMode(hdc, TRANSPARENT); 
1107     SetTextColor (hdc, COLOR_BTNTEXT);
1108
1109     /*
1110      * Deflate the rectangle to acount for the padding
1111      */
1112     InflateRect(&r, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
1113
1114     /*
1115      * if owner draw, tell the owner to draw
1116      */
1117     if ( (lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd) )
1118     {
1119         DRAWITEMSTRUCT dis;
1120         WND *pwndPtr;
1121         UINT id;
1122
1123         /*
1124          * get the control id
1125          */
1126         pwndPtr = WIN_FindWndPtr( hwnd );
1127         id = pwndPtr->wIDmenu;
1128         WIN_ReleaseWndPtr(pwndPtr);
1129
1130         /* 
1131          * put together the DRAWITEMSTRUCT
1132          */
1133         dis.CtlType    = ODT_TAB;       
1134         dis.CtlID      = id;            
1135         dis.itemID     = iItem;         
1136         dis.itemAction = ODA_DRAWENTIRE;        
1137         if ( iItem == infoPtr->iSelected )
1138             dis.itemState = ODS_SELECTED;       
1139         else                            
1140             dis.itemState = 0;          
1141         dis.hwndItem = hwnd;            /* */
1142         dis.hDC      = hdc;             
1143         dis.rcItem   = r;               /* */
1144         dis.itemData = infoPtr->items[iItem].lParam;
1145
1146         /*
1147          * send the draw message
1148          */
1149         SendMessageA( GetParent(hwnd), WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1150     }
1151     else
1152     {
1153         /*
1154          * If not owner draw, then do the drawing ourselves.
1155          *
1156      * Draw the icon.
1157      */
1158     if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE) )
1159     {
1160       ImageList_Draw (infoPtr->himl, infoPtr->items[iItem].iImage, hdc, 
1161                       r.left, r.top+1, ILD_NORMAL);
1162       ImageList_GetIconSize (infoPtr->himl, &cx, &cy);
1163       r.left+=(cx + HORIZONTAL_ITEM_PADDING);
1164     }
1165
1166     /*
1167      * Draw the text;
1168      */
1169     if (lStyle & TCS_RIGHTJUSTIFY)
1170       DrawTextA(hdc,
1171               infoPtr->items[iItem].pszText, 
1172               lstrlenA(infoPtr->items[iItem].pszText),
1173               &r, 
1174               DT_CENTER|DT_SINGLELINE|DT_VCENTER);
1175     else
1176        DrawTextA(hdc,
1177               infoPtr->items[iItem].pszText, 
1178               lstrlenA(infoPtr->items[iItem].pszText),
1179               &r, 
1180               DT_LEFT|DT_SINGLELINE|DT_VCENTER);
1181     }
1182
1183     /*
1184      * Draw the focus rectangle
1185      */
1186     if (((lStyle & TCS_FOCUSNEVER) == 0) &&
1187          (GetFocus() == hwnd) &&
1188          (iItem == infoPtr->uFocus) )
1189     {
1190       InflateRect(&r, FOCUS_RECT_HOFFSET, FOCUS_RECT_VOFFSET);
1191
1192       SelectObject(hdc, hfocusPen);
1193
1194       MoveToEx (hdc, r.left,    r.top, NULL);
1195       LineTo   (hdc, r.right-1, r.top); 
1196       LineTo   (hdc, r.right-1, r.bottom -1);
1197       LineTo   (hdc, r.left,    r.bottom -1);
1198       LineTo   (hdc, r.left,    r.top);
1199     }
1200
1201     /*
1202      * Cleanup
1203      */
1204     SetBkMode(hdc, oldBkMode);
1205     SelectObject(hdc, holdPen);
1206     DeleteObject(hfocusPen);
1207     if (deleteBrush) DeleteObject(hbr);
1208   }
1209 }
1210
1211 /******************************************************************************
1212  * TAB_DrawBorder
1213  *
1214  * This method is used to draw the raised border around the tab control
1215  * "content" area.
1216  */         
1217 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
1218 {
1219   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1220   HPEN htmPen;
1221   HPEN hwPen  = GetSysColorPen (COLOR_3DHILIGHT);
1222   HPEN hbPen  = GetSysColorPen (COLOR_3DDKSHADOW);
1223   HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW);
1224   RECT rect;
1225
1226   GetClientRect (hwnd, &rect);
1227
1228   /*
1229    * Adjust for the style
1230    */
1231   if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM) 
1232   {
1233     rect.bottom -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
1234   }
1235   else
1236   {
1237     rect.top += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
1238   }
1239
1240   /*
1241    * Shave-off the right and bottom margins (exluded in the
1242    * rect)
1243    */
1244   rect.right--;
1245   rect.bottom--;
1246
1247   /* highlight */
1248   htmPen = SelectObject (hdc, hwPen);
1249   
1250   MoveToEx (hdc, rect.left, rect.bottom, NULL);
1251   LineTo (hdc, rect.left, rect.top); 
1252   LineTo (hdc, rect.right, rect.top);
1253
1254   /* Dark Shadow */
1255   SelectObject (hdc, hbPen);
1256   LineTo (hdc, rect.right, rect.bottom );
1257   LineTo (hdc, rect.left, rect.bottom);
1258
1259   /* shade */
1260   SelectObject (hdc, hShade );
1261   MoveToEx (hdc, rect.right-1, rect.top, NULL);
1262   LineTo   (hdc, rect.right-1, rect.bottom-1);
1263   LineTo   (hdc, rect.left,    rect.bottom-1);
1264
1265   SelectObject(hdc, htmPen);
1266 }
1267
1268 /******************************************************************************
1269  * TAB_Refresh
1270  *
1271  * This method repaints the tab control..
1272  */             
1273 static void TAB_Refresh (HWND hwnd, HDC hdc)
1274 {
1275   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1276   HFONT hOldFont;
1277   INT i;
1278
1279   if (!infoPtr->DoRedraw)
1280     return;
1281
1282   hOldFont = SelectObject (hdc, infoPtr->hFont);
1283
1284   if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
1285   {
1286     for (i = 0; i < infoPtr->uNumItem; i++) 
1287     {
1288         TAB_DrawItem (hwnd, hdc, i);
1289     }
1290   }
1291   else
1292   {
1293     /*
1294      * Draw all the non selected item first.
1295      */
1296     for (i = 0; i < infoPtr->uNumItem; i++) 
1297     {
1298       if (i != infoPtr->iSelected)
1299         TAB_DrawItem (hwnd, hdc, i);
1300     }
1301
1302     /*
1303      * Now, draw the border, draw it before the selected item
1304      * since the selected item overwrites part of the border.
1305      */
1306     TAB_DrawBorder (hwnd, hdc);
1307
1308     /*
1309      * Then, draw the selected item
1310      */
1311     TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
1312
1313     /*
1314      * If we haven't set the current focus yet, set it now.
1315      * Only happens when we first paint the tab controls.
1316      */
1317    if (infoPtr->uFocus == -1)
1318      TAB_SetCurFocus(hwnd, infoPtr->iSelected);
1319   }
1320
1321   SelectObject (hdc, hOldFont);
1322 }
1323
1324 static LRESULT
1325 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
1326 {
1327     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1328   
1329   infoPtr->DoRedraw=(BOOL) wParam;
1330   return 0;
1331 }
1332
1333 static LRESULT TAB_EraseBackground(
1334   HWND hwnd, 
1335   HDC  givenDC)
1336 {
1337   HDC  hdc;
1338   RECT clientRect;
1339
1340   HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1341
1342   hdc = givenDC ? givenDC : GetDC(hwnd);
1343
1344   GetClientRect(hwnd, &clientRect);
1345
1346   FillRect(hdc, &clientRect, brush);
1347
1348   if (givenDC==0)
1349     ReleaseDC(hwnd, hdc);
1350
1351   DeleteObject(brush);
1352
1353   return 0;
1354 }
1355
1356 /******************************************************************************
1357  * TAB_EnsureSelectionVisible
1358  *
1359  * This method will make sure that the current selection is completely
1360  * visible by scrolling until it is.
1361  */
1362 static void TAB_EnsureSelectionVisible(
1363   HWND      hwnd,
1364   TAB_INFO* infoPtr)
1365 {
1366   INT iSelected = infoPtr->iSelected;
1367
1368   /* 
1369    * set the items row to the bottommost row or topmost row depending on 
1370    * style 
1371    */
1372
1373   if (infoPtr->uNumRows > 0)
1374   {
1375       INT newselected=infoPtr->items[iSelected].rect.top;
1376       INT iTargetRow;
1377       LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1378
1379       if (lStyle & TCS_BOTTOM)
1380           iTargetRow = 0;
1381       else
1382           iTargetRow = infoPtr->uNumRows;
1383
1384       if (newselected != iTargetRow)
1385       {
1386          INT i;
1387          for (i=0; i < infoPtr->uNumItem; i++)
1388              if (infoPtr->items[i].rect.top == newselected )
1389                  infoPtr->items[i].rect.top = iTargetRow;
1390              else if (lStyle&TCS_BOTTOM)
1391                  {
1392                   if (infoPtr->items[i].rect.top < newselected)
1393                      infoPtr->items[i].rect.top+=1;
1394                  }
1395                  else 
1396                  {
1397                    if (infoPtr->items[i].rect.top > newselected)
1398                      infoPtr->items[i].rect.top-=1;
1399                  }
1400       }
1401   }
1402
1403   /*
1404    * Do the trivial cases first.
1405    */
1406   if ( (!infoPtr->needsScrolling) ||
1407        (infoPtr->hwndUpDown==0) )
1408     return;
1409
1410   if (infoPtr->leftmostVisible >= iSelected)
1411   {
1412     infoPtr->leftmostVisible = iSelected;
1413   }
1414   else
1415   {
1416      RECT r;
1417      INT  width, i;
1418      /*
1419       * Calculate the part of the client area that is visible.
1420       */
1421      GetClientRect(hwnd, &r);
1422      width = r.right;
1423
1424      GetClientRect(infoPtr->hwndUpDown, &r);
1425      width -= r.right;
1426
1427      if ((infoPtr->items[iSelected].rect.right -
1428           infoPtr->items[iSelected].rect.left) >= width )
1429      {
1430         /* Special case: width of selected item is greater than visible
1431          * part of control.
1432          */
1433         infoPtr->leftmostVisible = iSelected;
1434      }
1435      else
1436      {
1437         for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
1438         {
1439            if ((infoPtr->items[iSelected].rect.right -
1440                 infoPtr->items[i].rect.left) < width)
1441               break;
1442         }
1443         infoPtr->leftmostVisible = i;
1444      }
1445   }
1446
1447   SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
1448                MAKELONG(infoPtr->leftmostVisible, 0));
1449 }
1450
1451 /******************************************************************************
1452  * TAB_InvalidateTabArea
1453  *
1454  * This method will invalidate the portion of the control that contains the
1455  * tabs. It is called when the state of the control changes and needs
1456  * to be redisplayed
1457  */
1458 static void TAB_InvalidateTabArea(
1459   HWND      hwnd,
1460   TAB_INFO* infoPtr)
1461 {
1462   RECT clientRect;
1463
1464   GetClientRect(hwnd, &clientRect);
1465
1466   if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM) 
1467   {
1468     clientRect.top = clientRect.bottom - (infoPtr->tabHeight * 
1469                                            (infoPtr->uNumRows + 1) + 3);
1470   }
1471   else
1472   {
1473     clientRect.bottom = clientRect.top + (infoPtr->tabHeight * 
1474                                            (infoPtr->uNumRows + 1) + 1);
1475   }
1476
1477   InvalidateRect(hwnd, &clientRect, TRUE);
1478 }
1479
1480 static LRESULT
1481 TAB_Paint (HWND hwnd, WPARAM wParam)
1482 {
1483   HDC hdc;
1484   PAINTSTRUCT ps;
1485     
1486   hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
1487   TAB_Refresh (hwnd, hdc);
1488     
1489   if(!wParam)
1490     EndPaint (hwnd, &ps);
1491
1492   return 0;
1493 }
1494
1495 static LRESULT
1496 TAB_InsertItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1497 {    
1498   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1499   TCITEMA *pti;
1500   INT iItem, len;
1501   RECT rect;
1502   
1503   GetClientRect (hwnd, &rect);
1504   TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
1505         rect.top, rect.left, rect.bottom, rect.right);  
1506   
1507   pti = (TCITEMA *)lParam;
1508   iItem = (INT)wParam;
1509   
1510   if (iItem < 0) return -1;
1511   if (iItem > infoPtr->uNumItem)
1512     iItem = infoPtr->uNumItem;
1513   
1514   if (infoPtr->uNumItem == 0) {
1515     infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
1516     infoPtr->uNumItem++;
1517     infoPtr->iSelected = 0;
1518   }
1519   else {
1520     TAB_ITEM *oldItems = infoPtr->items;
1521     
1522     infoPtr->uNumItem++;
1523     infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
1524     
1525     /* pre insert copy */
1526     if (iItem > 0) {
1527       memcpy (&infoPtr->items[0], &oldItems[0],
1528               iItem * sizeof(TAB_ITEM));
1529     }
1530     
1531     /* post insert copy */
1532     if (iItem < infoPtr->uNumItem - 1) {
1533       memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
1534               (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
1535       
1536     }
1537
1538     if (iItem <= infoPtr->iSelected)
1539       infoPtr->iSelected++;
1540
1541     COMCTL32_Free (oldItems);
1542   }
1543   
1544   infoPtr->items[iItem].mask = pti->mask;
1545   if (pti->mask & TCIF_TEXT) {
1546     len = lstrlenA (pti->pszText);
1547     infoPtr->items[iItem].pszText = COMCTL32_Alloc (len+1);
1548     lstrcpyA (infoPtr->items[iItem].pszText, pti->pszText);
1549     infoPtr->items[iItem].cchTextMax = pti->cchTextMax;
1550   }
1551   
1552   if (pti->mask & TCIF_IMAGE)
1553     infoPtr->items[iItem].iImage = pti->iImage;
1554   
1555   if (pti->mask & TCIF_PARAM)
1556     infoPtr->items[iItem].lParam = pti->lParam;
1557   
1558   TAB_InvalidateTabArea(hwnd, infoPtr);
1559   
1560   TRACE("[%04x]: added item %d '%s'\n",
1561         hwnd, iItem, infoPtr->items[iItem].pszText);
1562
1563   TAB_SetItemBounds(hwnd);
1564   return iItem;
1565 }
1566
1567 static LRESULT 
1568 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
1569 {
1570   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1571   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1572   LONG lResult = 0;
1573
1574   if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
1575   {
1576     lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
1577     infoPtr->tabWidth = (INT)LOWORD(lParam);
1578     infoPtr->tabHeight = (INT)HIWORD(lParam);
1579   }
1580   infoPtr->fSizeSet = TRUE;
1581
1582   return lResult;
1583 }
1584
1585 static LRESULT 
1586 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1587 {
1588   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1589   TCITEMA *tabItem; 
1590   TAB_ITEM *wineItem; 
1591   INT    iItem,len;
1592
1593   iItem=(INT) wParam;
1594   tabItem=(LPTCITEMA ) lParam;
1595   TRACE("%d %p\n",iItem, tabItem);
1596   if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
1597
1598   wineItem=& infoPtr->items[iItem];
1599
1600   if (tabItem->mask & TCIF_IMAGE)
1601     wineItem->iImage=tabItem->iImage;
1602
1603   if (tabItem->mask & TCIF_PARAM)
1604     wineItem->lParam=tabItem->lParam;
1605
1606   if (tabItem->mask & TCIF_RTLREADING) 
1607     FIXME("TCIF_RTLREADING\n");
1608
1609   if (tabItem->mask & TCIF_STATE) 
1610     wineItem->dwState=tabItem->dwState;
1611
1612   if (tabItem->mask & TCIF_TEXT) {
1613    len=lstrlenA (tabItem->pszText);
1614    if (len>wineItem->cchTextMax) 
1615      wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
1616    lstrcpyA (wineItem->pszText, tabItem->pszText);
1617   }
1618
1619   /*
1620    * Update and repaint tabs.
1621    */
1622   TAB_SetItemBounds(hwnd);
1623   TAB_InvalidateTabArea(hwnd,infoPtr);
1624
1625   return TRUE;
1626 }
1627
1628 static LRESULT 
1629 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1630 {
1631    TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1632
1633    return infoPtr->uNumItem;
1634 }
1635
1636
1637 static LRESULT 
1638 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1639 {
1640    TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1641    TCITEMA *tabItem;
1642    TAB_ITEM *wineItem;
1643    INT    iItem;
1644
1645   iItem=(INT) wParam;
1646   tabItem=(LPTCITEMA) lParam;
1647   TRACE("\n");
1648   if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
1649
1650   wineItem=& infoPtr->items[iItem];
1651
1652   if (tabItem->mask & TCIF_IMAGE) 
1653     tabItem->iImage=wineItem->iImage;
1654
1655   if (tabItem->mask & TCIF_PARAM) 
1656     tabItem->lParam=wineItem->lParam;
1657
1658   if (tabItem->mask & TCIF_RTLREADING) 
1659     FIXME("TCIF_RTLREADING\n");
1660
1661   if (tabItem->mask & TCIF_STATE) 
1662     tabItem->dwState=wineItem->dwState;
1663
1664   if (tabItem->mask & TCIF_TEXT) 
1665    lstrcpynA (tabItem->pszText, wineItem->pszText, tabItem->cchTextMax);
1666
1667   return TRUE;
1668 }
1669
1670 static LRESULT 
1671 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1672 {
1673   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1674   INT iItem = (INT) wParam;
1675   BOOL bResult = FALSE;
1676
1677   if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
1678   {
1679     TAB_ITEM *oldItems = infoPtr->items;
1680     
1681     infoPtr->uNumItem--;
1682     infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
1683     
1684     if (iItem > 0) 
1685       memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
1686     
1687     if (iItem < infoPtr->uNumItem) 
1688       memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
1689               (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
1690     
1691     COMCTL32_Free (oldItems);
1692
1693     /*
1694      * Readjust the selected index.
1695      */
1696     if ((iItem == infoPtr->iSelected) && (iItem > 0))
1697       infoPtr->iSelected--;
1698       
1699     if (iItem < infoPtr->iSelected)
1700       infoPtr->iSelected--;
1701
1702     if (infoPtr->uNumItem == 0)
1703       infoPtr->iSelected = -1;
1704
1705     /*
1706      * Reposition and repaint tabs.
1707      */
1708     TAB_SetItemBounds(hwnd);
1709     TAB_InvalidateTabArea(hwnd,infoPtr);
1710
1711     bResult = TRUE;
1712   }
1713
1714   return bResult;
1715 }
1716
1717 static LRESULT 
1718 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
1719 {
1720    TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1721
1722   COMCTL32_Free (infoPtr->items);
1723   infoPtr->uNumItem = 0;
1724   infoPtr->iSelected = -1;
1725  
1726   TAB_SetItemBounds(hwnd);
1727   TAB_InvalidateTabArea(hwnd,infoPtr);
1728   return TRUE;
1729 }
1730
1731
1732 static LRESULT
1733 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1734 {
1735   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1736
1737   TRACE("\n");
1738   return (LRESULT)infoPtr->hFont;
1739 }
1740
1741 static LRESULT
1742 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1743
1744 {
1745   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1746   
1747   TRACE("%x %lx\n",wParam, lParam);
1748   
1749   infoPtr->hFont = (HFONT)wParam;
1750   
1751   TAB_SetItemBounds(hwnd);
1752
1753   TAB_InvalidateTabArea(hwnd, infoPtr);
1754
1755   return 0;
1756 }
1757
1758
1759 static LRESULT
1760 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1761 {
1762   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1763
1764   TRACE("\n");
1765   return (LRESULT)infoPtr->himl;
1766 }
1767
1768 static LRESULT
1769 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1770 {
1771     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1772     HIMAGELIST himlPrev;
1773
1774     TRACE("\n");
1775     himlPrev = infoPtr->himl;
1776     infoPtr->himl= (HIMAGELIST)lParam;
1777     return (LRESULT)himlPrev;
1778 }
1779
1780
1781 static LRESULT
1782 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1783
1784 {
1785 /* I'm not really sure what the following code was meant to do.
1786    This is what it is doing:
1787    When WM_SIZE is sent with SIZE_RESTORED, the control
1788    gets positioned in the top left corner.
1789
1790   RECT parent_rect;
1791   HWND parent;
1792   UINT uPosFlags,cx,cy;
1793
1794   uPosFlags=0;
1795   if (!wParam) {
1796     parent = GetParent (hwnd);
1797     GetClientRect(parent, &parent_rect);
1798     cx=LOWORD (lParam);
1799     cy=HIWORD (lParam);
1800     if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE) 
1801         uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
1802
1803     SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
1804             cx, cy, uPosFlags | SWP_NOZORDER);
1805   } else {
1806     FIXME (tab,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
1807   } */
1808
1809   /*
1810    * Recompute the size/position of the tabs.
1811    */
1812   TAB_SetItemBounds (hwnd);
1813
1814   /*
1815    * Force a repaint of the control.
1816    */
1817   InvalidateRect(hwnd, NULL, TRUE);
1818
1819   return 0;
1820 }
1821
1822
1823 static LRESULT 
1824 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
1825 {
1826   TAB_INFO *infoPtr;
1827   TEXTMETRICA fontMetrics;
1828   HDC hdc;
1829   HFONT hOldFont;
1830   DWORD dwStyle;
1831
1832   infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
1833
1834   SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
1835    
1836   infoPtr->uNumItem        = 0;
1837   infoPtr->uNumRows        = 0;
1838   infoPtr->hFont           = 0;
1839   infoPtr->items           = 0;
1840   infoPtr->hcurArrow       = LoadCursorA (0, IDC_ARROWA);
1841   infoPtr->iSelected       = -1;
1842   infoPtr->uFocus          = -1;  
1843   infoPtr->hwndToolTip     = 0;
1844   infoPtr->DoRedraw        = TRUE;
1845   infoPtr->needsScrolling  = FALSE;
1846   infoPtr->hwndUpDown      = 0;
1847   infoPtr->leftmostVisible = 0;
1848   infoPtr->fSizeSet        = FALSE;
1849   
1850   TRACE("Created tab control, hwnd [%04x]\n", hwnd); 
1851
1852   /* The tab control always has the WS_CLIPSIBLINGS style. Even 
1853      if you don't specify in CreateWindow. This is necesary in 
1854      order for paint to work correctly. This follows windows behaviour. */
1855   dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1856   SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
1857
1858   if (dwStyle & TCS_TOOLTIPS) {
1859     /* Create tooltip control */
1860     infoPtr->hwndToolTip =
1861       CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
1862                        CW_USEDEFAULT, CW_USEDEFAULT,
1863                        CW_USEDEFAULT, CW_USEDEFAULT,
1864                        hwnd, 0, 0, 0);
1865     
1866     /* Send NM_TOOLTIPSCREATED notification */
1867     if (infoPtr->hwndToolTip) {
1868       NMTOOLTIPSCREATED nmttc;
1869       
1870       nmttc.hdr.hwndFrom = hwnd;
1871       nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1872       nmttc.hdr.code = NM_TOOLTIPSCREATED;
1873       nmttc.hwndToolTips = infoPtr->hwndToolTip;
1874       
1875       SendMessageA (GetParent (hwnd), WM_NOTIFY,
1876                     (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
1877     }
1878   }  
1879     
1880   /*
1881    * We need to get text information so we need a DC and we need to select
1882    * a font.
1883    */
1884   hdc = GetDC(hwnd); 
1885   hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
1886
1887   /*
1888    * Use the system font to determine the initial height of a tab.
1889    */
1890   GetTextMetricsA(hdc, &fontMetrics);
1891
1892   /*
1893    * Make sure there is enough space for the letters + growing the 
1894    * selected item + extra space for the selected item.   
1895    */
1896   infoPtr->tabHeight = fontMetrics.tmHeight + 2*VERTICAL_ITEM_PADDING +  
1897                        SELECTED_TAB_OFFSET;
1898
1899   /*
1900    * Initialize the width of a tab.
1901    */
1902   infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
1903
1904   SelectObject (hdc, hOldFont);
1905   ReleaseDC(hwnd, hdc);
1906
1907   return 0;
1908 }
1909
1910 static LRESULT
1911 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
1912 {
1913   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1914   INT iItem;
1915
1916   if (!infoPtr)
1917       return 0;
1918
1919   if (infoPtr->items) {
1920     for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
1921       if (infoPtr->items[iItem].pszText)
1922         COMCTL32_Free (infoPtr->items[iItem].pszText);
1923     }
1924     COMCTL32_Free (infoPtr->items);
1925   }
1926   
1927   if (infoPtr->hwndToolTip) 
1928     DestroyWindow (infoPtr->hwndToolTip);
1929  
1930   if (infoPtr->hwndUpDown)
1931     DestroyWindow(infoPtr->hwndUpDown);
1932
1933   COMCTL32_Free (infoPtr);
1934   return 0;
1935 }
1936
1937 static LRESULT WINAPI
1938 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1939 {
1940     switch (uMsg)
1941     {
1942     case TCM_GETIMAGELIST:
1943       return TAB_GetImageList (hwnd, wParam, lParam);
1944       
1945     case TCM_SETIMAGELIST:
1946       return TAB_SetImageList (hwnd, wParam, lParam);
1947       
1948     case TCM_GETITEMCOUNT:
1949       return TAB_GetItemCount (hwnd, wParam, lParam);
1950       
1951     case TCM_GETITEMA:
1952       return TAB_GetItemA (hwnd, wParam, lParam);
1953       
1954     case TCM_GETITEMW:
1955       FIXME("Unimplemented msg TCM_GETITEMW\n");
1956       return 0;
1957       
1958     case TCM_SETITEMA:
1959       return TAB_SetItemA (hwnd, wParam, lParam);
1960       
1961     case TCM_SETITEMW:
1962       FIXME("Unimplemented msg TCM_SETITEMW\n");
1963       return 0;
1964       
1965     case TCM_DELETEITEM:
1966       return TAB_DeleteItem (hwnd, wParam, lParam);
1967       
1968     case TCM_DELETEALLITEMS:
1969      return TAB_DeleteAllItems (hwnd, wParam, lParam);
1970      
1971     case TCM_GETITEMRECT:
1972      return TAB_GetItemRect (hwnd, wParam, lParam);
1973       
1974     case TCM_GETCURSEL:
1975       return TAB_GetCurSel (hwnd);
1976       
1977     case TCM_HITTEST:
1978       return TAB_HitTest (hwnd, wParam, lParam);
1979       
1980     case TCM_SETCURSEL:
1981       return TAB_SetCurSel (hwnd, wParam);
1982       
1983     case TCM_INSERTITEMA:
1984       return TAB_InsertItem (hwnd, wParam, lParam);
1985       
1986     case TCM_INSERTITEMW:
1987       FIXME("Unimplemented msg TCM_INSERTITEMW\n");
1988       return 0;
1989       
1990     case TCM_SETITEMEXTRA:
1991       FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
1992       return 0;
1993       
1994     case TCM_ADJUSTRECT:
1995       return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
1996       
1997     case TCM_SETITEMSIZE:
1998       return TAB_SetItemSize (hwnd, wParam, lParam);
1999       
2000     case TCM_REMOVEIMAGE:
2001       FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
2002       return 0;
2003       
2004     case TCM_SETPADDING:
2005       FIXME("Unimplemented msg TCM_SETPADDING\n");
2006       return 0;
2007       
2008     case TCM_GETROWCOUNT:
2009       FIXME("Unimplemented msg TCM_GETROWCOUNT\n");
2010       return 0;
2011
2012     case TCM_GETUNICODEFORMAT:
2013       FIXME("Unimplemented msg TCM_GETUNICODEFORMAT\n");
2014       return 0;
2015
2016     case TCM_SETUNICODEFORMAT:
2017       FIXME("Unimplemented msg TCM_SETUNICODEFORMAT\n");
2018       return 0;
2019
2020     case TCM_HIGHLIGHTITEM:
2021       FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
2022       return 0;
2023       
2024     case TCM_GETTOOLTIPS:
2025       return TAB_GetToolTips (hwnd, wParam, lParam);
2026       
2027     case TCM_SETTOOLTIPS:
2028       return TAB_SetToolTips (hwnd, wParam, lParam);
2029       
2030     case TCM_GETCURFOCUS:
2031       return TAB_GetCurFocus (hwnd);
2032       
2033     case TCM_SETCURFOCUS:
2034       return TAB_SetCurFocus (hwnd, wParam);
2035       
2036     case TCM_SETMINTABWIDTH:
2037       FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
2038       return 0;
2039       
2040     case TCM_DESELECTALL:
2041       FIXME("Unimplemented msg TCM_DESELECTALL\n");
2042       return 0;
2043       
2044     case TCM_GETEXTENDEDSTYLE:
2045       FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
2046       return 0;
2047
2048     case TCM_SETEXTENDEDSTYLE:
2049       FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
2050       return 0;
2051
2052     case WM_GETFONT:
2053       return TAB_GetFont (hwnd, wParam, lParam);
2054       
2055     case WM_SETFONT:
2056       return TAB_SetFont (hwnd, wParam, lParam);
2057       
2058     case WM_CREATE:
2059       return TAB_Create (hwnd, wParam, lParam);
2060       
2061     case WM_NCDESTROY:
2062       return TAB_Destroy (hwnd, wParam, lParam);
2063       
2064     case WM_GETDLGCODE:
2065       return DLGC_WANTARROWS | DLGC_WANTCHARS;
2066       
2067     case WM_LBUTTONDOWN:
2068       return TAB_LButtonDown (hwnd, wParam, lParam);
2069       
2070     case WM_LBUTTONUP:
2071       return TAB_LButtonUp (hwnd, wParam, lParam);
2072       
2073     case WM_RBUTTONDOWN:
2074       return TAB_RButtonDown (hwnd, wParam, lParam);
2075       
2076     case WM_MOUSEMOVE:
2077       return TAB_MouseMove (hwnd, wParam, lParam);
2078       
2079     case WM_ERASEBKGND:
2080       return TAB_EraseBackground (hwnd, (HDC)wParam);
2081
2082     case WM_PAINT:
2083       return TAB_Paint (hwnd, wParam);
2084
2085     case WM_SIZE:
2086       return TAB_Size (hwnd, wParam, lParam);
2087       
2088     case WM_SETREDRAW:
2089       return TAB_SetRedraw (hwnd, wParam);
2090
2091     case WM_HSCROLL:
2092       return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
2093
2094     case WM_STYLECHANGED:
2095       TAB_SetItemBounds (hwnd);
2096       InvalidateRect(hwnd, NULL, TRUE);
2097       return 0;
2098       
2099     case WM_KILLFOCUS:
2100     case WM_SETFOCUS:
2101       return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
2102
2103     case WM_KEYUP:
2104       return TAB_KeyUp(hwnd, wParam);
2105
2106     default:
2107       if (uMsg >= WM_USER)
2108         ERR("unknown msg %04x wp=%08x lp=%08lx\n",
2109              uMsg, wParam, lParam);
2110       return DefWindowProcA (hwnd, uMsg, wParam, lParam);
2111     }
2112
2113     return 0;
2114 }
2115
2116
2117 VOID
2118 TAB_Register (void)
2119 {
2120   WNDCLASSA wndClass;
2121
2122   ZeroMemory (&wndClass, sizeof(WNDCLASSA));
2123   wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
2124   wndClass.lpfnWndProc   = (WNDPROC)TAB_WindowProc;
2125   wndClass.cbClsExtra    = 0;
2126   wndClass.cbWndExtra    = sizeof(TAB_INFO *);
2127   wndClass.hCursor       = LoadCursorA (0, IDC_ARROWA);
2128   wndClass.hbrBackground = (HBRUSH)NULL;
2129   wndClass.lpszClassName = WC_TABCONTROLA;
2130   
2131   RegisterClassA (&wndClass);
2132 }
2133
2134
2135 VOID
2136 TAB_Unregister (void)
2137 {
2138     UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);
2139 }
2140