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