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