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