Correct the test for the ODS_SELECTED bit in the WM_DRAWITEM message
[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  * Copyright 2003 Vitaliy Margolen
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  * TODO:
24  *  Image list support
25  *  Unicode support (under construction)
26  *
27  * FIXME:
28  *  UpDown control not displayed until after a tab is clicked on
29  */
30
31 #include <stdarg.h>
32 #include <string.h>
33
34 #include "windef.h"
35 #include "winbase.h"
36 #include "wingdi.h"
37 #include "winuser.h"
38 #include "winnls.h"
39 #include "commctrl.h"
40 #include "comctl32.h"
41 #include "wine/debug.h"
42 #include <math.h>
43
44 WINE_DEFAULT_DEBUG_CHANNEL(tab);
45
46 typedef struct
47 {
48   UINT   mask;
49   DWORD  dwState;
50   LPWSTR pszText;
51   INT    iImage;
52   LPARAM lParam;
53   RECT   rect;    /* bounding rectangle of the item relative to the
54                    * leftmost item (the leftmost item, 0, would have a
55                    * "left" member of 0 in this rectangle)
56                    *
57                    * additionally the top member hold the row number
58                    * and bottom is unused and should be 0 */
59 } TAB_ITEM;
60
61 typedef struct
62 {
63   HWND       hwndNotify;      /* notification window (parent) */
64   UINT       uNumItem;        /* number of tab items */
65   UINT       uNumRows;        /* number of tab rows */
66   INT        tabHeight;       /* height of the tab row */
67   INT        tabWidth;        /* width of tabs */
68   INT        tabMinWidth;     /* minimum width of items */
69   USHORT     uHItemPadding;   /* amount of horizontal padding, in pixels */
70   USHORT     uVItemPadding;   /* amount of vertical padding, in pixels */
71   USHORT     uHItemPadding_s; /* Set amount of horizontal padding, in pixels */
72   USHORT     uVItemPadding_s; /* Set amount of vertical padding, in pixels */
73   HFONT      hFont;           /* handle to the current font */
74   HCURSOR    hcurArrow;       /* handle to the current cursor */
75   HIMAGELIST himl;            /* handle to a image list (may be 0) */
76   HWND       hwndToolTip;     /* handle to tab's tooltip */
77   INT        leftmostVisible; /* Used for scrolling, this member contains
78                                * the index of the first visible item */
79   INT        iSelected;       /* the currently selected item */
80   INT        iHotTracked;     /* the highlighted item under the mouse */
81   INT        uFocus;          /* item which has the focus */
82   TAB_ITEM*  items;           /* pointer to an array of TAB_ITEM's */
83   BOOL       DoRedraw;        /* flag for redrawing when tab contents is changed*/
84   BOOL       needsScrolling;  /* TRUE if the size of the tabs is greater than
85                                * the size of the control */
86   BOOL       fHeightSet;      /* was the height of the tabs explicitly set? */
87   BOOL       bUnicode;        /* Unicode control? */
88   HWND       hwndUpDown;      /* Updown control used for scrolling */
89 } TAB_INFO;
90
91 /******************************************************************************
92  * Positioning constants
93  */
94 #define SELECTED_TAB_OFFSET     2
95 #define ROUND_CORNER_SIZE       2
96 #define DISPLAY_AREA_PADDINGX   2
97 #define DISPLAY_AREA_PADDINGY   2
98 #define CONTROL_BORDER_SIZEX    2
99 #define CONTROL_BORDER_SIZEY    2
100 #define BUTTON_SPACINGX         3
101 #define BUTTON_SPACINGY         4
102 #define FLAT_BTN_SPACINGX       8
103 #define DEFAULT_TAB_WIDTH       96
104
105 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
106
107 /******************************************************************************
108  * Hot-tracking timer constants
109  */
110 #define TAB_HOTTRACK_TIMER            1
111 #define TAB_HOTTRACK_TIMER_INTERVAL   100   /* milliseconds */
112
113 /******************************************************************************
114  * Prototypes
115  */
116 static void TAB_Refresh (HWND hwnd, HDC hdc);
117 static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
118 static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
119 static void TAB_DrawItem(HWND hwnd, HDC hdc, INT iItem);
120 static void TAB_DrawItemInterior(HWND hwnd, HDC hdc, INT iItem, RECT* drawRect);
121
122 static BOOL
123 TAB_SendSimpleNotify (HWND hwnd, UINT code)
124 {
125     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
126     NMHDR nmhdr;
127
128     nmhdr.hwndFrom = hwnd;
129     nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
130     nmhdr.code = code;
131
132     return (BOOL) SendMessageA (infoPtr->hwndNotify, WM_NOTIFY,
133             (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
134 }
135
136 static VOID
137 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
138             WPARAM wParam, LPARAM lParam)
139 {
140     MSG msg;
141
142     msg.hwnd = hwndMsg;
143     msg.message = uMsg;
144     msg.wParam = wParam;
145     msg.lParam = lParam;
146     msg.time = GetMessageTime ();
147     msg.pt.x = LOWORD(GetMessagePos ());
148     msg.pt.y = HIWORD(GetMessagePos ());
149
150     SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
151 }
152
153 static void
154 TAB_DumpItemExternalA(TCITEMA *pti, UINT iItem)
155 {
156     if (TRACE_ON(tab)) {
157         TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
158               iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
159         TRACE("external tab %d,   iImage=%d, lParam=0x%08lx, pszTextA=%s\n",
160               iItem, pti->iImage, pti->lParam, debugstr_a(pti->pszText));
161     }
162 }
163
164
165 static void
166 TAB_DumpItemExternalW(TCITEMW *pti, UINT iItem)
167 {
168     if (TRACE_ON(tab)) {
169         TRACE("external tab %d, mask=0x%08x, dwState=0x%08lx, dwStateMask=0x%08lx, cchTextMax=0x%08x\n",
170               iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
171         TRACE("external tab %d,   iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
172               iItem, pti->iImage, pti->lParam, debugstr_w(pti->pszText));
173     }
174 }
175
176 static void
177 TAB_DumpItemInternal(TAB_INFO *infoPtr, UINT iItem)
178 {
179     if (TRACE_ON(tab)) {
180         TAB_ITEM *ti;
181
182         ti = &infoPtr->items[iItem];
183         TRACE("tab %d, mask=0x%08x, dwState=0x%08lx, pszText=%s, iImage=%d\n",
184               iItem, ti->mask, ti->dwState, debugstr_w(ti->pszText),
185               ti->iImage);
186         TRACE("tab %d, lParam=0x%08lx, rect.left=%ld, rect.top(row)=%ld\n",
187               iItem, ti->lParam, ti->rect.left, ti->rect.top);
188     }
189 }
190
191 static LRESULT
192 TAB_GetCurSel (HWND hwnd)
193 {
194     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
195
196     return infoPtr->iSelected;
197 }
198
199 static LRESULT
200 TAB_GetCurFocus (HWND hwnd)
201 {
202     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
203
204     return infoPtr->uFocus;
205 }
206
207 static LRESULT
208 TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
209 {
210     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
211
212     if (infoPtr == NULL) return 0;
213     return (LRESULT)infoPtr->hwndToolTip;
214 }
215
216 static LRESULT
217 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
218 {
219     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
220   INT iItem = (INT)wParam;
221   INT prevItem;
222
223   prevItem = -1;
224   if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
225     prevItem=infoPtr->iSelected;
226       infoPtr->iSelected=iItem;
227       TAB_EnsureSelectionVisible(hwnd, infoPtr);
228       TAB_InvalidateTabArea(hwnd, infoPtr);
229   }
230   return prevItem;
231 }
232
233 static LRESULT
234 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
235 {
236   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
237   INT iItem=(INT) wParam;
238
239   if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
240
241   if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
242     FIXME("Should set input focus\n");
243   } else {
244     int oldFocus = infoPtr->uFocus;
245     if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) {
246       infoPtr->uFocus = iItem;
247       if (oldFocus != -1) {
248         if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE)  {
249           infoPtr->iSelected = iItem;
250           TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
251         }
252         else
253           infoPtr->iSelected = iItem;
254         TAB_EnsureSelectionVisible(hwnd, infoPtr);
255         TAB_InvalidateTabArea(hwnd, infoPtr);
256       }
257     }
258   }
259   return 0;
260 }
261
262 static LRESULT
263 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
264 {
265     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
266
267     if (infoPtr == NULL) return 0;
268     infoPtr->hwndToolTip = (HWND)wParam;
269     return 0;
270 }
271
272 static LRESULT
273 TAB_SetPadding (HWND hwnd, WPARAM wParam, LPARAM lParam)
274 {
275     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
276     
277     if (infoPtr == NULL) return 0;
278     infoPtr->uHItemPadding_s=LOWORD(lParam);
279     infoPtr->uVItemPadding_s=HIWORD(lParam);
280     return 0;
281 }
282
283 /******************************************************************************
284  * TAB_InternalGetItemRect
285  *
286  * This method will calculate the rectangle representing a given tab item in
287  * client coordinates. This method takes scrolling into account.
288  *
289  * This method returns TRUE if the item is visible in the window and FALSE
290  * if it is completely outside the client area.
291  */
292 static BOOL TAB_InternalGetItemRect(
293   HWND        hwnd,
294   TAB_INFO*   infoPtr,
295   INT         itemIndex,
296   RECT*       itemRect,
297   RECT*       selectedRect)
298 {
299   RECT tmpItemRect,clientRect;
300   LONG        lStyle  = GetWindowLongA(hwnd, GWL_STYLE);
301
302   /* Perform a sanity check and a trivial visibility check. */
303   if ( (infoPtr->uNumItem <= 0) ||
304        (itemIndex >= infoPtr->uNumItem) ||
305        (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
306     return FALSE;
307
308   /*
309    * Avoid special cases in this procedure by assigning the "out"
310    * parameters if the caller didn't supply them
311    */
312   if (itemRect == NULL)
313     itemRect = &tmpItemRect;
314
315   /* Retrieve the unmodified item rect. */
316   *itemRect = infoPtr->items[itemIndex].rect;
317
318   /* calculate the times bottom and top based on the row */
319   GetClientRect(hwnd, &clientRect);
320
321   if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
322   {
323     itemRect->bottom = clientRect.bottom -
324                    itemRect->top * (infoPtr->tabHeight - 2) -
325                    ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
326
327     itemRect->top = clientRect.bottom -
328                    infoPtr->tabHeight -
329                    itemRect->top * (infoPtr->tabHeight - 2) -
330                    ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
331   }
332   else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
333   {
334     itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * (infoPtr->tabHeight - 2) -
335                       ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
336     itemRect->left = clientRect.right - infoPtr->tabHeight - itemRect->left * (infoPtr->tabHeight - 2) -
337                       ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
338   }
339   else if((lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM))
340   {
341     itemRect->right = clientRect.left + infoPtr->tabHeight + itemRect->left * (infoPtr->tabHeight - 2) +
342                       ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
343     itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * (infoPtr->tabHeight - 2) +
344                       ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
345   }
346   else if(!(lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM)) /* not TCS_BOTTOM and not TCS_VERTICAL */
347   {
348     itemRect->bottom = clientRect.top +
349                       infoPtr->tabHeight +
350                       itemRect->top * (infoPtr->tabHeight - 2) +
351                       ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
352     itemRect->top = clientRect.top +
353                    itemRect->top * (infoPtr->tabHeight - 2) +
354                    ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
355  }
356
357   /*
358    * "scroll" it to make sure the item at the very left of the
359    * tab control is the leftmost visible tab.
360    */
361   if(lStyle & TCS_VERTICAL)
362   {
363     OffsetRect(itemRect,
364              0,
365              -(clientRect.bottom - infoPtr->items[infoPtr->leftmostVisible].rect.bottom));
366
367     /*
368      * Move the rectangle so the first item is slightly offset from
369      * the bottom of the tab control.
370      */
371     OffsetRect(itemRect,
372              0,
373              -SELECTED_TAB_OFFSET);
374
375   } else
376   {
377     OffsetRect(itemRect,
378              -infoPtr->items[infoPtr->leftmostVisible].rect.left,
379              0);
380
381     /*
382      * Move the rectangle so the first item is slightly offset from
383      * the left of the tab control.
384      */
385     OffsetRect(itemRect,
386              SELECTED_TAB_OFFSET,
387              0);
388   }
389   TRACE("item %d tab h=%d, rect=(%ld,%ld)-(%ld,%ld)\n",
390         itemIndex, infoPtr->tabHeight,
391         itemRect->left, itemRect->top, itemRect->right, itemRect->bottom);
392
393   /* Now, calculate the position of the item as if it were selected. */
394   if (selectedRect!=NULL)
395   {
396     CopyRect(selectedRect, itemRect);
397
398     /* The rectangle of a selected item is a bit wider. */
399     if(lStyle & TCS_VERTICAL)
400       InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
401     else
402       InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
403
404     /* If it also a bit higher. */
405     if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
406     {
407       selectedRect->bottom += SELECTED_TAB_OFFSET;
408     }
409     else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
410     {
411       selectedRect->left -= 2; /* the border is thicker on the right */
412       selectedRect->right += SELECTED_TAB_OFFSET;
413     }
414     else if(lStyle & TCS_VERTICAL)
415     {
416       selectedRect->left -= SELECTED_TAB_OFFSET;
417       selectedRect->right += 1;
418     }
419     else
420     {
421       selectedRect->top -= SELECTED_TAB_OFFSET;
422       selectedRect->bottom -= 1;
423     }
424   }
425
426   return TRUE;
427 }
428
429 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
430 {
431   return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
432                                  (LPRECT)lParam, (LPRECT)NULL);
433 }
434
435 /******************************************************************************
436  * TAB_KeyUp
437  *
438  * This method is called to handle keyboard input
439  */
440 static LRESULT TAB_KeyUp(
441   HWND   hwnd,
442   WPARAM keyCode)
443 {
444   TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
445   int       newItem = -1;
446
447   switch (keyCode)
448   {
449     case VK_LEFT:
450       newItem = infoPtr->uFocus - 1;
451       break;
452     case VK_RIGHT:
453       newItem = infoPtr->uFocus + 1;
454       break;
455   }
456
457   /*
458    * If we changed to a valid item, change the selection
459    */
460   if ((newItem >= 0) &&
461        (newItem < infoPtr->uNumItem) &&
462        (infoPtr->uFocus != newItem))
463   {
464     if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
465     {
466       infoPtr->iSelected = newItem;
467       infoPtr->uFocus    = newItem;
468       TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
469
470       TAB_EnsureSelectionVisible(hwnd, infoPtr);
471       TAB_InvalidateTabArea(hwnd, infoPtr);
472     }
473   }
474
475   return 0;
476 }
477
478 /******************************************************************************
479  * TAB_FocusChanging
480  *
481  * This method is called whenever the focus goes in or out of this control
482  * it is used to update the visual state of the control.
483  */
484 static LRESULT TAB_FocusChanging(
485   HWND   hwnd,
486   UINT   uMsg,
487   WPARAM wParam,
488   LPARAM lParam)
489 {
490   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
491   RECT      selectedRect;
492   BOOL      isVisible;
493
494   /*
495    * Get the rectangle for the item.
496    */
497   isVisible = TAB_InternalGetItemRect(hwnd,
498                                       infoPtr,
499                                       infoPtr->uFocus,
500                                       NULL,
501                                       &selectedRect);
502
503   /*
504    * If the rectangle is not completely invisible, invalidate that
505    * portion of the window.
506    */
507   if (isVisible)
508   {
509       TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
510             selectedRect.left,selectedRect.top,
511             selectedRect.right,selectedRect.bottom);
512     InvalidateRect(hwnd, &selectedRect, TRUE);
513   }
514
515   /*
516    * Don't otherwise disturb normal behavior.
517    */
518   return DefWindowProcA (hwnd, uMsg, wParam, lParam);
519 }
520
521 static INT TAB_InternalHitTest (
522   HWND      hwnd,
523   TAB_INFO* infoPtr,
524   POINT     pt,
525   UINT*     flags)
526
527 {
528   RECT rect;
529   INT iCount;
530
531   for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
532   {
533     TAB_InternalGetItemRect(hwnd, infoPtr, iCount, &rect, NULL);
534
535     if (PtInRect(&rect, pt))
536     {
537       *flags = TCHT_ONITEM;
538       return iCount;
539     }
540   }
541
542   *flags = TCHT_NOWHERE;
543   return -1;
544 }
545
546 static LRESULT
547 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
548 {
549   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
550   LPTCHITTESTINFO lptest = (LPTCHITTESTINFO) lParam;
551
552   return TAB_InternalHitTest (hwnd, infoPtr, lptest->pt, &lptest->flags);
553 }
554
555 /******************************************************************************
556  * TAB_NCHitTest
557  *
558  * Napster v2b5 has a tab control for its main navigation which has a client
559  * area that covers the whole area of the dialog pages.
560  * That's why it receives all msgs for that area and the underlying dialog ctrls
561  * are dead.
562  * So I decided that we should handle WM_NCHITTEST here and return
563  * HTTRANSPARENT if we don't hit the tab control buttons.
564  * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
565  * doesn't do it that way. Maybe depends on tab control styles ?
566  */
567 static LRESULT
568 TAB_NCHitTest (HWND hwnd, LPARAM lParam)
569 {
570   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
571   POINT pt;
572   UINT dummyflag;
573
574   pt.x = LOWORD(lParam);
575   pt.y = HIWORD(lParam);
576   ScreenToClient(hwnd, &pt);
577
578   if (TAB_InternalHitTest(hwnd, infoPtr, pt, &dummyflag) == -1)
579     return HTTRANSPARENT;
580   else
581     return HTCLIENT;
582 }
583
584 static LRESULT
585 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
586 {
587   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
588   POINT pt;
589   INT newItem, dummy;
590
591   if (infoPtr->hwndToolTip)
592     TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
593                     WM_LBUTTONDOWN, wParam, lParam);
594
595   if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
596     SetFocus (hwnd);
597   }
598
599   if (infoPtr->hwndToolTip)
600     TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
601                     WM_LBUTTONDOWN, wParam, lParam);
602
603   pt.x = (INT)LOWORD(lParam);
604   pt.y = (INT)HIWORD(lParam);
605
606   newItem = TAB_InternalHitTest (hwnd, infoPtr, pt, &dummy);
607
608   TRACE("On Tab, item %d\n", newItem);
609
610   if ((newItem != -1) && (infoPtr->iSelected != newItem))
611   {
612     if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING) != TRUE)
613     {
614       infoPtr->iSelected = newItem;
615       infoPtr->uFocus    = newItem;
616       TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
617
618       TAB_EnsureSelectionVisible(hwnd, infoPtr);
619
620       TAB_InvalidateTabArea(hwnd, infoPtr);
621     }
622   }
623   return 0;
624 }
625
626 static LRESULT
627 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
628 {
629   TAB_SendSimpleNotify(hwnd, NM_CLICK);
630
631   return 0;
632 }
633
634 static LRESULT
635 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
636 {
637   TAB_SendSimpleNotify(hwnd, NM_RCLICK);
638   return 0;
639 }
640
641 /******************************************************************************
642  * TAB_DrawLoneItemInterior
643  *
644  * This calls TAB_DrawItemInterior.  However, TAB_DrawItemInterior is normally
645  * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
646  * up the device context and font.  This routine does the same setup but
647  * only calls TAB_DrawItemInterior for the single specified item.
648  */
649 static void
650 TAB_DrawLoneItemInterior(HWND hwnd, TAB_INFO* infoPtr, int iItem)
651 {
652   HDC hdc = GetDC(hwnd);
653   RECT r, rC;
654
655   GetWindowRect(hwnd, &rC);
656   GetWindowRect(infoPtr->hwndUpDown, &r);
657   ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top);
658   TAB_DrawItemInterior(hwnd, hdc, iItem, NULL);
659   ReleaseDC(hwnd, hdc);
660 }
661
662 /******************************************************************************
663  * TAB_HotTrackTimerProc
664  *
665  * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
666  * timer is setup so we can check if the mouse is moved out of our window.
667  * (We don't get an event when the mouse leaves, the mouse-move events just
668  * stop being delivered to our window and just start being delivered to
669  * another window.)  This function is called when the timer triggers so
670  * we can check if the mouse has left our window.  If so, we un-highlight
671  * the hot-tracked tab.
672  */
673 static VOID CALLBACK
674 TAB_HotTrackTimerProc
675   (
676   HWND hwnd,    /* handle of window for timer messages */
677   UINT uMsg,    /* WM_TIMER message */
678   UINT idEvent, /* timer identifier */
679   DWORD dwTime  /* current system time */
680   )
681 {
682   TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
683
684   if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
685   {
686     POINT pt;
687
688     /*
689     ** If we can't get the cursor position, or if the cursor is outside our
690     ** window, we un-highlight the hot-tracked tab.  Note that the cursor is
691     ** "outside" even if it is within our bounding rect if another window
692     ** overlaps.  Note also that the case where the cursor stayed within our
693     ** window but has moved off the hot-tracked tab will be handled by the
694     ** WM_MOUSEMOVE event.
695     */
696     if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
697     {
698       /* Redraw iHotTracked to look normal */
699       INT iRedraw = infoPtr->iHotTracked;
700       infoPtr->iHotTracked = -1;
701       TAB_DrawLoneItemInterior(hwnd, infoPtr, iRedraw);
702
703       /* Kill this timer */
704       KillTimer(hwnd, TAB_HOTTRACK_TIMER);
705     }
706   }
707 }
708
709 /******************************************************************************
710  * TAB_RecalcHotTrack
711  *
712  * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
713  * should be highlighted.  This function determines which tab in a tab control,
714  * if any, is under the mouse and records that information.  The caller may
715  * supply output parameters to receive the item number of the tab item which
716  * was highlighted but isn't any longer and of the tab item which is now
717  * highlighted but wasn't previously.  The caller can use this information to
718  * selectively redraw those tab items.
719  *
720  * If the caller has a mouse position, it can supply it through the pos
721  * parameter.  For example, TAB_MouseMove does this.  Otherwise, the caller
722  * supplies NULL and this function determines the current mouse position
723  * itself.
724  */
725 static void
726 TAB_RecalcHotTrack
727   (
728   HWND            hwnd,
729   const LPARAM*   pos,
730   int*            out_redrawLeave,
731   int*            out_redrawEnter
732   )
733 {
734   TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
735
736   int item = -1;
737
738
739   if (out_redrawLeave != NULL)
740     *out_redrawLeave = -1;
741   if (out_redrawEnter != NULL)
742     *out_redrawEnter = -1;
743
744   if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_HOTTRACK)
745   {
746     POINT pt;
747     UINT  flags;
748
749     if (pos == NULL)
750     {
751       GetCursorPos(&pt);
752       ScreenToClient(hwnd, &pt);
753     }
754     else
755     {
756       pt.x = LOWORD(*pos);
757       pt.y = HIWORD(*pos);
758     }
759
760     item = TAB_InternalHitTest(hwnd, infoPtr, pt, &flags);
761   }
762
763   if (item != infoPtr->iHotTracked)
764   {
765     if (infoPtr->iHotTracked >= 0)
766     {
767       /* Mark currently hot-tracked to be redrawn to look normal */
768       if (out_redrawLeave != NULL)
769         *out_redrawLeave = infoPtr->iHotTracked;
770
771       if (item < 0)
772       {
773         /* Kill timer which forces recheck of mouse pos */
774         KillTimer(hwnd, TAB_HOTTRACK_TIMER);
775       }
776     }
777     else
778     {
779       /* Start timer so we recheck mouse pos */
780       UINT timerID = SetTimer
781         (
782         hwnd,
783         TAB_HOTTRACK_TIMER,
784         TAB_HOTTRACK_TIMER_INTERVAL,
785         TAB_HotTrackTimerProc
786         );
787
788       if (timerID == 0)
789         return; /* Hot tracking not available */
790     }
791
792     infoPtr->iHotTracked = item;
793
794     if (item >= 0)
795     {
796         /* Mark new hot-tracked to be redrawn to look highlighted */
797       if (out_redrawEnter != NULL)
798         *out_redrawEnter = item;
799     }
800   }
801 }
802
803 /******************************************************************************
804  * TAB_MouseMove
805  *
806  * Handles the mouse-move event.  Updates tooltips.  Updates hot-tracking.
807  */
808 static LRESULT
809 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
810 {
811   int redrawLeave;
812   int redrawEnter;
813
814   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
815
816   if (infoPtr->hwndToolTip)
817     TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
818                     WM_LBUTTONDOWN, wParam, lParam);
819
820   /* Determine which tab to highlight.  Redraw tabs which change highlight
821   ** status. */
822   TAB_RecalcHotTrack(hwnd, &lParam, &redrawLeave, &redrawEnter);
823
824   if (redrawLeave != -1)
825     TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawLeave);
826   if (redrawEnter != -1)
827     TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawEnter);
828
829   return 0;
830 }
831
832 /******************************************************************************
833  * TAB_AdjustRect
834  *
835  * Calculates the tab control's display area given the window rectangle or
836  * the window rectangle given the requested display rectangle.
837  */
838 static LRESULT TAB_AdjustRect(
839   HWND   hwnd,
840   WPARAM fLarger,
841   LPRECT prc)
842 {
843   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
844   DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
845
846   if(lStyle & TCS_VERTICAL)
847   {
848     if (fLarger) /* Go from display rectangle */
849     {
850       /* Add the height of the tabs. */
851       if (lStyle & TCS_BOTTOM)
852         prc->right += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
853       else
854         prc->left -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
855
856       /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
857       /* Inflate the rectangle for the padding */
858       InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
859
860       /* Inflate for the border */
861       InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
862     }
863     else /* Go from window rectangle. */
864     {
865       /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
866       /* Deflate the rectangle for the border */
867       InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
868
869       /* Deflate the rectangle for the padding */
870       InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
871
872       /* Remove the height of the tabs. */
873       if (lStyle & TCS_BOTTOM)
874         prc->right -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
875       else
876         prc->left += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
877     }
878   }
879   else {
880     if (fLarger) /* Go from display rectangle */
881     {
882       /* Add the height of the tabs. */
883       if (lStyle & TCS_BOTTOM)
884         prc->bottom += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
885       else
886         prc->top -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
887
888       /* Inflate the rectangle for the padding */
889       InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
890
891       /* Inflate for the border */
892       InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
893     }
894     else /* Go from window rectangle. */
895     {
896       /* Deflate the rectangle for the border */
897       InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
898
899       /* Deflate the rectangle for the padding */
900       InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
901
902       /* Remove the height of the tabs. */
903       if (lStyle & TCS_BOTTOM)
904         prc->bottom -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
905       else
906         prc->top += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
907     }
908   }
909
910   return 0;
911 }
912
913 /******************************************************************************
914  * TAB_OnHScroll
915  *
916  * This method will handle the notification from the scroll control and
917  * perform the scrolling operation on the tab control.
918  */
919 static LRESULT TAB_OnHScroll(
920   HWND    hwnd,
921   int     nScrollCode,
922   int     nPos,
923   HWND    hwndScroll)
924 {
925   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
926
927   if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
928   {
929      if(nPos < infoPtr->leftmostVisible)
930         infoPtr->leftmostVisible--;
931      else
932         infoPtr->leftmostVisible++;
933
934      TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
935      TAB_InvalidateTabArea(hwnd, infoPtr);
936      SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
937                    MAKELONG(infoPtr->leftmostVisible, 0));
938    }
939
940    return 0;
941 }
942
943 /******************************************************************************
944  * TAB_SetupScrolling
945  *
946  * This method will check the current scrolling state and make sure the
947  * scrolling control is displayed (or not).
948  */
949 static void TAB_SetupScrolling(
950   HWND        hwnd,
951   TAB_INFO*   infoPtr,
952   const RECT* clientRect)
953 {
954   INT maxRange = 0;
955   DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
956
957   if (infoPtr->needsScrolling)
958   {
959     RECT controlPos;
960     INT vsize, tabwidth;
961
962     /*
963      * Calculate the position of the scroll control.
964      */
965     if(lStyle & TCS_VERTICAL)
966     {
967       controlPos.right = clientRect->right;
968       controlPos.left  = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
969
970       if (lStyle & TCS_BOTTOM)
971       {
972         controlPos.top    = clientRect->bottom - infoPtr->tabHeight;
973         controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
974       }
975       else
976       {
977         controlPos.bottom = clientRect->top + infoPtr->tabHeight;
978         controlPos.top    = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
979       }
980     }
981     else
982     {
983       controlPos.right = clientRect->right;
984       controlPos.left  = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
985
986       if (lStyle & TCS_BOTTOM)
987       {
988         controlPos.top    = clientRect->bottom - infoPtr->tabHeight;
989         controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
990       }
991       else
992       {
993         controlPos.bottom = clientRect->top + infoPtr->tabHeight;
994         controlPos.top    = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
995       }
996     }
997
998     /*
999      * If we don't have a scroll control yet, we want to create one.
1000      * If we have one, we want to make sure it's positioned properly.
1001      */
1002     if (infoPtr->hwndUpDown==0)
1003     {
1004       infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
1005                                           "",
1006                                           WS_VISIBLE | WS_CHILD | UDS_HORZ,
1007                                           controlPos.left, controlPos.top,
1008                                           controlPos.right - controlPos.left,
1009                                           controlPos.bottom - controlPos.top,
1010                                           hwnd,
1011                                           NULL,
1012                                           NULL,
1013                                           NULL);
1014     }
1015     else
1016     {
1017       SetWindowPos(infoPtr->hwndUpDown,
1018                    NULL,
1019                    controlPos.left, controlPos.top,
1020                    controlPos.right - controlPos.left,
1021                    controlPos.bottom - controlPos.top,
1022                    SWP_SHOWWINDOW | SWP_NOZORDER);
1023     }
1024
1025     /* Now calculate upper limit of the updown control range.
1026      * We do this by calculating how many tabs will be offscreen when the
1027      * last tab is visible.
1028      */
1029     if(infoPtr->uNumItem)
1030     {
1031        vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
1032        maxRange = infoPtr->uNumItem;
1033        tabwidth = infoPtr->items[maxRange - 1].rect.right;
1034
1035        for(; maxRange > 0; maxRange--)
1036        {
1037           if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
1038              break;
1039        }
1040
1041        if(maxRange == infoPtr->uNumItem)
1042           maxRange--;
1043     }
1044   }
1045   else
1046   {
1047     /* If we once had a scroll control... hide it */
1048     if (infoPtr->hwndUpDown!=0)
1049       ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1050   }
1051   if (infoPtr->hwndUpDown)
1052      SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1053 }
1054
1055 /******************************************************************************
1056  * TAB_SetItemBounds
1057  *
1058  * This method will calculate the position rectangles of all the items in the
1059  * control. The rectangle calculated starts at 0 for the first item in the
1060  * list and ignores scrolling and selection.
1061  * It also uses the current font to determine the height of the tab row and
1062  * it checks if all the tabs fit in the client area of the window. If they
1063  * dont, a scrolling control is added.
1064  */
1065 static void TAB_SetItemBounds (HWND hwnd)
1066 {
1067   TAB_INFO*   infoPtr = TAB_GetInfoPtr(hwnd);
1068   LONG        lStyle  = GetWindowLongA(hwnd, GWL_STYLE);
1069   TEXTMETRICA fontMetrics;
1070   INT         curItem;
1071   INT         curItemLeftPos;
1072   INT         curItemRowCount;
1073   HFONT       hFont, hOldFont;
1074   HDC         hdc;
1075   RECT        clientRect;
1076   SIZE        size;
1077   INT         iTemp;
1078   RECT*       rcItem;
1079   INT         iIndex;
1080   INT         icon_width = 0;
1081
1082   /*
1083    * We need to get text information so we need a DC and we need to select
1084    * a font.
1085    */
1086   hdc = GetDC(hwnd);
1087
1088   hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1089   hOldFont = SelectObject (hdc, hFont);
1090
1091   /*
1092    * We will base the rectangle calculations on the client rectangle
1093    * of the control.
1094    */
1095   GetClientRect(hwnd, &clientRect);
1096
1097   /* if TCS_VERTICAL then swap the height and width so this code places the
1098      tabs along the top of the rectangle and we can just rotate them after
1099      rather than duplicate all of the below code */
1100   if(lStyle & TCS_VERTICAL)
1101   {
1102      iTemp = clientRect.bottom;
1103      clientRect.bottom = clientRect.right;
1104      clientRect.right = iTemp;
1105   }
1106
1107   /* Now use hPadding and vPadding */
1108   infoPtr->uHItemPadding = infoPtr->uHItemPadding_s;
1109   infoPtr->uVItemPadding = infoPtr->uVItemPadding_s;
1110   
1111   /* The leftmost item will be "0" aligned */
1112   curItemLeftPos = 0;
1113   curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1114
1115   if (!(infoPtr->fHeightSet))
1116   {
1117     int item_height;
1118     int icon_height = 0;
1119
1120     /* Use the current font to determine the height of a tab. */
1121     GetTextMetricsA(hdc, &fontMetrics);
1122
1123     /* Get the icon height */
1124     if (infoPtr->himl)
1125       ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1126
1127     /* Take the highest between font or icon */
1128     if (fontMetrics.tmHeight > icon_height)
1129       item_height = fontMetrics.tmHeight + 2;
1130     else
1131       item_height = icon_height;
1132
1133     /*
1134      * Make sure there is enough space for the letters + icon + growing the
1135      * selected item + extra space for the selected item.
1136      */
1137     infoPtr->tabHeight = item_height + SELECTED_TAB_OFFSET +
1138                          ((lStyle & TCS_BUTTONS) ? 2 : 1) *
1139                           infoPtr->uVItemPadding;
1140
1141     TRACE("tabH=%d, tmH=%ld, iconh=%d\n",
1142           infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1143   }
1144
1145   TRACE("client right=%ld\n", clientRect.right);
1146
1147   /* Get the icon width */
1148   if (infoPtr->himl)
1149   {
1150     ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1151
1152     if (lStyle & TCS_FIXEDWIDTH)
1153       icon_width += 4;
1154     else
1155       /* Add padding if icon is present */
1156       icon_width += infoPtr->uHItemPadding;
1157   }
1158
1159   for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1160   {
1161     /* Set the leftmost position of the tab. */
1162     infoPtr->items[curItem].rect.left = curItemLeftPos;
1163
1164     if (lStyle & TCS_FIXEDWIDTH)
1165     {
1166       infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1167         max(infoPtr->tabWidth, icon_width);
1168     }
1169     else
1170     {
1171       int num = 2;
1172
1173       /* Calculate how wide the tab is depending on the text it contains */
1174       GetTextExtentPoint32W(hdc, infoPtr->items[curItem].pszText,
1175                             lstrlenW(infoPtr->items[curItem].pszText), &size);
1176
1177       infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1178                                            size.cx + icon_width +
1179                                            num * infoPtr->uHItemPadding;
1180       TRACE("for <%s>, l,r=%ld,%ld, num=%d\n",
1181           debugstr_w(infoPtr->items[curItem].pszText),
1182           infoPtr->items[curItem].rect.left,
1183           infoPtr->items[curItem].rect.right,
1184           num);
1185     }
1186
1187     /*
1188      * Check if this is a multiline tab control and if so
1189      * check to see if we should wrap the tabs
1190      *
1191      * Because we are going to arange all these tabs evenly
1192      * really we are basically just counting rows at this point
1193      *
1194      */
1195
1196     if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1197         (infoPtr->items[curItem].rect.right > clientRect.right))
1198     {
1199         infoPtr->items[curItem].rect.right -=
1200                                       infoPtr->items[curItem].rect.left;
1201
1202         infoPtr->items[curItem].rect.left = 0;
1203         curItemRowCount++;
1204         TRACE("wrapping <%s>, l,r=%ld,%ld\n",
1205             debugstr_w(infoPtr->items[curItem].pszText),
1206             infoPtr->items[curItem].rect.left,
1207             infoPtr->items[curItem].rect.right);
1208     }
1209
1210     infoPtr->items[curItem].rect.bottom = 0;
1211     infoPtr->items[curItem].rect.top = curItemRowCount - 1;
1212
1213     TRACE("TextSize: %li\n", size.cx);
1214     TRACE("Rect: T %li, L %li, B %li, R %li\n",
1215           infoPtr->items[curItem].rect.top,
1216           infoPtr->items[curItem].rect.left,
1217           infoPtr->items[curItem].rect.bottom,
1218           infoPtr->items[curItem].rect.right);
1219
1220     /*
1221      * The leftmost position of the next item is the rightmost position
1222      * of this one.
1223      */
1224     if (lStyle & TCS_BUTTONS)
1225     {
1226       curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX;
1227       if (lStyle & TCS_FLATBUTTONS)
1228         curItemLeftPos += FLAT_BTN_SPACINGX;
1229     }
1230     else
1231       curItemLeftPos = infoPtr->items[curItem].rect.right;
1232   }
1233
1234   if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1235   {
1236     /*
1237      * Check if we need a scrolling control.
1238      */
1239     infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1240                                clientRect.right);
1241
1242     /* Don't need scrolling, then update infoPtr->leftmostVisible */
1243     if(!infoPtr->needsScrolling)
1244       infoPtr->leftmostVisible = 0;
1245
1246     TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
1247   }
1248
1249   /* Set the number of rows */
1250   infoPtr->uNumRows = curItemRowCount;
1251
1252    if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->uNumItem > 0))
1253    {
1254       INT widthDiff, remainder;
1255       INT tabPerRow,remTab;
1256       INT iRow,iItm;
1257       INT iIndexStart=0,iIndexEnd=0, iCount=0;
1258
1259       /*
1260        * Ok windows tries to even out the rows. place the same
1261        * number of tabs in each row. So lets give that a shot
1262        */
1263
1264       tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1265       remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1266
1267       for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1268            iItm<infoPtr->uNumItem;
1269            iItm++,iCount++)
1270       {
1271           /* normalize the current rect */
1272
1273           /* shift the item to the left side of the clientRect */
1274           infoPtr->items[iItm].rect.right -=
1275             infoPtr->items[iItm].rect.left;
1276           infoPtr->items[iItm].rect.left = 0;
1277
1278           TRACE("r=%ld, cl=%d, cl.r=%ld, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1279               infoPtr->items[iItm].rect.right,
1280               curItemLeftPos, clientRect.right,
1281               iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1282
1283           /* if we have reached the maximum number of tabs on this row */
1284           /* move to the next row, reset our current item left position and */
1285           /* the count of items on this row */
1286
1287           /* ************  FIXME FIXME FIXME  *************** */
1288           /*                                                  */
1289           /* FIXME:                                           */
1290           /* if vertical,                                     */
1291           /*   if item n and n+1 are in the same row,         */
1292           /*      then the display has n+1 lower (toward the  */
1293           /*      bottom) than n. We do it just the           */
1294           /*      opposite!!!                                 */
1295           /*                                                  */
1296           /* ************  FIXME FIXME FIXME  *************** */
1297
1298           if (lStyle & TCS_VERTICAL) {
1299               /* Vert: Add the remaining tabs in the *last* remainder rows */
1300               if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1301                   iRow++;
1302                   curItemLeftPos = 0;
1303                   iCount = 0;
1304               }
1305           } else {
1306               /* Horz: Add the remaining tabs in the *first* remainder rows */
1307               if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1308                   iRow++;
1309                   curItemLeftPos = 0;
1310                   iCount = 0;
1311               }
1312           }
1313
1314           /* shift the item to the right to place it as the next item in this row */
1315           infoPtr->items[iItm].rect.left += curItemLeftPos;
1316           infoPtr->items[iItm].rect.right += curItemLeftPos;
1317           infoPtr->items[iItm].rect.top = iRow;
1318           if (lStyle & TCS_BUTTONS)
1319           {
1320             curItemLeftPos = infoPtr->items[iItm].rect.right + 1;
1321             if (lStyle & TCS_FLATBUTTONS)
1322               curItemLeftPos += FLAT_BTN_SPACINGX;
1323           }
1324           else
1325             curItemLeftPos = infoPtr->items[iItm].rect.right;
1326
1327           TRACE("arranging <%s>, l,r=%ld,%ld, row=%ld\n",
1328               debugstr_w(infoPtr->items[iItm].pszText),
1329               infoPtr->items[iItm].rect.left,
1330               infoPtr->items[iItm].rect.right,
1331               infoPtr->items[iItm].rect.top);
1332       }
1333
1334       /*
1335        * Justify the rows
1336        */
1337       {
1338          while(iIndexStart < infoPtr->uNumItem)
1339         {
1340         /*
1341          * find the indexs of the row
1342          */
1343         /* find the first item on the next row */
1344         for (iIndexEnd=iIndexStart;
1345              (iIndexEnd < infoPtr->uNumItem) &&
1346                (infoPtr->items[iIndexEnd].rect.top ==
1347                 infoPtr->items[iIndexStart].rect.top) ;
1348             iIndexEnd++)
1349         /* intentionally blank */;
1350
1351         /*
1352          * we need to justify these tabs so they fill the whole given
1353          * client area
1354          *
1355          */
1356         /* find the amount of space remaining on this row */
1357         widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1358                             infoPtr->items[iIndexEnd - 1].rect.right;
1359
1360         /* iCount is the number of tab items on this row */
1361         iCount = iIndexEnd - iIndexStart;
1362
1363
1364         if (iCount > 1)
1365         {
1366            remainder = widthDiff % iCount;
1367            widthDiff = widthDiff / iCount;
1368            /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1369            for (iIndex=iIndexStart,iCount=0; iIndex < iIndexEnd;
1370                 iIndex++,iCount++)
1371            {
1372               infoPtr->items[iIndex].rect.left += iCount * widthDiff;
1373               infoPtr->items[iIndex].rect.right += (iCount + 1) * widthDiff;
1374
1375               TRACE("adjusting 1 <%s>, l,r=%ld,%ld\n",
1376                   debugstr_w(infoPtr->items[iIndex].pszText),
1377                   infoPtr->items[iIndex].rect.left,
1378                   infoPtr->items[iIndex].rect.right);
1379
1380            }
1381            infoPtr->items[iIndex - 1].rect.right += remainder;
1382         }
1383         else /* we have only one item on this row, make it take up the entire row */
1384         {
1385           infoPtr->items[iIndexStart].rect.left = clientRect.left;
1386           infoPtr->items[iIndexStart].rect.right = clientRect.right - 4;
1387
1388           TRACE("adjusting 2 <%s>, l,r=%ld,%ld\n",
1389               debugstr_w(infoPtr->items[iIndexStart].pszText),
1390               infoPtr->items[iIndexStart].rect.left,
1391               infoPtr->items[iIndexStart].rect.right);
1392
1393         }
1394
1395
1396         iIndexStart = iIndexEnd;
1397         }
1398       }
1399   }
1400
1401   /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1402   if(lStyle & TCS_VERTICAL)
1403   {
1404     RECT rcOriginal;
1405     for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1406     {
1407       rcItem = &(infoPtr->items[iIndex].rect);
1408
1409       rcOriginal = *rcItem;
1410
1411       /* this is rotating the items by 90 degrees around the center of the control */
1412       rcItem->top = (clientRect.right - (rcOriginal.left - clientRect.left)) - (rcOriginal.right - rcOriginal.left);
1413       rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1414       rcItem->left = rcOriginal.top;
1415       rcItem->right = rcOriginal.bottom;
1416     }
1417   }
1418
1419   TAB_EnsureSelectionVisible(hwnd,infoPtr);
1420   TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1421
1422   /* Cleanup */
1423   SelectObject (hdc, hOldFont);
1424   ReleaseDC (hwnd, hdc);
1425 }
1426
1427
1428 static void
1429 TAB_EraseTabInterior
1430     (
1431     HWND        hwnd,
1432     HDC         hdc,
1433     INT         iItem,
1434     RECT*       drawRect
1435     )
1436 {
1437     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1438     LONG     lStyle  = GetWindowLongA(hwnd, GWL_STYLE);
1439     HBRUSH   hbr = CreateSolidBrush (comctl32_color.clrBtnFace);
1440     BOOL     deleteBrush = TRUE;
1441     RECT     rTemp = *drawRect;
1442
1443     InflateRect(&rTemp, -2, -2);
1444     if (lStyle & TCS_BUTTONS)
1445     {
1446         if (iItem == infoPtr->iSelected)
1447         {
1448             /* Background color */
1449             if (!(lStyle & TCS_OWNERDRAWFIXED))
1450             {
1451                 DeleteObject(hbr);
1452                 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1453
1454                 SetTextColor(hdc, comctl32_color.clr3dFace);
1455                 SetBkColor(hdc, comctl32_color.clr3dHilight);
1456
1457                 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1458                 * we better use 0x55aa bitmap brush to make scrollbar's background
1459                 * look different from the window background.
1460                 */
1461                 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1462                     hbr = COMCTL32_hPattern55AABrush;
1463
1464                 deleteBrush = FALSE;
1465             }
1466             FillRect(hdc, &rTemp, hbr);
1467         }
1468         else  /* ! selected */
1469         {
1470             if (lStyle & TCS_FLATBUTTONS)
1471             {
1472                 FillRect(hdc, drawRect, hbr);
1473                 if (iItem == infoPtr->iHotTracked)
1474                     DrawEdge(hdc, drawRect, EDGE_RAISED, BF_SOFT|BF_RECT);
1475             }
1476             else
1477                 FillRect(hdc, &rTemp, hbr);
1478         }
1479
1480     }
1481     else /* !TCS_BUTTONS */
1482     {
1483         FillRect(hdc, &rTemp, hbr);
1484     }
1485
1486     /* Cleanup */
1487     if (deleteBrush) DeleteObject(hbr);
1488 }
1489
1490 /******************************************************************************
1491  * TAB_DrawItemInterior
1492  *
1493  * This method is used to draw the interior (text and icon) of a single tab
1494  * into the tab control.
1495  */
1496 static void
1497 TAB_DrawItemInterior
1498   (
1499   HWND        hwnd,
1500   HDC         hdc,
1501   INT         iItem,
1502   RECT*       drawRect
1503   )
1504 {
1505   TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1506   LONG      lStyle  = GetWindowLongA(hwnd, GWL_STYLE);
1507
1508   RECT localRect;
1509
1510   HPEN   htextPen;
1511   HPEN   holdPen;
1512   INT    oldBkMode;
1513   HFONT  hOldFont;
1514   
1515   if (drawRect == NULL)
1516   {
1517     BOOL isVisible;
1518     RECT itemRect;
1519     RECT selectedRect;
1520
1521     /*
1522      * Get the rectangle for the item.
1523      */
1524     isVisible = TAB_InternalGetItemRect(hwnd, infoPtr, iItem, &itemRect, &selectedRect);
1525     if (!isVisible)
1526       return;
1527
1528     /*
1529      * Make sure drawRect points to something valid; simplifies code.
1530      */
1531     drawRect = &localRect;
1532
1533     /*
1534      * This logic copied from the part of TAB_DrawItem which draws
1535      * the tab background.  It's important to keep it in sync.  I
1536      * would have liked to avoid code duplication, but couldn't figure
1537      * out how without making spaghetti of TAB_DrawItem.
1538      */
1539     if (lStyle & TCS_BUTTONS)
1540     {
1541       *drawRect = itemRect;
1542       if (iItem == infoPtr->iSelected)
1543       {
1544         OffsetRect(drawRect, 1, 1);
1545       }
1546     }
1547     else
1548     {
1549       if (iItem == infoPtr->iSelected)
1550       {
1551         *drawRect = selectedRect;
1552         if (lStyle & TCS_BOTTOM)
1553         {
1554           if (lStyle & TCS_VERTICAL)
1555           {
1556             drawRect->left++;
1557           }
1558           else
1559           {
1560             drawRect->top += 3;
1561             drawRect->left += 1;
1562           }
1563         }
1564       }
1565       else
1566         *drawRect = itemRect;
1567
1568
1569       if (lStyle & TCS_BOTTOM && !(lStyle & TCS_VERTICAL))
1570       {
1571         drawRect->top--;
1572         drawRect->bottom--;
1573       }
1574     }
1575   }
1576   TRACE("drawRect=(%ld,%ld)-(%ld,%ld)\n",
1577           drawRect->left, drawRect->top, drawRect->right, drawRect->bottom);
1578
1579   /* Clear interior */
1580   TAB_EraseTabInterior (hwnd, hdc, iItem, drawRect);
1581
1582   /* Draw the focus rectangle */
1583   if (!(lStyle & TCS_FOCUSNEVER) &&
1584       (GetFocus() == hwnd) &&
1585       (iItem == infoPtr->uFocus) )
1586   {
1587     RECT rFocus = *drawRect;
1588     InflateRect(&rFocus, -3, -3);
1589     if (lStyle & TCS_BOTTOM && !(lStyle & TCS_VERTICAL))
1590       rFocus.top -= 3;
1591     if (lStyle & TCS_BUTTONS)
1592     {
1593       rFocus.left -= 3;
1594       rFocus.top -= 3;
1595     }
1596
1597     DrawFocusRect(hdc, &rFocus);
1598   }
1599
1600   /*
1601    * Text pen
1602    */
1603   htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1604   holdPen  = SelectObject(hdc, htextPen);
1605   hOldFont = SelectObject(hdc, infoPtr->hFont);
1606
1607   /*
1608    * Setup for text output
1609   */
1610   oldBkMode = SetBkMode(hdc, TRANSPARENT);
1611   SetTextColor(hdc, (((iItem == infoPtr->iHotTracked) && !(lStyle & TCS_FLATBUTTONS)) |
1612                      (infoPtr->items[iItem].dwState & TCIS_HIGHLIGHTED)) ?
1613                      comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1614
1615   /*
1616    * if owner draw, tell the owner to draw
1617    */
1618   if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd))
1619   {
1620     DRAWITEMSTRUCT dis;
1621     UINT id;
1622
1623     /*
1624      * get the control id
1625      */
1626     id = GetWindowLongA( hwnd, GWL_ID );
1627
1628     /*
1629      * put together the DRAWITEMSTRUCT
1630      */
1631     dis.CtlType    = ODT_TAB;
1632     dis.CtlID      = id;
1633     dis.itemID     = iItem;
1634     dis.itemAction = ODA_DRAWENTIRE;
1635     dis.itemState = 0;
1636     if ( iItem == infoPtr->iSelected )
1637       dis.itemState |= ODS_SELECTED;
1638     if (infoPtr->uFocus == iItem) 
1639       dis.itemState |= ODS_FOCUS;
1640     dis.hwndItem = hwnd;                /* */
1641     dis.hDC      = hdc;
1642     CopyRect(&dis.rcItem,drawRect);
1643     InflateRect(&dis.rcItem, -2, -2);
1644     dis.itemData = infoPtr->items[iItem].lParam;
1645
1646     /*
1647      * send the draw message
1648      */
1649     SendMessageA( infoPtr->hwndNotify, WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1650   }
1651   else
1652   {
1653     RECT rcTemp;
1654     RECT rcImage;
1655
1656     /* used to center the icon and text in the tab */
1657     RECT rcText;
1658     INT center_offset_h, center_offset_v;
1659
1660     /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1661     rcImage = *drawRect;
1662
1663     rcTemp = *drawRect;
1664
1665     rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1666
1667     /* get the rectangle that the text fits in */
1668     DrawTextW(hdc, infoPtr->items[iItem].pszText, -1,
1669               &rcText, DT_CALCRECT);
1670     /*
1671      * If not owner draw, then do the drawing ourselves.
1672      *
1673      * Draw the icon.
1674      */
1675     if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE))
1676     {
1677       INT cx;
1678       INT cy;
1679       
1680       ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1681
1682       if(lStyle & TCS_VERTICAL)
1683       {
1684         center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right  - rcText.left))) / 2;
1685         center_offset_v = ((drawRect->right - drawRect->left) - (cx + infoPtr->uVItemPadding)) / 2;
1686       }
1687       else
1688       {
1689         center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right  - rcText.left))) / 2;
1690         center_offset_v = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uVItemPadding)) / 2;
1691       }
1692
1693       if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1694         center_offset_h = infoPtr->uHItemPadding;
1695
1696       if (center_offset_h < 2)
1697         center_offset_h = 2;
1698         
1699       TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1700           debugstr_w(infoPtr->items[iItem].pszText), center_offset_h, center_offset_v,
1701           drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1702           (rcText.right-rcText.left));
1703
1704       if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1705       {
1706         rcImage.top = drawRect->top + center_offset_h;
1707         /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1708         /* right side of the tab, but the image still uses the left as its x position */
1709         /* this keeps the image always drawn off of the same side of the tab */
1710         rcImage.left = drawRect->right - cx - center_offset_v;
1711         drawRect->top += cy + infoPtr->uHItemPadding;
1712       }
1713       else if(lStyle & TCS_VERTICAL)
1714       {
1715         rcImage.top  = drawRect->bottom - cy - center_offset_h;
1716         rcImage.left = drawRect->left + center_offset_v;
1717         drawRect->bottom -= cy + infoPtr->uHItemPadding;
1718       }
1719       else /* normal style, whether TCS_BOTTOM or not */
1720       {
1721         rcImage.left = drawRect->left + center_offset_h;
1722         rcImage.top = drawRect->top + center_offset_v;
1723         drawRect->left += cx + infoPtr->uHItemPadding;
1724       }
1725
1726       TRACE("drawing image=%d, left=%ld, top=%ld\n",
1727             infoPtr->items[iItem].iImage, rcImage.left, rcImage.top-1);
1728       ImageList_Draw
1729         (
1730         infoPtr->himl,
1731         infoPtr->items[iItem].iImage,
1732         hdc,
1733         rcImage.left,
1734         rcImage.top,
1735         ILD_NORMAL
1736         );
1737     }
1738
1739     /* Now position text */
1740     if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT)
1741       center_offset_h = infoPtr->uHItemPadding;
1742     else
1743       if(lStyle & TCS_VERTICAL)
1744         center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1745       else
1746         center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1747
1748     if(lStyle & TCS_VERTICAL)
1749     {
1750       if(lStyle & TCS_BOTTOM)
1751         drawRect->top+=center_offset_h;
1752       else
1753         drawRect->bottom-=center_offset_h;
1754
1755       drawRect->left += ((drawRect->right - drawRect->left) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1756     }
1757     else
1758     {
1759       drawRect->left += center_offset_h;
1760       drawRect->top += ((drawRect->bottom - drawRect->top) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1761     }
1762
1763     /* Draw the text */
1764     if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1765     {
1766       LOGFONTA logfont;
1767       HFONT hFont = 0;
1768       INT nEscapement = 900;
1769       INT nOrientation = 900;
1770
1771       if(lStyle & TCS_BOTTOM)
1772       {
1773         nEscapement = -900;
1774         nOrientation = -900;
1775       }
1776
1777       /* to get a font with the escapement and orientation we are looking for, we need to */
1778       /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1779       if (!GetObjectA((infoPtr->hFont) ?
1780                 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1781                 sizeof(LOGFONTA),&logfont))
1782       {
1783         INT iPointSize = 9;
1784
1785         lstrcpyA(logfont.lfFaceName, "Arial");
1786         logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1787                                     72);
1788         logfont.lfWeight = FW_NORMAL;
1789         logfont.lfItalic = 0;
1790         logfont.lfUnderline = 0;
1791         logfont.lfStrikeOut = 0;
1792       }
1793
1794       logfont.lfEscapement = nEscapement;
1795       logfont.lfOrientation = nOrientation;
1796       hFont = CreateFontIndirectA(&logfont);
1797       SelectObject(hdc, hFont);
1798
1799       ExtTextOutW(hdc,
1800       (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1801       (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1802       ETO_CLIPPED,
1803       drawRect,
1804       infoPtr->items[iItem].pszText,
1805       lstrlenW(infoPtr->items[iItem].pszText),
1806       0);
1807
1808       DeleteObject(hFont);
1809     }
1810     else
1811     {
1812       DrawTextW
1813       (
1814         hdc,
1815         infoPtr->items[iItem].pszText,
1816         lstrlenW(infoPtr->items[iItem].pszText),
1817         drawRect,
1818         DT_LEFT | DT_SINGLELINE
1819         );
1820     }
1821
1822     *drawRect = rcTemp; /* restore drawRect */
1823   }
1824
1825   /*
1826   * Cleanup
1827   */
1828   SelectObject(hdc, hOldFont);
1829   SetBkMode(hdc, oldBkMode);
1830   SelectObject(hdc, holdPen);
1831   DeleteObject( htextPen );
1832 }
1833
1834 /******************************************************************************
1835  * TAB_DrawItem
1836  *
1837  * This method is used to draw a single tab into the tab control.
1838  */
1839 static void TAB_DrawItem(
1840   HWND hwnd,
1841   HDC  hdc,
1842   INT  iItem)
1843 {
1844   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1845   LONG      lStyle  = GetWindowLongA(hwnd, GWL_STYLE);
1846   RECT      itemRect;
1847   RECT      selectedRect;
1848   BOOL      isVisible;
1849   RECT      r, fillRect, r1;
1850   INT       clRight = 0;
1851   INT       clBottom = 0;
1852   COLORREF  bkgnd, corner;
1853
1854   /*
1855    * Get the rectangle for the item.
1856    */
1857   isVisible = TAB_InternalGetItemRect(hwnd,
1858                                       infoPtr,
1859                                       iItem,
1860                                       &itemRect,
1861                                       &selectedRect);
1862
1863   if (isVisible)
1864   {
1865     RECT rUD, rC;
1866
1867     /* Clip UpDown control to not draw over it */
1868     GetWindowRect(hwnd, &rC);
1869     GetWindowRect(infoPtr->hwndUpDown, &rUD);
1870     ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
1871
1872     /* If you need to see what the control is doing,
1873      * then override these variables. They will change what
1874      * fill colors are used for filling the tabs, and the
1875      * corners when drawing the edge.
1876      */
1877     bkgnd = comctl32_color.clrBtnFace;
1878     corner = comctl32_color.clrBtnFace;
1879
1880     if (lStyle & TCS_BUTTONS)
1881     {
1882       /* Get item rectangle */
1883       r = itemRect;
1884
1885       /* Separators between flat buttons */
1886       if (lStyle & TCS_FLATBUTTONS)
1887       {
1888         r1 = r;
1889         r1.right += (FLAT_BTN_SPACINGX -2);
1890         DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1891       }
1892
1893       if (iItem == infoPtr->iSelected)
1894       {
1895         DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1896         
1897         OffsetRect(&r, 1, 1);
1898       }
1899       else  /* ! selected */
1900       {
1901         if (!(lStyle & TCS_FLATBUTTONS))
1902           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1903       }
1904     }
1905     else /* !TCS_BUTTONS */
1906     {
1907       /* We draw a rectangle of different sizes depending on the selection
1908        * state. */
1909       if (iItem == infoPtr->iSelected) {
1910         RECT rect;
1911         GetClientRect (hwnd, &rect);
1912         clRight = rect.right;
1913         clBottom = rect.bottom;
1914         r = selectedRect;
1915       }
1916       else
1917         r = itemRect;
1918
1919       /*
1920        * Erase the background. (Delay it but setup rectangle.)
1921        * This is necessary when drawing the selected item since it is larger
1922        * than the others, it might overlap with stuff already drawn by the
1923        * other tabs
1924        */
1925       fillRect = r;
1926
1927       if(lStyle & TCS_VERTICAL)
1928       {
1929         /* These are for adjusting the drawing of a Selected tab      */
1930         /* The initial values are for the normal case of non-Selected */
1931         int ZZ = 1;   /* Do not strech if selected */
1932         if (iItem == infoPtr->iSelected) {
1933             ZZ = 0;
1934
1935             /* if leftmost draw the line longer */
1936             if(selectedRect.top == 0)
1937                 fillRect.top += 2;
1938             /* if rightmost draw the line longer */
1939             if(selectedRect.bottom == clBottom)
1940                 fillRect.bottom -= 2;
1941         }
1942
1943         if (lStyle & TCS_BOTTOM)
1944         {
1945           /* Adjust both rectangles to match native */
1946           r.left += (1-ZZ);
1947
1948           TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1949                 iItem,
1950                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1951                 r.left,r.top,r.right,r.bottom);
1952
1953           /* Clear interior */
1954           SetBkColor(hdc, bkgnd);
1955           ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1956
1957           /* Draw rectangular edge around tab */
1958           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
1959
1960           /* Now erase the top corner and draw diagonal edge */
1961           SetBkColor(hdc, corner);
1962           r1.left = r.right - ROUND_CORNER_SIZE - 1;
1963           r1.top = r.top;
1964           r1.right = r.right;
1965           r1.bottom = r1.top + ROUND_CORNER_SIZE;
1966           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1967           r1.right--;
1968           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
1969
1970           /* Now erase the bottom corner and draw diagonal edge */
1971           r1.left = r.right - ROUND_CORNER_SIZE - 1;
1972           r1.bottom = r.bottom;
1973           r1.right = r.right;
1974           r1.top = r1.bottom - ROUND_CORNER_SIZE;
1975           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1976           r1.right--;
1977           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
1978
1979           if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
1980               r1 = r;
1981               r1.right = r1.left;
1982               r1.left--;
1983               DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
1984           }
1985
1986         }
1987         else
1988         {
1989           /* Adjust both rectangles to match native */
1990           fillRect.right += (1-ZZ);
1991
1992           TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1993                 iItem,
1994                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1995                 r.left,r.top,r.right,r.bottom);
1996
1997           /* Clear interior */
1998           SetBkColor(hdc, bkgnd);
1999           ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2000
2001           /* Draw rectangular edge around tab */
2002           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2003
2004           /* Now erase the top corner and draw diagonal edge */
2005           SetBkColor(hdc, corner);
2006           r1.left = r.left;
2007           r1.top = r.top;
2008           r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2009           r1.bottom = r1.top + ROUND_CORNER_SIZE;
2010           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2011           r1.left++;
2012           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2013
2014           /* Now erase the bottom corner and draw diagonal edge */
2015           r1.left = r.left;
2016           r1.bottom = r.bottom;
2017           r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2018           r1.top = r1.bottom - ROUND_CORNER_SIZE;
2019           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2020           r1.left++;
2021           DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2022         }
2023       }
2024       else  /* ! TCS_VERTICAL */
2025       {
2026         /* These are for adjusting the drawing of a Selected tab      */
2027         /* The initial values are for the normal case of non-Selected */
2028         int ZZ = 1;   /* Do not strech if selected */
2029         if (iItem == infoPtr->iSelected) {
2030             ZZ = 0;
2031
2032             /* if leftmost draw the line longer */
2033             if(selectedRect.left == 0)
2034                 fillRect.left += 2;
2035             /* if rightmost draw the line longer */
2036             if(selectedRect.right == clRight)
2037                 fillRect.right -= 2;
2038         }
2039
2040         if (lStyle & TCS_BOTTOM)
2041         {
2042
2043           /* Adjust both rectangles to match native */
2044           fillRect.top--;
2045           fillRect.bottom--;
2046           r.bottom--;
2047           r.top -= ZZ;
2048
2049           TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2050                 iItem,
2051                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2052                 r.left,r.top,r.right,r.bottom);
2053
2054           /* Clear interior */
2055           SetBkColor(hdc, bkgnd);
2056           ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2057
2058           /* Draw rectangular edge around tab */
2059           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2060
2061           /* Now erase the righthand corner and draw diagonal edge */
2062           SetBkColor(hdc, corner);
2063           r1.left = r.right - ROUND_CORNER_SIZE;
2064           r1.bottom = r.bottom;
2065           r1.right = r.right;
2066           r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2067           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2068           r1.bottom--;
2069           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2070
2071           /* Now erase the lefthand corner and draw diagonal edge */
2072           r1.left = r.left;
2073           r1.bottom = r.bottom;
2074           r1.right = r1.left + ROUND_CORNER_SIZE;
2075           r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2076           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2077           r1.bottom--;
2078           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2079
2080           if (iItem == infoPtr->iSelected)
2081           {
2082             r.top += 2;
2083             r.left += 1;
2084             if (selectedRect.left == 0)
2085             {
2086               r1 = r;
2087               r1.bottom = r1.top;
2088               r1.top--;
2089               DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2090             }
2091           }
2092
2093         }
2094         else
2095         {
2096
2097           /* Adjust both rectangles to match native */
2098           fillRect.bottom += (1-ZZ);
2099
2100           TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2101                 iItem,
2102                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2103                 r.left,r.top,r.right,r.bottom);
2104
2105           /* Clear interior */
2106           SetBkColor(hdc, bkgnd);
2107           ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2108
2109           /* Draw rectangular edge around tab */
2110           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2111
2112           /* Now erase the righthand corner and draw diagonal edge */
2113           SetBkColor(hdc, corner);
2114           r1.left = r.right - ROUND_CORNER_SIZE;
2115           r1.top = r.top;
2116           r1.right = r.right;
2117           r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2118           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2119           r1.top++;
2120           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2121
2122           /* Now erase the lefthand corner and draw diagonal edge */
2123           r1.left = r.left;
2124           r1.top = r.top;
2125           r1.right = r1.left + ROUND_CORNER_SIZE;
2126           r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2127           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2128           r1.top++;
2129           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2130
2131         }
2132       }
2133     }
2134
2135     TAB_DumpItemInternal(infoPtr, iItem);
2136
2137     /* This modifies r to be the text rectangle. */
2138     TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
2139   }
2140 }
2141
2142 /******************************************************************************
2143  * TAB_DrawBorder
2144  *
2145  * This method is used to draw the raised border around the tab control
2146  * "content" area.
2147  */
2148 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
2149 {
2150   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2151   RECT rect;
2152   DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2153
2154   GetClientRect (hwnd, &rect);
2155
2156   /*
2157    * Adjust for the style
2158    */
2159
2160   if (infoPtr->uNumItem)
2161   {
2162     if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2163     {
2164       rect.bottom -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 3;
2165     }
2166     else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2167     {
2168       rect.right -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2169     }
2170     else if(lStyle & TCS_VERTICAL)
2171     {
2172       rect.left += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2173     }
2174     else /* not TCS_VERTICAL and not TCS_BOTTOM */
2175     {
2176       rect.top += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2177     }
2178   }
2179
2180   TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2181         rect.left, rect.top, rect.right, rect.bottom);
2182
2183   DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2184 }
2185
2186 /******************************************************************************
2187  * TAB_Refresh
2188  *
2189  * This method repaints the tab control..
2190  */
2191 static void TAB_Refresh (HWND hwnd, HDC hdc)
2192 {
2193   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2194   HFONT hOldFont;
2195   INT i;
2196
2197   if (!infoPtr->DoRedraw)
2198     return;
2199
2200   hOldFont = SelectObject (hdc, infoPtr->hFont);
2201
2202   if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
2203   {
2204     for (i = 0; i < infoPtr->uNumItem; i++)
2205       TAB_DrawItem (hwnd, hdc, i);
2206   }
2207   else
2208   {
2209     /* Draw all the non selected item first */
2210     for (i = 0; i < infoPtr->uNumItem; i++)
2211     {
2212       if (i != infoPtr->iSelected)
2213         TAB_DrawItem (hwnd, hdc, i);
2214     }
2215
2216     /* Now, draw the border, draw it before the selected item
2217      * since the selected item overwrites part of the border. */
2218     TAB_DrawBorder (hwnd, hdc);
2219
2220     /* Then, draw the selected item */
2221     TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
2222
2223     /* If we haven't set the current focus yet, set it now.
2224      * Only happens when we first paint the tab controls */
2225     if (infoPtr->uFocus == -1)
2226       TAB_SetCurFocus(hwnd, infoPtr->iSelected);
2227   }
2228
2229   SelectObject (hdc, hOldFont);
2230 }
2231
2232 static DWORD
2233 TAB_GetRowCount (HWND hwnd )
2234 {
2235   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2236
2237   return infoPtr->uNumRows;
2238 }
2239
2240 static LRESULT
2241 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
2242 {
2243     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2244
2245   infoPtr->DoRedraw=(BOOL) wParam;
2246   return 0;
2247 }
2248
2249 static LRESULT TAB_EraseBackground(
2250   HWND hwnd,
2251   HDC  givenDC)
2252 {
2253   HDC  hdc;
2254   RECT clientRect;
2255
2256   HBRUSH brush = CreateSolidBrush(comctl32_color.clrBtnFace);
2257
2258   hdc = givenDC ? givenDC : GetDC(hwnd);
2259
2260   GetClientRect(hwnd, &clientRect);
2261
2262   FillRect(hdc, &clientRect, brush);
2263
2264   if (givenDC==0)
2265     ReleaseDC(hwnd, hdc);
2266
2267   DeleteObject(brush);
2268
2269   return 0;
2270 }
2271
2272 /******************************************************************************
2273  * TAB_EnsureSelectionVisible
2274  *
2275  * This method will make sure that the current selection is completely
2276  * visible by scrolling until it is.
2277  */
2278 static void TAB_EnsureSelectionVisible(
2279   HWND      hwnd,
2280   TAB_INFO* infoPtr)
2281 {
2282   INT iSelected = infoPtr->iSelected;
2283   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2284   INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2285
2286   /* set the items row to the bottommost row or topmost row depending on
2287    * style */
2288   if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2289   {
2290       INT newselected;
2291       INT iTargetRow;
2292
2293       if(lStyle & TCS_VERTICAL)
2294         newselected = infoPtr->items[iSelected].rect.left;
2295       else
2296         newselected = infoPtr->items[iSelected].rect.top;
2297
2298       /* the target row is always (number of rows - 1)
2299          as row 0 is furthest from the clientRect */
2300       iTargetRow = infoPtr->uNumRows - 1;
2301
2302       if (newselected != iTargetRow)
2303       {
2304          INT i;
2305          if(lStyle & TCS_VERTICAL)
2306          {
2307            for (i=0; i < infoPtr->uNumItem; i++)
2308            {
2309              /* move everything in the row of the selected item to the iTargetRow */
2310              if (infoPtr->items[i].rect.left == newselected )
2311                  infoPtr->items[i].rect.left = iTargetRow;
2312              else
2313              {
2314                if (infoPtr->items[i].rect.left > newselected)
2315                  infoPtr->items[i].rect.left-=1;
2316              }
2317            }
2318          }
2319          else
2320          {
2321            for (i=0; i < infoPtr->uNumItem; i++)
2322            {
2323              if (infoPtr->items[i].rect.top == newselected )
2324                  infoPtr->items[i].rect.top = iTargetRow;
2325              else
2326              {
2327                if (infoPtr->items[i].rect.top > newselected)
2328                  infoPtr->items[i].rect.top-=1;
2329              }
2330           }
2331         }
2332         TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2333       }
2334   }
2335
2336   /*
2337    * Do the trivial cases first.
2338    */
2339   if ( (!infoPtr->needsScrolling) ||
2340        (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2341     return;
2342
2343   if (infoPtr->leftmostVisible >= iSelected)
2344   {
2345     infoPtr->leftmostVisible = iSelected;
2346   }
2347   else
2348   {
2349      RECT r;
2350      INT  width, i;
2351
2352      /* Calculate the part of the client area that is visible */
2353      GetClientRect(hwnd, &r);
2354      width = r.right;
2355
2356      GetClientRect(infoPtr->hwndUpDown, &r);
2357      width -= r.right;
2358
2359      if ((infoPtr->items[iSelected].rect.right -
2360           infoPtr->items[iSelected].rect.left) >= width )
2361      {
2362         /* Special case: width of selected item is greater than visible
2363          * part of control.
2364          */
2365         infoPtr->leftmostVisible = iSelected;
2366      }
2367      else
2368      {
2369         for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2370         {
2371            if ((infoPtr->items[iSelected].rect.right -
2372                 infoPtr->items[i].rect.left) < width)
2373               break;
2374         }
2375         infoPtr->leftmostVisible = i;
2376      }
2377   }
2378
2379   if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2380     TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2381
2382   SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2383                MAKELONG(infoPtr->leftmostVisible, 0));
2384 }
2385
2386 /******************************************************************************
2387  * TAB_InvalidateTabArea
2388  *
2389  * This method will invalidate the portion of the control that contains the
2390  * tabs. It is called when the state of the control changes and needs
2391  * to be redisplayed
2392  */
2393 static void TAB_InvalidateTabArea(
2394   HWND      hwnd,
2395   TAB_INFO* infoPtr)
2396 {
2397   RECT clientRect, rInvalidate;
2398   DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2399   INT lastRow = infoPtr->uNumRows - 1;
2400   RECT rect;
2401
2402   if (lastRow < 0) return;
2403
2404   GetClientRect(hwnd, &clientRect);
2405   rInvalidate = clientRect;
2406
2407   TAB_InternalGetItemRect(hwnd, infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2408   if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2409   {
2410     rInvalidate.top = clientRect.bottom -
2411                    infoPtr->tabHeight -
2412                    lastRow * (infoPtr->tabHeight - 2) -
2413                    ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 3;
2414     rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2415   }
2416   else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2417   {
2418     rInvalidate.left = clientRect.right - infoPtr->tabHeight -
2419                       lastRow * (infoPtr->tabHeight - 2) -
2420                       ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 2;
2421     rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2422   }
2423   else if(lStyle & TCS_VERTICAL)
2424   {
2425     rInvalidate.right = clientRect.left + infoPtr->tabHeight +
2426                        lastRow * (infoPtr->tabHeight - 2) -
2427                       ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 2;
2428     rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2429   }
2430   else
2431   {
2432     rInvalidate.bottom = clientRect.top + infoPtr->tabHeight +
2433                       lastRow * (infoPtr->tabHeight - 2) +
2434                       ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 2;
2435     rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2436   }
2437   
2438   /* Punch out the updown control */
2439   if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2440     RECT r;
2441     GetClientRect(infoPtr->hwndUpDown, &r);
2442     if (rInvalidate.right > clientRect.right - r.left)
2443       rInvalidate.right = rInvalidate.right - (r.right - r.left);
2444     else
2445       rInvalidate.right = clientRect.right - r.left;
2446   }
2447   
2448   TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2449         clientRect.left,clientRect.top,
2450         clientRect.right,clientRect.bottom);
2451  
2452   InvalidateRect(hwnd, &rInvalidate, TRUE);
2453 }
2454
2455 static LRESULT
2456 TAB_Paint (HWND hwnd, WPARAM wParam)
2457 {
2458   HDC hdc;
2459   PAINTSTRUCT ps;
2460
2461   if (wParam == 0)
2462   {
2463     hdc = BeginPaint (hwnd, &ps);
2464     TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2465          ps.fErase,
2466          ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2467
2468     if (ps.fErase)
2469       TAB_EraseBackground (hwnd, hdc);
2470
2471   } else {
2472     hdc = (HDC)wParam;
2473   }
2474     
2475   TAB_Refresh (hwnd, hdc);
2476
2477   if(!wParam)
2478     EndPaint (hwnd, &ps);
2479
2480   return 0;
2481 }
2482
2483 static LRESULT
2484 TAB_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2485 {
2486   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2487   TCITEMA *pti;
2488   INT iItem;
2489   RECT rect;
2490
2491   GetClientRect (hwnd, &rect);
2492   TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2493         rect.top, rect.left, rect.bottom, rect.right);
2494
2495   pti = (TCITEMA *)lParam;
2496   iItem = (INT)wParam;
2497
2498   if (iItem < 0) return -1;
2499   if (iItem > infoPtr->uNumItem)
2500     iItem = infoPtr->uNumItem;
2501
2502   TAB_DumpItemExternalA(pti, iItem);
2503
2504
2505   if (infoPtr->uNumItem == 0) {
2506     infoPtr->items = Alloc (sizeof (TAB_ITEM));
2507     infoPtr->uNumItem++;
2508     infoPtr->iSelected = 0;
2509   }
2510   else {
2511     TAB_ITEM *oldItems = infoPtr->items;
2512
2513     infoPtr->uNumItem++;
2514     infoPtr->items = Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2515
2516     /* pre insert copy */
2517     if (iItem > 0) {
2518       memcpy (&infoPtr->items[0], &oldItems[0],
2519               iItem * sizeof(TAB_ITEM));
2520     }
2521
2522     /* post insert copy */
2523     if (iItem < infoPtr->uNumItem - 1) {
2524       memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2525               (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2526
2527     }
2528
2529     if (iItem <= infoPtr->iSelected)
2530       infoPtr->iSelected++;
2531
2532     Free (oldItems);
2533   }
2534
2535   infoPtr->items[iItem].mask = pti->mask;
2536   if (pti->mask & TCIF_TEXT)
2537     Str_SetPtrAtoW (&infoPtr->items[iItem].pszText, pti->pszText);
2538
2539   if (pti->mask & TCIF_IMAGE)
2540     infoPtr->items[iItem].iImage = pti->iImage;
2541
2542   if (pti->mask & TCIF_PARAM)
2543     infoPtr->items[iItem].lParam = pti->lParam;
2544
2545   TAB_SetItemBounds(hwnd);
2546   if (infoPtr->uNumItem > 1)
2547     TAB_InvalidateTabArea(hwnd, infoPtr);
2548   else
2549     InvalidateRect(hwnd, NULL, TRUE);
2550
2551   TRACE("[%p]: added item %d %s\n",
2552         hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2553
2554   return iItem;
2555 }
2556
2557
2558 static LRESULT
2559 TAB_InsertItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2560 {
2561   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2562   TCITEMW *pti;
2563   INT iItem;
2564   RECT rect;
2565
2566   GetClientRect (hwnd, &rect);
2567   TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2568         rect.top, rect.left, rect.bottom, rect.right);
2569
2570   pti = (TCITEMW *)lParam;
2571   iItem = (INT)wParam;
2572
2573   if (iItem < 0) return -1;
2574   if (iItem > infoPtr->uNumItem)
2575     iItem = infoPtr->uNumItem;
2576
2577   TAB_DumpItemExternalW(pti, iItem);
2578
2579   if (infoPtr->uNumItem == 0) {
2580     infoPtr->items = Alloc (sizeof (TAB_ITEM));
2581     infoPtr->uNumItem++;
2582     infoPtr->iSelected = 0;
2583   }
2584   else {
2585     TAB_ITEM *oldItems = infoPtr->items;
2586
2587     infoPtr->uNumItem++;
2588     infoPtr->items = Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2589
2590     /* pre insert copy */
2591     if (iItem > 0) {
2592       memcpy (&infoPtr->items[0], &oldItems[0],
2593               iItem * sizeof(TAB_ITEM));
2594     }
2595
2596     /* post insert copy */
2597     if (iItem < infoPtr->uNumItem - 1) {
2598       memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2599               (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2600
2601   }
2602
2603     if (iItem <= infoPtr->iSelected)
2604       infoPtr->iSelected++;
2605
2606     Free (oldItems);
2607   }
2608
2609   infoPtr->items[iItem].mask = pti->mask;
2610   if (pti->mask & TCIF_TEXT)
2611     Str_SetPtrW (&infoPtr->items[iItem].pszText, pti->pszText);
2612
2613   if (pti->mask & TCIF_IMAGE)
2614     infoPtr->items[iItem].iImage = pti->iImage;
2615
2616   if (pti->mask & TCIF_PARAM)
2617     infoPtr->items[iItem].lParam = pti->lParam;
2618
2619   TAB_SetItemBounds(hwnd);
2620   if (infoPtr->uNumItem > 1)
2621     TAB_InvalidateTabArea(hwnd, infoPtr);
2622   else
2623     InvalidateRect(hwnd, NULL, TRUE);
2624
2625   TRACE("[%p]: added item %d %s\n",
2626         hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2627
2628   return iItem;
2629 }
2630
2631
2632 static LRESULT
2633 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2634 {
2635   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2636   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2637   LONG lResult = 0;
2638   BOOL bNeedPaint = FALSE;
2639
2640   lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2641
2642   /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2643   if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2644   {
2645     infoPtr->tabWidth = max((INT)LOWORD(lParam), infoPtr->tabMinWidth);
2646     bNeedPaint = TRUE;
2647   }
2648
2649   if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2650   {
2651     if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2652       infoPtr->tabHeight = (INT)HIWORD(lParam);
2653
2654     bNeedPaint = TRUE;
2655   }
2656   TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2657        HIWORD(lResult), LOWORD(lResult),
2658        infoPtr->tabHeight, infoPtr->tabWidth);
2659
2660   if (bNeedPaint)
2661   {
2662     TAB_SetItemBounds(hwnd);
2663     RedrawWindow(hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2664   }
2665     
2666   return lResult;
2667 }
2668
2669 static LRESULT
2670 TAB_SetMinTabWidth (HWND hwnd, LPARAM lParam)
2671 {
2672   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2673   INT cx = (INT)lParam;
2674   INT oldcx;
2675
2676   if (infoPtr) {
2677     oldcx = infoPtr->tabMinWidth;
2678     infoPtr->tabMinWidth = (cx==-1)?DEFAULT_TAB_WIDTH:cx;
2679   } else
2680     return 0;
2681
2682   return oldcx;
2683 }
2684
2685 static LRESULT 
2686 TAB_HighlightItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2687 {
2688   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2689   INT iItem = (INT)wParam;
2690   BOOL fHighlight = (BOOL)LOWORD(lParam);
2691
2692   if ((infoPtr) && (iItem>=0) && (iItem<infoPtr->uNumItem)) {
2693     if (fHighlight)
2694       infoPtr->items[iItem].dwState |= TCIS_HIGHLIGHTED;
2695     else
2696       infoPtr->items[iItem].dwState &= ~TCIS_HIGHLIGHTED;
2697   } else
2698     return FALSE;
2699
2700   return TRUE;
2701 }
2702
2703 static LRESULT
2704 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2705 {
2706   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2707   TCITEMA *tabItem;
2708   TAB_ITEM *wineItem;
2709   INT    iItem;
2710
2711   iItem = (INT)wParam;
2712   tabItem = (LPTCITEMA)lParam;
2713
2714   TRACE("%d %p\n", iItem, tabItem);
2715   if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2716
2717   TAB_DumpItemExternalA(tabItem, iItem);
2718
2719   wineItem = &infoPtr->items[iItem];
2720
2721   if (tabItem->mask & TCIF_IMAGE)
2722     wineItem->iImage = tabItem->iImage;
2723
2724   if (tabItem->mask & TCIF_PARAM)
2725     wineItem->lParam = tabItem->lParam;
2726
2727   if (tabItem->mask & TCIF_RTLREADING)
2728     FIXME("TCIF_RTLREADING\n");
2729
2730   if (tabItem->mask & TCIF_STATE)
2731     wineItem->dwState = tabItem->dwState;
2732
2733   if (tabItem->mask & TCIF_TEXT)
2734    Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2735
2736   /* Update and repaint tabs */
2737   TAB_SetItemBounds(hwnd);
2738   TAB_InvalidateTabArea(hwnd,infoPtr);
2739
2740   return TRUE;
2741   }
2742
2743
2744 static LRESULT
2745 TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2746 {
2747   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2748   TCITEMW *tabItem;
2749   TAB_ITEM *wineItem;
2750   INT    iItem;
2751
2752   iItem = (INT)wParam;
2753   tabItem = (LPTCITEMW)lParam;
2754
2755   TRACE("%d %p\n", iItem, tabItem);
2756   if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2757
2758   TAB_DumpItemExternalW(tabItem, iItem);
2759
2760   wineItem = &infoPtr->items[iItem];
2761
2762   if (tabItem->mask & TCIF_IMAGE)
2763     wineItem->iImage = tabItem->iImage;
2764
2765   if (tabItem->mask & TCIF_PARAM)
2766     wineItem->lParam = tabItem->lParam;
2767
2768   if (tabItem->mask & TCIF_RTLREADING)
2769     FIXME("TCIF_RTLREADING\n");
2770
2771   if (tabItem->mask & TCIF_STATE)
2772     wineItem->dwState = tabItem->dwState;
2773
2774   if (tabItem->mask & TCIF_TEXT)
2775    Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2776
2777   /* Update and repaint tabs */
2778   TAB_SetItemBounds(hwnd);
2779   TAB_InvalidateTabArea(hwnd,infoPtr);
2780
2781   return TRUE;
2782 }
2783
2784
2785 static LRESULT
2786 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2787 {
2788    TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2789
2790    return infoPtr->uNumItem;
2791 }
2792
2793
2794 static LRESULT
2795 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2796 {
2797    TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2798    TCITEMA *tabItem;
2799    TAB_ITEM *wineItem;
2800    INT    iItem;
2801
2802   iItem = (INT)wParam;
2803   tabItem = (LPTCITEMA)lParam;
2804   TRACE("\n");
2805   if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2806     return FALSE;
2807
2808   wineItem = &infoPtr->items[iItem];
2809
2810   if (tabItem->mask & TCIF_IMAGE)
2811     tabItem->iImage = wineItem->iImage;
2812
2813   if (tabItem->mask & TCIF_PARAM)
2814     tabItem->lParam = wineItem->lParam;
2815
2816   if (tabItem->mask & TCIF_RTLREADING)
2817     FIXME("TCIF_RTLREADING\n");
2818
2819   if (tabItem->mask & TCIF_STATE)
2820     tabItem->dwState = wineItem->dwState;
2821
2822   if (tabItem->mask & TCIF_TEXT)
2823    Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2824
2825   TAB_DumpItemExternalA(tabItem, iItem);
2826
2827   return TRUE;
2828 }
2829
2830
2831 static LRESULT
2832 TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2833 {
2834   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2835   TCITEMW *tabItem;
2836   TAB_ITEM *wineItem;
2837   INT    iItem;
2838
2839   iItem = (INT)wParam;
2840   tabItem = (LPTCITEMW)lParam;
2841   TRACE("\n");
2842   if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2843     return FALSE;
2844
2845   wineItem=& infoPtr->items[iItem];
2846
2847   if (tabItem->mask & TCIF_IMAGE)
2848     tabItem->iImage = wineItem->iImage;
2849
2850   if (tabItem->mask & TCIF_PARAM)
2851     tabItem->lParam = wineItem->lParam;
2852
2853   if (tabItem->mask & TCIF_RTLREADING)
2854     FIXME("TCIF_RTLREADING\n");
2855
2856   if (tabItem->mask & TCIF_STATE)
2857     tabItem->dwState = wineItem->dwState;
2858
2859   if (tabItem->mask & TCIF_TEXT)
2860    Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2861
2862   TAB_DumpItemExternalW(tabItem, iItem);
2863
2864   return TRUE;
2865 }
2866
2867
2868 static LRESULT
2869 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2870 {
2871     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2872     INT iItem = (INT) wParam;
2873     BOOL bResult = FALSE;
2874
2875     if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2876     {
2877         TAB_ITEM *oldItems = infoPtr->items;
2878         
2879         TAB_InvalidateTabArea(hwnd, infoPtr);
2880
2881         infoPtr->uNumItem--;
2882         infoPtr->items = Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
2883
2884         if (iItem > 0)
2885             memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
2886
2887         if (iItem < infoPtr->uNumItem)
2888             memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
2889                    (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
2890
2891         Free(oldItems);
2892
2893         /* Readjust the selected index */
2894         if ((iItem == infoPtr->iSelected) && (iItem > 0))
2895             infoPtr->iSelected--;
2896
2897         if (iItem < infoPtr->iSelected)
2898             infoPtr->iSelected--;
2899
2900         if (infoPtr->uNumItem == 0)
2901             infoPtr->iSelected = -1;
2902
2903         /* Reposition and repaint tabs */
2904         TAB_SetItemBounds(hwnd);
2905
2906         bResult = TRUE;
2907     }
2908
2909     return bResult;
2910 }
2911
2912 static LRESULT
2913 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2914 {
2915     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2916    
2917     TAB_InvalidateTabArea(hwnd,infoPtr);  
2918
2919     Free (infoPtr->items);
2920     infoPtr->uNumItem = 0;
2921     infoPtr->iSelected = -1;
2922     if (infoPtr->iHotTracked >= 0)
2923         KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2924     infoPtr->iHotTracked = -1;
2925     
2926     TAB_SetItemBounds(hwnd);
2927     return TRUE;
2928 }
2929
2930
2931 static LRESULT
2932 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2933 {
2934   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2935
2936   TRACE("\n");
2937   return (LRESULT)infoPtr->hFont;
2938 }
2939
2940 static LRESULT
2941 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2942
2943 {
2944   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2945
2946   TRACE("%x %lx\n",wParam, lParam);
2947
2948   infoPtr->hFont = (HFONT)wParam;
2949
2950   TAB_SetItemBounds(hwnd);
2951
2952   TAB_InvalidateTabArea(hwnd, infoPtr);
2953
2954   return 0;
2955 }
2956
2957
2958 static LRESULT
2959 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2960 {
2961   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2962
2963   TRACE("\n");
2964   return (LRESULT)infoPtr->himl;
2965 }
2966
2967 static LRESULT
2968 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2969 {
2970     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2971     HIMAGELIST himlPrev;
2972
2973     TRACE("\n");
2974     himlPrev = infoPtr->himl;
2975     infoPtr->himl= (HIMAGELIST)lParam;
2976     return (LRESULT)himlPrev;
2977 }
2978
2979 static LRESULT
2980 TAB_GetUnicodeFormat (HWND hwnd)
2981 {
2982     TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2983     return infoPtr->bUnicode;
2984 }
2985
2986 static LRESULT
2987 TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
2988 {
2989     TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2990     BOOL bTemp = infoPtr->bUnicode;
2991
2992     infoPtr->bUnicode = (BOOL)wParam;
2993
2994     return bTemp;
2995 }
2996
2997 static LRESULT
2998 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2999
3000 {
3001 /* I'm not really sure what the following code was meant to do.
3002    This is what it is doing:
3003    When WM_SIZE is sent with SIZE_RESTORED, the control
3004    gets positioned in the top left corner.
3005
3006   RECT parent_rect;
3007   HWND parent;
3008   UINT uPosFlags,cx,cy;
3009
3010   uPosFlags=0;
3011   if (!wParam) {
3012     parent = GetParent (hwnd);
3013     GetClientRect(parent, &parent_rect);
3014     cx=LOWORD (lParam);
3015     cy=HIWORD (lParam);
3016     if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
3017         uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
3018
3019     SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
3020             cx, cy, uPosFlags | SWP_NOZORDER);
3021   } else {
3022     FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
3023   } */
3024
3025   /* Recompute the size/position of the tabs. */
3026   TAB_SetItemBounds (hwnd);
3027
3028   /* Force a repaint of the control. */
3029   InvalidateRect(hwnd, NULL, TRUE);
3030
3031   return 0;
3032 }
3033
3034
3035 static LRESULT
3036 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
3037 {
3038   TAB_INFO *infoPtr;
3039   TEXTMETRICA fontMetrics;
3040   HDC hdc;
3041   HFONT hOldFont;
3042   DWORD dwStyle;
3043
3044   infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
3045
3046   SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
3047
3048   infoPtr->hwndNotify      = ((LPCREATESTRUCTW)lParam)->hwndParent;
3049   infoPtr->uNumItem        = 0;
3050   infoPtr->uNumRows        = 0;
3051   infoPtr->uHItemPadding   = 6;
3052   infoPtr->uVItemPadding   = 3;
3053   infoPtr->uHItemPadding_s = 6;
3054   infoPtr->uVItemPadding_s = 3;
3055   infoPtr->hFont           = 0;
3056   infoPtr->items           = 0;
3057   infoPtr->hcurArrow       = LoadCursorA (0, (LPSTR)IDC_ARROW);
3058   infoPtr->iSelected       = -1;
3059   infoPtr->iHotTracked     = -1;
3060   infoPtr->uFocus          = -1;
3061   infoPtr->hwndToolTip     = 0;
3062   infoPtr->DoRedraw        = TRUE;
3063   infoPtr->needsScrolling  = FALSE;
3064   infoPtr->hwndUpDown      = 0;
3065   infoPtr->leftmostVisible = 0;
3066   infoPtr->fHeightSet      = FALSE;
3067   infoPtr->bUnicode        = IsWindowUnicode (hwnd);
3068
3069   TRACE("Created tab control, hwnd [%p]\n", hwnd);
3070
3071   /* The tab control always has the WS_CLIPSIBLINGS style. Even
3072      if you don't specify it in CreateWindow. This is necessary in
3073      order for paint to work correctly. This follows windows behaviour. */
3074   dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
3075   SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
3076
3077   if (dwStyle & TCS_TOOLTIPS) {
3078     /* Create tooltip control */
3079     infoPtr->hwndToolTip =
3080       CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
3081                        CW_USEDEFAULT, CW_USEDEFAULT,
3082                        CW_USEDEFAULT, CW_USEDEFAULT,
3083                        hwnd, 0, 0, 0);
3084
3085     /* Send NM_TOOLTIPSCREATED notification */
3086     if (infoPtr->hwndToolTip) {
3087       NMTOOLTIPSCREATED nmttc;
3088
3089       nmttc.hdr.hwndFrom = hwnd;
3090       nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
3091       nmttc.hdr.code = NM_TOOLTIPSCREATED;
3092       nmttc.hwndToolTips = infoPtr->hwndToolTip;
3093
3094       SendMessageA (infoPtr->hwndNotify, WM_NOTIFY,
3095                     (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
3096     }
3097   }
3098
3099   /*
3100    * We need to get text information so we need a DC and we need to select
3101    * a font.
3102    */
3103   hdc = GetDC(hwnd);
3104   hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3105
3106   /* Use the system font to determine the initial height of a tab. */
3107   GetTextMetricsA(hdc, &fontMetrics);
3108
3109   /*
3110    * Make sure there is enough space for the letters + growing the
3111    * selected item + extra space for the selected item.
3112    */
3113   infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3114                        ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
3115                         infoPtr->uVItemPadding;
3116
3117   /* Initialize the width of a tab. */
3118   infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
3119   infoPtr->tabMinWidth = 0;
3120
3121   TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3122
3123   SelectObject (hdc, hOldFont);
3124   ReleaseDC(hwnd, hdc);
3125
3126   return 0;
3127 }
3128
3129 static LRESULT
3130 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
3131 {
3132   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3133   INT iItem;
3134
3135   if (!infoPtr)
3136       return 0;
3137
3138   if (infoPtr->items) {
3139     for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3140       if (infoPtr->items[iItem].pszText)
3141         Free (infoPtr->items[iItem].pszText);
3142     }
3143     Free (infoPtr->items);
3144   }
3145
3146   if (infoPtr->hwndToolTip)
3147     DestroyWindow (infoPtr->hwndToolTip);
3148
3149   if (infoPtr->hwndUpDown)
3150     DestroyWindow(infoPtr->hwndUpDown);
3151
3152   if (infoPtr->iHotTracked >= 0)
3153     KillTimer(hwnd, TAB_HOTTRACK_TIMER);
3154
3155   Free (infoPtr);
3156   SetWindowLongA(hwnd, 0, 0);
3157   return 0;
3158 }
3159
3160 static LRESULT WINAPI
3161 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3162 {
3163     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3164
3165     TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3166     if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
3167       return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3168
3169     switch (uMsg)
3170     {
3171     case TCM_GETIMAGELIST:
3172       return TAB_GetImageList (hwnd, wParam, lParam);
3173
3174     case TCM_SETIMAGELIST:
3175       return TAB_SetImageList (hwnd, wParam, lParam);
3176
3177     case TCM_GETITEMCOUNT:
3178       return TAB_GetItemCount (hwnd, wParam, lParam);
3179
3180     case TCM_GETITEMA:
3181       return TAB_GetItemA (hwnd, wParam, lParam);
3182
3183     case TCM_GETITEMW:
3184       return TAB_GetItemW (hwnd, wParam, lParam);
3185
3186     case TCM_SETITEMA:
3187       return TAB_SetItemA (hwnd, wParam, lParam);
3188
3189     case TCM_SETITEMW:
3190       return TAB_SetItemW (hwnd, wParam, lParam);
3191
3192     case TCM_DELETEITEM:
3193       return TAB_DeleteItem (hwnd, wParam, lParam);
3194
3195     case TCM_DELETEALLITEMS:
3196      return TAB_DeleteAllItems (hwnd, wParam, lParam);
3197
3198     case TCM_GETITEMRECT:
3199      return TAB_GetItemRect (hwnd, wParam, lParam);
3200
3201     case TCM_GETCURSEL:
3202       return TAB_GetCurSel (hwnd);
3203
3204     case TCM_HITTEST:
3205       return TAB_HitTest (hwnd, wParam, lParam);
3206
3207     case TCM_SETCURSEL:
3208       return TAB_SetCurSel (hwnd, wParam);
3209
3210     case TCM_INSERTITEMA:
3211       return TAB_InsertItemA (hwnd, wParam, lParam);
3212
3213     case TCM_INSERTITEMW:
3214       return TAB_InsertItemW (hwnd, wParam, lParam);
3215
3216     case TCM_SETITEMEXTRA:
3217       FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
3218       return 0;
3219
3220     case TCM_ADJUSTRECT:
3221       return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
3222
3223     case TCM_SETITEMSIZE:
3224       return TAB_SetItemSize (hwnd, wParam, lParam);
3225
3226     case TCM_REMOVEIMAGE:
3227       FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3228       return 0;
3229
3230     case TCM_SETPADDING:
3231       return TAB_SetPadding (hwnd, wParam, lParam);
3232
3233     case TCM_GETROWCOUNT:
3234       return TAB_GetRowCount(hwnd);
3235
3236     case TCM_GETUNICODEFORMAT:
3237       return TAB_GetUnicodeFormat (hwnd);
3238
3239     case TCM_SETUNICODEFORMAT:
3240       return TAB_SetUnicodeFormat (hwnd, wParam);
3241
3242     case TCM_HIGHLIGHTITEM:
3243       return TAB_HighlightItem (hwnd, wParam, lParam);
3244
3245     case TCM_GETTOOLTIPS:
3246       return TAB_GetToolTips (hwnd, wParam, lParam);
3247
3248     case TCM_SETTOOLTIPS:
3249       return TAB_SetToolTips (hwnd, wParam, lParam);
3250
3251     case TCM_GETCURFOCUS:
3252       return TAB_GetCurFocus (hwnd);
3253
3254     case TCM_SETCURFOCUS:
3255       return TAB_SetCurFocus (hwnd, wParam);
3256
3257     case TCM_SETMINTABWIDTH:
3258       return TAB_SetMinTabWidth(hwnd, lParam);
3259
3260     case TCM_DESELECTALL:
3261       FIXME("Unimplemented msg TCM_DESELECTALL\n");
3262       return 0;
3263
3264     case TCM_GETEXTENDEDSTYLE:
3265       FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3266       return 0;
3267
3268     case TCM_SETEXTENDEDSTYLE:
3269       FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3270       return 0;
3271
3272     case WM_GETFONT:
3273       return TAB_GetFont (hwnd, wParam, lParam);
3274
3275     case WM_SETFONT:
3276       return TAB_SetFont (hwnd, wParam, lParam);
3277
3278     case WM_CREATE:
3279       return TAB_Create (hwnd, wParam, lParam);
3280
3281     case WM_NCDESTROY:
3282       return TAB_Destroy (hwnd, wParam, lParam);
3283
3284     case WM_GETDLGCODE:
3285       return DLGC_WANTARROWS | DLGC_WANTCHARS;
3286
3287     case WM_LBUTTONDOWN:
3288       return TAB_LButtonDown (hwnd, wParam, lParam);
3289
3290     case WM_LBUTTONUP:
3291       return TAB_LButtonUp (hwnd, wParam, lParam);
3292
3293     case WM_NOTIFY:
3294       return SendMessageA(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3295
3296     case WM_RBUTTONDOWN:
3297       return TAB_RButtonDown (hwnd, wParam, lParam);
3298
3299     case WM_MOUSEMOVE:
3300       return TAB_MouseMove (hwnd, wParam, lParam);
3301
3302     case WM_ERASEBKGND:
3303       return TAB_EraseBackground (hwnd, (HDC)wParam);
3304
3305     case WM_PAINT:
3306       return TAB_Paint (hwnd, wParam);
3307
3308     case WM_SIZE:
3309       return TAB_Size (hwnd, wParam, lParam);
3310
3311     case WM_SETREDRAW:
3312       return TAB_SetRedraw (hwnd, wParam);
3313
3314     case WM_HSCROLL:
3315       return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3316
3317     case WM_STYLECHANGED:
3318       TAB_SetItemBounds (hwnd);
3319       InvalidateRect(hwnd, NULL, TRUE);
3320       return 0;
3321
3322     case WM_SYSCOLORCHANGE:
3323       COMCTL32_RefreshSysColors();
3324       return 0;
3325
3326     case WM_KILLFOCUS:
3327     case WM_SETFOCUS:
3328       return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
3329
3330     case WM_KEYUP:
3331       return TAB_KeyUp(hwnd, wParam);
3332     case WM_NCHITTEST:
3333       return TAB_NCHitTest(hwnd, lParam);
3334
3335     default:
3336       if ((uMsg >= WM_USER) && (uMsg < WM_APP))
3337         WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3338              uMsg, wParam, lParam);
3339       return DefWindowProcA(hwnd, uMsg, wParam, lParam);
3340     }
3341
3342     return 0;
3343 }
3344
3345
3346 VOID
3347 TAB_Register (void)
3348 {
3349   WNDCLASSA wndClass;
3350
3351   ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3352   wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3353   wndClass.lpfnWndProc   = (WNDPROC)TAB_WindowProc;
3354   wndClass.cbClsExtra    = 0;
3355   wndClass.cbWndExtra    = sizeof(TAB_INFO *);
3356   wndClass.hCursor       = LoadCursorA (0, (LPSTR)IDC_ARROW);
3357   wndClass.hbrBackground = NULL;
3358   wndClass.lpszClassName = WC_TABCONTROLA;
3359
3360   RegisterClassA (&wndClass);
3361 }
3362
3363
3364 VOID
3365 TAB_Unregister (void)
3366 {
3367     UnregisterClassA (WC_TABCONTROLA, NULL);
3368 }