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