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