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