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