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