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