Avoid excessive heap memory reallocation when generating EMF
[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       fSizeSet;        /* was the size 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 (!(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet) )
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) || ((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
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) && infoPtr->fSizeSet))
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
2338   if (lastRow < 0) return;
2339
2340   GetClientRect(hwnd, &clientRect);
2341
2342   if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2343   {
2344     clientRect.top = clientRect.bottom -
2345                    infoPtr->tabHeight -
2346                    lastRow * (infoPtr->tabHeight - 2) -
2347                    ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 3;
2348   }
2349   else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2350   {
2351     clientRect.left = clientRect.right - infoPtr->tabHeight -
2352                       lastRow * (infoPtr->tabHeight - 2) -
2353                       ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 2;
2354   }
2355   else if(lStyle & TCS_VERTICAL)
2356   {
2357     clientRect.right = clientRect.left + infoPtr->tabHeight +
2358                        lastRow * (infoPtr->tabHeight - 2) -
2359                       ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 2;
2360
2361   }
2362   else
2363   {
2364     clientRect.bottom = clientRect.top + infoPtr->tabHeight +
2365                       lastRow * (infoPtr->tabHeight - 2) +
2366                       ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 2;
2367   }
2368   
2369   /* Punch out the updown control */
2370   if (infoPtr->needsScrolling && (clientRect.right > 0)) {
2371     GetClientRect(infoPtr->hwndUpDown, &r);
2372     clientRect.right = clientRect.right - (r.right - r.left);
2373   }
2374   
2375   TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2376         clientRect.left,clientRect.top,
2377         clientRect.right,clientRect.bottom);
2378  
2379   InvalidateRect(hwnd, &clientRect, TRUE);
2380 }
2381
2382 static LRESULT
2383 TAB_Paint (HWND hwnd, WPARAM wParam)
2384 {
2385   HDC hdc;
2386   PAINTSTRUCT ps;
2387
2388   hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2389
2390   TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2391         ps.fErase,
2392         ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2393
2394   if (ps.fErase)
2395       TAB_EraseBackground (hwnd, hdc);
2396
2397   TAB_Refresh (hwnd, hdc);
2398
2399   if(!wParam)
2400     EndPaint (hwnd, &ps);
2401
2402   return 0;
2403 }
2404
2405 static LRESULT
2406 TAB_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2407 {
2408   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2409   TCITEMA *pti;
2410   INT iItem;
2411   RECT rect;
2412
2413   GetClientRect (hwnd, &rect);
2414   TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2415         rect.top, rect.left, rect.bottom, rect.right);
2416
2417   pti = (TCITEMA *)lParam;
2418   iItem = (INT)wParam;
2419
2420   if (iItem < 0) return -1;
2421   if (iItem > infoPtr->uNumItem)
2422     iItem = infoPtr->uNumItem;
2423
2424   TAB_DumpItemExternalA(pti, iItem);
2425
2426
2427   if (infoPtr->uNumItem == 0) {
2428     infoPtr->items = Alloc (sizeof (TAB_ITEM));
2429     infoPtr->uNumItem++;
2430     infoPtr->iSelected = 0;
2431   }
2432   else {
2433     TAB_ITEM *oldItems = infoPtr->items;
2434
2435     infoPtr->uNumItem++;
2436     infoPtr->items = Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2437
2438     /* pre insert copy */
2439     if (iItem > 0) {
2440       memcpy (&infoPtr->items[0], &oldItems[0],
2441               iItem * sizeof(TAB_ITEM));
2442     }
2443
2444     /* post insert copy */
2445     if (iItem < infoPtr->uNumItem - 1) {
2446       memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2447               (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2448
2449     }
2450
2451     if (iItem <= infoPtr->iSelected)
2452       infoPtr->iSelected++;
2453
2454     Free (oldItems);
2455   }
2456
2457   infoPtr->items[iItem].mask = pti->mask;
2458   if (pti->mask & TCIF_TEXT)
2459     Str_SetPtrAtoW (&infoPtr->items[iItem].pszText, pti->pszText);
2460
2461   if (pti->mask & TCIF_IMAGE)
2462     infoPtr->items[iItem].iImage = pti->iImage;
2463
2464   if (pti->mask & TCIF_PARAM)
2465     infoPtr->items[iItem].lParam = pti->lParam;
2466
2467   TAB_SetItemBounds(hwnd);
2468   if (infoPtr->uNumItem > 1)
2469     TAB_InvalidateTabArea(hwnd, infoPtr);
2470   else
2471     InvalidateRect(hwnd, NULL, TRUE);
2472
2473   TRACE("[%p]: added item %d %s\n",
2474         hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2475
2476   return iItem;
2477 }
2478
2479
2480 static LRESULT
2481 TAB_InsertItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2482 {
2483   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2484   TCITEMW *pti;
2485   INT iItem;
2486   RECT rect;
2487
2488   GetClientRect (hwnd, &rect);
2489   TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2490         rect.top, rect.left, rect.bottom, rect.right);
2491
2492   pti = (TCITEMW *)lParam;
2493   iItem = (INT)wParam;
2494
2495   if (iItem < 0) return -1;
2496   if (iItem > infoPtr->uNumItem)
2497     iItem = infoPtr->uNumItem;
2498
2499   TAB_DumpItemExternalW(pti, iItem);
2500
2501   if (infoPtr->uNumItem == 0) {
2502     infoPtr->items = Alloc (sizeof (TAB_ITEM));
2503     infoPtr->uNumItem++;
2504     infoPtr->iSelected = 0;
2505   }
2506   else {
2507     TAB_ITEM *oldItems = infoPtr->items;
2508
2509     infoPtr->uNumItem++;
2510     infoPtr->items = Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2511
2512     /* pre insert copy */
2513     if (iItem > 0) {
2514       memcpy (&infoPtr->items[0], &oldItems[0],
2515               iItem * sizeof(TAB_ITEM));
2516     }
2517
2518     /* post insert copy */
2519     if (iItem < infoPtr->uNumItem - 1) {
2520       memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2521               (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2522
2523   }
2524
2525     if (iItem <= infoPtr->iSelected)
2526       infoPtr->iSelected++;
2527
2528     Free (oldItems);
2529   }
2530
2531   infoPtr->items[iItem].mask = pti->mask;
2532   if (pti->mask & TCIF_TEXT)
2533     Str_SetPtrW (&infoPtr->items[iItem].pszText, pti->pszText);
2534
2535   if (pti->mask & TCIF_IMAGE)
2536     infoPtr->items[iItem].iImage = pti->iImage;
2537
2538   if (pti->mask & TCIF_PARAM)
2539     infoPtr->items[iItem].lParam = pti->lParam;
2540
2541   TAB_SetItemBounds(hwnd);
2542   if (infoPtr->uNumItem > 1)
2543     TAB_InvalidateTabArea(hwnd, infoPtr);
2544   else
2545     InvalidateRect(hwnd, NULL, TRUE);
2546
2547   TRACE("[%p]: added item %d %s\n",
2548         hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2549
2550   return iItem;
2551 }
2552
2553
2554 static LRESULT
2555 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2556 {
2557   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2558   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2559   LONG lResult = 0;
2560
2561   TRACE("\n");
2562   if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
2563   {
2564     lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2565     /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use default. */    
2566     if (LOWORD(lParam)) infoPtr->tabWidth = max((INT)LOWORD(lParam), infoPtr->tabMinWidth);
2567     if (HIWORD(lParam)) infoPtr->tabHeight = (INT)HIWORD(lParam);
2568     TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2569           HIWORD(lResult), LOWORD(lResult),
2570           infoPtr->tabHeight, infoPtr->tabWidth);
2571   }
2572   infoPtr->fSizeSet = TRUE;
2573
2574   return lResult;
2575 }
2576
2577 static LRESULT
2578 TAB_SetMinTabWidth (HWND hwnd, LPARAM lParam)
2579 {
2580   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2581   INT cx = (INT)lParam;
2582   INT oldcx;
2583
2584   if (infoPtr) {
2585     oldcx = infoPtr->tabMinWidth;
2586     infoPtr->tabMinWidth = (cx==-1)?DEFAULT_TAB_WIDTH:cx;
2587   } else
2588     return 0;
2589
2590   return oldcx;
2591 }
2592
2593 static LRESULT 
2594 TAB_HighlightItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2595 {
2596   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2597   INT iItem = (INT)wParam;
2598   BOOL fHighlight = (BOOL)LOWORD(lParam);
2599
2600   if ((infoPtr) && (iItem>=0) && (iItem<infoPtr->uNumItem)) {
2601     if (fHighlight)
2602       infoPtr->items[iItem].dwState |= TCIS_HIGHLIGHTED;
2603     else
2604       infoPtr->items[iItem].dwState &= ~TCIS_HIGHLIGHTED;
2605   } else
2606     return FALSE;
2607
2608   return TRUE;
2609 }
2610
2611 static LRESULT
2612 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2613 {
2614   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2615   TCITEMA *tabItem;
2616   TAB_ITEM *wineItem;
2617   INT    iItem;
2618
2619   iItem = (INT)wParam;
2620   tabItem = (LPTCITEMA)lParam;
2621
2622   TRACE("%d %p\n", iItem, tabItem);
2623   if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2624
2625   TAB_DumpItemExternalA(tabItem, iItem);
2626
2627   wineItem = &infoPtr->items[iItem];
2628
2629   if (tabItem->mask & TCIF_IMAGE)
2630     wineItem->iImage = tabItem->iImage;
2631
2632   if (tabItem->mask & TCIF_PARAM)
2633     wineItem->lParam = tabItem->lParam;
2634
2635   if (tabItem->mask & TCIF_RTLREADING)
2636     FIXME("TCIF_RTLREADING\n");
2637
2638   if (tabItem->mask & TCIF_STATE)
2639     wineItem->dwState = tabItem->dwState;
2640
2641   if (tabItem->mask & TCIF_TEXT)
2642    Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2643
2644   /* Update and repaint tabs */
2645   TAB_SetItemBounds(hwnd);
2646   TAB_InvalidateTabArea(hwnd,infoPtr);
2647
2648   return TRUE;
2649   }
2650
2651
2652 static LRESULT
2653 TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2654 {
2655   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2656   TCITEMW *tabItem;
2657   TAB_ITEM *wineItem;
2658   INT    iItem;
2659
2660   iItem = (INT)wParam;
2661   tabItem = (LPTCITEMW)lParam;
2662
2663   TRACE("%d %p\n", iItem, tabItem);
2664   if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2665
2666   TAB_DumpItemExternalW(tabItem, iItem);
2667
2668   wineItem = &infoPtr->items[iItem];
2669
2670   if (tabItem->mask & TCIF_IMAGE)
2671     wineItem->iImage = tabItem->iImage;
2672
2673   if (tabItem->mask & TCIF_PARAM)
2674     wineItem->lParam = tabItem->lParam;
2675
2676   if (tabItem->mask & TCIF_RTLREADING)
2677     FIXME("TCIF_RTLREADING\n");
2678
2679   if (tabItem->mask & TCIF_STATE)
2680     wineItem->dwState = tabItem->dwState;
2681
2682   if (tabItem->mask & TCIF_TEXT)
2683    Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2684
2685   /* Update and repaint tabs */
2686   TAB_SetItemBounds(hwnd);
2687   TAB_InvalidateTabArea(hwnd,infoPtr);
2688
2689   return TRUE;
2690 }
2691
2692
2693 static LRESULT
2694 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2695 {
2696    TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2697
2698    return infoPtr->uNumItem;
2699 }
2700
2701
2702 static LRESULT
2703 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2704 {
2705    TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2706    TCITEMA *tabItem;
2707    TAB_ITEM *wineItem;
2708    INT    iItem;
2709
2710   iItem = (INT)wParam;
2711   tabItem = (LPTCITEMA)lParam;
2712   TRACE("\n");
2713   if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2714     return FALSE;
2715
2716   wineItem = &infoPtr->items[iItem];
2717
2718   if (tabItem->mask & TCIF_IMAGE)
2719     tabItem->iImage = wineItem->iImage;
2720
2721   if (tabItem->mask & TCIF_PARAM)
2722     tabItem->lParam = wineItem->lParam;
2723
2724   if (tabItem->mask & TCIF_RTLREADING)
2725     FIXME("TCIF_RTLREADING\n");
2726
2727   if (tabItem->mask & TCIF_STATE)
2728     tabItem->dwState = wineItem->dwState;
2729
2730   if (tabItem->mask & TCIF_TEXT)
2731    Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2732
2733   TAB_DumpItemExternalA(tabItem, iItem);
2734
2735   return TRUE;
2736 }
2737
2738
2739 static LRESULT
2740 TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2741 {
2742   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2743   TCITEMW *tabItem;
2744   TAB_ITEM *wineItem;
2745   INT    iItem;
2746
2747   iItem = (INT)wParam;
2748   tabItem = (LPTCITEMW)lParam;
2749   TRACE("\n");
2750   if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2751     return FALSE;
2752
2753   wineItem=& infoPtr->items[iItem];
2754
2755   if (tabItem->mask & TCIF_IMAGE)
2756     tabItem->iImage = wineItem->iImage;
2757
2758   if (tabItem->mask & TCIF_PARAM)
2759     tabItem->lParam = wineItem->lParam;
2760
2761   if (tabItem->mask & TCIF_RTLREADING)
2762     FIXME("TCIF_RTLREADING\n");
2763
2764   if (tabItem->mask & TCIF_STATE)
2765     tabItem->dwState = wineItem->dwState;
2766
2767   if (tabItem->mask & TCIF_TEXT)
2768    Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2769
2770   TAB_DumpItemExternalW(tabItem, iItem);
2771
2772   return TRUE;
2773 }
2774
2775
2776 static LRESULT
2777 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2778 {
2779   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2780   INT iItem = (INT) wParam;
2781   BOOL bResult = FALSE;
2782
2783   if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2784   {
2785     TAB_ITEM *oldItems = infoPtr->items;
2786
2787     infoPtr->uNumItem--;
2788     infoPtr->items = Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
2789
2790     if (iItem > 0)
2791       memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
2792
2793     if (iItem < infoPtr->uNumItem)
2794       memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
2795               (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
2796
2797     Free(oldItems);
2798
2799     /* Readjust the selected index */
2800     if ((iItem == infoPtr->iSelected) && (iItem > 0))
2801       infoPtr->iSelected--;
2802
2803     if (iItem < infoPtr->iSelected)
2804       infoPtr->iSelected--;
2805
2806     if (infoPtr->uNumItem == 0)
2807       infoPtr->iSelected = -1;
2808
2809     /* Reposition and repaint tabs */
2810     TAB_SetItemBounds(hwnd);
2811     TAB_InvalidateTabArea(hwnd,infoPtr);
2812
2813     bResult = TRUE;
2814   }
2815
2816   return bResult;
2817 }
2818
2819 static LRESULT
2820 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2821 {
2822    TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2823
2824   Free (infoPtr->items);
2825   infoPtr->uNumItem = 0;
2826   infoPtr->iSelected = -1;
2827   if (infoPtr->iHotTracked >= 0)
2828     KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2829   infoPtr->iHotTracked = -1;
2830
2831   TAB_SetItemBounds(hwnd);
2832   TAB_InvalidateTabArea(hwnd,infoPtr);
2833   return TRUE;
2834 }
2835
2836
2837 static LRESULT
2838 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2839 {
2840   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2841
2842   TRACE("\n");
2843   return (LRESULT)infoPtr->hFont;
2844 }
2845
2846 static LRESULT
2847 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2848
2849 {
2850   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2851
2852   TRACE("%x %lx\n",wParam, lParam);
2853
2854   infoPtr->hFont = (HFONT)wParam;
2855
2856   TAB_SetItemBounds(hwnd);
2857
2858   TAB_InvalidateTabArea(hwnd, infoPtr);
2859
2860   return 0;
2861 }
2862
2863
2864 static LRESULT
2865 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2866 {
2867   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2868
2869   TRACE("\n");
2870   return (LRESULT)infoPtr->himl;
2871 }
2872
2873 static LRESULT
2874 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2875 {
2876     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2877     HIMAGELIST himlPrev;
2878
2879     TRACE("\n");
2880     himlPrev = infoPtr->himl;
2881     infoPtr->himl= (HIMAGELIST)lParam;
2882     return (LRESULT)himlPrev;
2883 }
2884
2885 static LRESULT
2886 TAB_GetUnicodeFormat (HWND hwnd)
2887 {
2888     TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2889     return infoPtr->bUnicode;
2890 }
2891
2892 static LRESULT
2893 TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
2894 {
2895     TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2896     BOOL bTemp = infoPtr->bUnicode;
2897
2898     infoPtr->bUnicode = (BOOL)wParam;
2899
2900     return bTemp;
2901 }
2902
2903 static LRESULT
2904 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2905
2906 {
2907 /* I'm not really sure what the following code was meant to do.
2908    This is what it is doing:
2909    When WM_SIZE is sent with SIZE_RESTORED, the control
2910    gets positioned in the top left corner.
2911
2912   RECT parent_rect;
2913   HWND parent;
2914   UINT uPosFlags,cx,cy;
2915
2916   uPosFlags=0;
2917   if (!wParam) {
2918     parent = GetParent (hwnd);
2919     GetClientRect(parent, &parent_rect);
2920     cx=LOWORD (lParam);
2921     cy=HIWORD (lParam);
2922     if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2923         uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2924
2925     SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2926             cx, cy, uPosFlags | SWP_NOZORDER);
2927   } else {
2928     FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2929   } */
2930
2931   /* Recompute the size/position of the tabs. */
2932   TAB_SetItemBounds (hwnd);
2933
2934   /* Force a repaint of the control. */
2935   InvalidateRect(hwnd, NULL, TRUE);
2936
2937   return 0;
2938 }
2939
2940
2941 static LRESULT
2942 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2943 {
2944   TAB_INFO *infoPtr;
2945   TEXTMETRICA fontMetrics;
2946   HDC hdc;
2947   HFONT hOldFont;
2948   DWORD dwStyle;
2949
2950   infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
2951
2952   SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
2953
2954   infoPtr->uNumItem        = 0;
2955   infoPtr->uNumRows        = 0;
2956   infoPtr->uHItemPadding   = 6;
2957   infoPtr->uVItemPadding   = 3;
2958   infoPtr->hFont           = 0;
2959   infoPtr->items           = 0;
2960   infoPtr->hcurArrow       = LoadCursorA (0, (LPSTR)IDC_ARROW);
2961   infoPtr->iSelected       = -1;
2962   infoPtr->iHotTracked     = -1;
2963   infoPtr->uFocus          = -1;
2964   infoPtr->hwndToolTip     = 0;
2965   infoPtr->DoRedraw        = TRUE;
2966   infoPtr->needsScrolling  = FALSE;
2967   infoPtr->hwndUpDown      = 0;
2968   infoPtr->leftmostVisible = 0;
2969   infoPtr->fSizeSet        = FALSE;
2970   infoPtr->bUnicode        = IsWindowUnicode (hwnd);
2971
2972   TRACE("Created tab control, hwnd [%p]\n", hwnd);
2973
2974   /* The tab control always has the WS_CLIPSIBLINGS style. Even
2975      if you don't specify it in CreateWindow. This is necessary in
2976      order for paint to work correctly. This follows windows behaviour. */
2977   dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
2978   SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2979
2980   if (dwStyle & TCS_TOOLTIPS) {
2981     /* Create tooltip control */
2982     infoPtr->hwndToolTip =
2983       CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2984                        CW_USEDEFAULT, CW_USEDEFAULT,
2985                        CW_USEDEFAULT, CW_USEDEFAULT,
2986                        hwnd, 0, 0, 0);
2987
2988     /* Send NM_TOOLTIPSCREATED notification */
2989     if (infoPtr->hwndToolTip) {
2990       NMTOOLTIPSCREATED nmttc;
2991
2992       nmttc.hdr.hwndFrom = hwnd;
2993       nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
2994       nmttc.hdr.code = NM_TOOLTIPSCREATED;
2995       nmttc.hwndToolTips = infoPtr->hwndToolTip;
2996
2997       SendMessageA (GetParent (hwnd), WM_NOTIFY,
2998                     (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
2999     }
3000   }
3001
3002   /*
3003    * We need to get text information so we need a DC and we need to select
3004    * a font.
3005    */
3006   hdc = GetDC(hwnd);
3007   hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3008
3009   /* Use the system font to determine the initial height of a tab. */
3010   GetTextMetricsA(hdc, &fontMetrics);
3011
3012   /*
3013    * Make sure there is enough space for the letters + growing the
3014    * selected item + extra space for the selected item.
3015    */
3016   infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3017                        ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
3018                         infoPtr->uVItemPadding;
3019
3020   /* Initialize the width of a tab. */
3021   infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
3022   /* The minimum width is the default width at creation */
3023   infoPtr->tabMinWidth = DEFAULT_TAB_WIDTH;
3024
3025   TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3026
3027   SelectObject (hdc, hOldFont);
3028   ReleaseDC(hwnd, hdc);
3029
3030   return 0;
3031 }
3032
3033 static LRESULT
3034 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
3035 {
3036   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3037   INT iItem;
3038
3039   if (!infoPtr)
3040       return 0;
3041
3042   if (infoPtr->items) {
3043     for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3044       if (infoPtr->items[iItem].pszText)
3045         Free (infoPtr->items[iItem].pszText);
3046     }
3047     Free (infoPtr->items);
3048   }
3049
3050   if (infoPtr->hwndToolTip)
3051     DestroyWindow (infoPtr->hwndToolTip);
3052
3053   if (infoPtr->hwndUpDown)
3054     DestroyWindow(infoPtr->hwndUpDown);
3055
3056   if (infoPtr->iHotTracked >= 0)
3057     KillTimer(hwnd, TAB_HOTTRACK_TIMER);
3058
3059   Free (infoPtr);
3060   SetWindowLongA(hwnd, 0, 0);
3061   return 0;
3062 }
3063
3064 static LRESULT WINAPI
3065 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3066 {
3067
3068     TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3069     if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
3070       return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3071
3072     switch (uMsg)
3073     {
3074     case TCM_GETIMAGELIST:
3075       return TAB_GetImageList (hwnd, wParam, lParam);
3076
3077     case TCM_SETIMAGELIST:
3078       return TAB_SetImageList (hwnd, wParam, lParam);
3079
3080     case TCM_GETITEMCOUNT:
3081       return TAB_GetItemCount (hwnd, wParam, lParam);
3082
3083     case TCM_GETITEMA:
3084       return TAB_GetItemA (hwnd, wParam, lParam);
3085
3086     case TCM_GETITEMW:
3087       return TAB_GetItemW (hwnd, wParam, lParam);
3088
3089     case TCM_SETITEMA:
3090       return TAB_SetItemA (hwnd, wParam, lParam);
3091
3092     case TCM_SETITEMW:
3093       return TAB_SetItemW (hwnd, wParam, lParam);
3094
3095     case TCM_DELETEITEM:
3096       return TAB_DeleteItem (hwnd, wParam, lParam);
3097
3098     case TCM_DELETEALLITEMS:
3099      return TAB_DeleteAllItems (hwnd, wParam, lParam);
3100
3101     case TCM_GETITEMRECT:
3102      return TAB_GetItemRect (hwnd, wParam, lParam);
3103
3104     case TCM_GETCURSEL:
3105       return TAB_GetCurSel (hwnd);
3106
3107     case TCM_HITTEST:
3108       return TAB_HitTest (hwnd, wParam, lParam);
3109
3110     case TCM_SETCURSEL:
3111       return TAB_SetCurSel (hwnd, wParam);
3112
3113     case TCM_INSERTITEMA:
3114       return TAB_InsertItemA (hwnd, wParam, lParam);
3115
3116     case TCM_INSERTITEMW:
3117       return TAB_InsertItemW (hwnd, wParam, lParam);
3118
3119     case TCM_SETITEMEXTRA:
3120       FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
3121       return 0;
3122
3123     case TCM_ADJUSTRECT:
3124       return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
3125
3126     case TCM_SETITEMSIZE:
3127       return TAB_SetItemSize (hwnd, wParam, lParam);
3128
3129     case TCM_REMOVEIMAGE:
3130       FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3131       return 0;
3132
3133     case TCM_SETPADDING:
3134       return TAB_SetPadding (hwnd, wParam, lParam);
3135
3136     case TCM_GETROWCOUNT:
3137       return TAB_GetRowCount(hwnd);
3138
3139     case TCM_GETUNICODEFORMAT:
3140       return TAB_GetUnicodeFormat (hwnd);
3141
3142     case TCM_SETUNICODEFORMAT:
3143       return TAB_SetUnicodeFormat (hwnd, wParam);
3144
3145     case TCM_HIGHLIGHTITEM:
3146       return TAB_HighlightItem (hwnd, wParam, lParam);
3147
3148     case TCM_GETTOOLTIPS:
3149       return TAB_GetToolTips (hwnd, wParam, lParam);
3150
3151     case TCM_SETTOOLTIPS:
3152       return TAB_SetToolTips (hwnd, wParam, lParam);
3153
3154     case TCM_GETCURFOCUS:
3155       return TAB_GetCurFocus (hwnd);
3156
3157     case TCM_SETCURFOCUS:
3158       return TAB_SetCurFocus (hwnd, wParam);
3159
3160     case TCM_SETMINTABWIDTH:
3161       return TAB_SetMinTabWidth(hwnd, lParam);
3162
3163     case TCM_DESELECTALL:
3164       FIXME("Unimplemented msg TCM_DESELECTALL\n");
3165       return 0;
3166
3167     case TCM_GETEXTENDEDSTYLE:
3168       FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3169       return 0;
3170
3171     case TCM_SETEXTENDEDSTYLE:
3172       FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3173       return 0;
3174
3175     case WM_GETFONT:
3176       return TAB_GetFont (hwnd, wParam, lParam);
3177
3178     case WM_SETFONT:
3179       return TAB_SetFont (hwnd, wParam, lParam);
3180
3181     case WM_CREATE:
3182       return TAB_Create (hwnd, wParam, lParam);
3183
3184     case WM_NCDESTROY:
3185       return TAB_Destroy (hwnd, wParam, lParam);
3186
3187     case WM_GETDLGCODE:
3188       return DLGC_WANTARROWS | DLGC_WANTCHARS;
3189
3190     case WM_LBUTTONDOWN:
3191       return TAB_LButtonDown (hwnd, wParam, lParam);
3192
3193     case WM_LBUTTONUP:
3194       return TAB_LButtonUp (hwnd, wParam, lParam);
3195
3196     case WM_NOTIFY:
3197       return SendMessageA(GetParent(hwnd), WM_NOTIFY, wParam, lParam);
3198
3199     case WM_RBUTTONDOWN:
3200       return TAB_RButtonDown (hwnd, wParam, lParam);
3201
3202     case WM_MOUSEMOVE:
3203       return TAB_MouseMove (hwnd, wParam, lParam);
3204
3205     case WM_ERASEBKGND:
3206       return TAB_EraseBackground (hwnd, (HDC)wParam);
3207
3208     case WM_PAINT:
3209       return TAB_Paint (hwnd, wParam);
3210
3211     case WM_SIZE:
3212       return TAB_Size (hwnd, wParam, lParam);
3213
3214     case WM_SETREDRAW:
3215       return TAB_SetRedraw (hwnd, wParam);
3216
3217     case WM_HSCROLL:
3218       return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3219
3220     case WM_STYLECHANGED:
3221       TAB_SetItemBounds (hwnd);
3222       InvalidateRect(hwnd, NULL, TRUE);
3223       return 0;
3224
3225     case WM_SYSCOLORCHANGE:
3226       COMCTL32_RefreshSysColors();
3227       return 0;
3228
3229     case WM_KILLFOCUS:
3230     case WM_SETFOCUS:
3231       return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
3232
3233     case WM_KEYUP:
3234       return TAB_KeyUp(hwnd, wParam);
3235     case WM_NCHITTEST:
3236       return TAB_NCHitTest(hwnd, lParam);
3237
3238     default:
3239       if ((uMsg >= WM_USER) && (uMsg < WM_APP))
3240         WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3241              uMsg, wParam, lParam);
3242       return DefWindowProcA(hwnd, uMsg, wParam, lParam);
3243     }
3244
3245     return 0;
3246 }
3247
3248
3249 VOID
3250 TAB_Register (void)
3251 {
3252   WNDCLASSA wndClass;
3253
3254   ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3255   wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3256   wndClass.lpfnWndProc   = (WNDPROC)TAB_WindowProc;
3257   wndClass.cbClsExtra    = 0;
3258   wndClass.cbWndExtra    = sizeof(TAB_INFO *);
3259   wndClass.hCursor       = LoadCursorA (0, (LPSTR)IDC_ARROW);
3260   wndClass.hbrBackground = NULL;
3261   wndClass.lpszClassName = WC_TABCONTROLA;
3262
3263   RegisterClassA (&wndClass);
3264 }
3265
3266
3267 VOID
3268 TAB_Unregister (void)
3269 {
3270     UnregisterClassA (WC_TABCONTROLA, NULL);
3271 }