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