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