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