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