Fix signed/unsigned comparison warnings.
[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 *)GetWindowLongPtrW(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 = GetWindowLongPtrW(hwnd, GWLP_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   UINT        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,iRow;
1247       UINT 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     drawRect->top += 2;
1641     drawRect->right -= 1;
1642     if ( iItem == infoPtr->iSelected )
1643     {
1644         drawRect->right -= 1;
1645         drawRect->left += 1;
1646     }
1647
1648     /*
1649      * get the control id
1650      */
1651     id = (UINT)GetWindowLongPtrW( hwnd, GWLP_ID );
1652
1653     /*
1654      * put together the DRAWITEMSTRUCT
1655      */
1656     dis.CtlType    = ODT_TAB;
1657     dis.CtlID      = id;
1658     dis.itemID     = iItem;
1659     dis.itemAction = ODA_DRAWENTIRE;
1660     dis.itemState = 0;
1661     if ( iItem == infoPtr->iSelected )
1662       dis.itemState |= ODS_SELECTED;
1663     if (infoPtr->uFocus == iItem) 
1664       dis.itemState |= ODS_FOCUS;
1665     dis.hwndItem = hwnd;                /* */
1666     dis.hDC      = hdc;
1667     CopyRect(&dis.rcItem,drawRect);
1668     dis.itemData = 0;
1669     memcpy( &dis.itemData, infoPtr->items[iItem].extra, min(sizeof(dis.itemData),infoPtr->cbInfo) );
1670
1671     /*
1672      * send the draw message
1673      */
1674     SendMessageA( infoPtr->hwndNotify, WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1675   }
1676   else
1677   {
1678     RECT rcTemp;
1679     RECT rcImage;
1680
1681     /* used to center the icon and text in the tab */
1682     RECT rcText;
1683     INT center_offset_h, center_offset_v;
1684
1685     /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1686     rcImage = *drawRect;
1687
1688     rcTemp = *drawRect;
1689
1690     rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1691
1692     /* get the rectangle that the text fits in */
1693     if (infoPtr->items[iItem].pszText)
1694     {
1695       DrawTextW(hdc, infoPtr->items[iItem].pszText, -1,
1696                 &rcText, DT_CALCRECT);
1697     }
1698     /*
1699      * If not owner draw, then do the drawing ourselves.
1700      *
1701      * Draw the icon.
1702      */
1703     if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE))
1704     {
1705       INT cx;
1706       INT cy;
1707       
1708       ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1709
1710       if(lStyle & TCS_VERTICAL)
1711       {
1712         center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right  - rcText.left))) / 2;
1713         center_offset_v = ((drawRect->right - drawRect->left) - (cx + infoPtr->uVItemPadding)) / 2;
1714       }
1715       else
1716       {
1717         center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right  - rcText.left))) / 2;
1718         center_offset_v = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uVItemPadding)) / 2;
1719       }
1720
1721       if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1722         center_offset_h = infoPtr->uHItemPadding;
1723
1724       if (center_offset_h < 2)
1725         center_offset_h = 2;
1726         
1727       if (center_offset_v < 0)
1728         center_offset_v = 0;
1729         
1730       TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1731           debugstr_w(infoPtr->items[iItem].pszText), center_offset_h, center_offset_v,
1732           drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1733           (rcText.right-rcText.left));
1734
1735       if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1736       {
1737         rcImage.top = drawRect->top + center_offset_h;
1738         /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1739         /* right side of the tab, but the image still uses the left as its x position */
1740         /* this keeps the image always drawn off of the same side of the tab */
1741         rcImage.left = drawRect->right - cx - center_offset_v;
1742         drawRect->top += cy + infoPtr->uHItemPadding;
1743       }
1744       else if(lStyle & TCS_VERTICAL)
1745       {
1746         rcImage.top  = drawRect->bottom - cy - center_offset_h;
1747         rcImage.left = drawRect->left + center_offset_v;
1748         drawRect->bottom -= cy + infoPtr->uHItemPadding;
1749       }
1750       else /* normal style, whether TCS_BOTTOM or not */
1751       {
1752         rcImage.left = drawRect->left + center_offset_h;
1753         rcImage.top = drawRect->top + center_offset_v;
1754         drawRect->left += cx + infoPtr->uHItemPadding;
1755       }
1756
1757       TRACE("drawing image=%d, left=%ld, top=%ld\n",
1758             infoPtr->items[iItem].iImage, rcImage.left, rcImage.top-1);
1759       ImageList_Draw
1760         (
1761         infoPtr->himl,
1762         infoPtr->items[iItem].iImage,
1763         hdc,
1764         rcImage.left,
1765         rcImage.top,
1766         ILD_NORMAL
1767         );
1768     }
1769
1770     /* Now position text */
1771     if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT)
1772       center_offset_h = infoPtr->uHItemPadding;
1773     else
1774       if(lStyle & TCS_VERTICAL)
1775         center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1776       else
1777         center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1778
1779     if(lStyle & TCS_VERTICAL)
1780     {
1781       if(lStyle & TCS_BOTTOM)
1782         drawRect->top+=center_offset_h;
1783       else
1784         drawRect->bottom-=center_offset_h;
1785
1786       center_offset_v = ((drawRect->right - drawRect->left) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1787     }
1788     else
1789     {
1790       drawRect->left += center_offset_h;
1791       center_offset_v = ((drawRect->bottom - drawRect->top) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1792     }
1793
1794     if (center_offset_v < 0)
1795       center_offset_v = 0;
1796
1797     if(lStyle & TCS_VERTICAL)
1798       drawRect->left += center_offset_v;
1799     else
1800       drawRect->top += center_offset_v;
1801
1802     /* Draw the text */
1803     if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1804     {
1805       LOGFONTA logfont;
1806       HFONT hFont = 0;
1807       INT nEscapement = 900;
1808       INT nOrientation = 900;
1809
1810       if(lStyle & TCS_BOTTOM)
1811       {
1812         nEscapement = -900;
1813         nOrientation = -900;
1814       }
1815
1816       /* to get a font with the escapement and orientation we are looking for, we need to */
1817       /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1818       if (!GetObjectA((infoPtr->hFont) ?
1819                 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1820                 sizeof(LOGFONTA),&logfont))
1821       {
1822         INT iPointSize = 9;
1823
1824         lstrcpyA(logfont.lfFaceName, "Arial");
1825         logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1826                                     72);
1827         logfont.lfWeight = FW_NORMAL;
1828         logfont.lfItalic = 0;
1829         logfont.lfUnderline = 0;
1830         logfont.lfStrikeOut = 0;
1831       }
1832
1833       logfont.lfEscapement = nEscapement;
1834       logfont.lfOrientation = nOrientation;
1835       hFont = CreateFontIndirectA(&logfont);
1836       SelectObject(hdc, hFont);
1837
1838       if (infoPtr->items[iItem].pszText)
1839       {
1840         ExtTextOutW(hdc,
1841         (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1842         (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1843         ETO_CLIPPED,
1844         drawRect,
1845         infoPtr->items[iItem].pszText,
1846         lstrlenW(infoPtr->items[iItem].pszText),
1847         0);
1848       }
1849
1850       DeleteObject(hFont);
1851     }
1852     else
1853     {
1854       TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1855           debugstr_w(infoPtr->items[iItem].pszText), center_offset_h, center_offset_v,
1856           drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1857           (rcText.right-rcText.left));
1858       if (infoPtr->items[iItem].pszText)
1859       {
1860         DrawTextW
1861         (
1862           hdc,
1863           infoPtr->items[iItem].pszText,
1864           lstrlenW(infoPtr->items[iItem].pszText),
1865           drawRect,
1866           DT_LEFT | DT_SINGLELINE
1867         );
1868       }
1869     }
1870
1871     *drawRect = rcTemp; /* restore drawRect */
1872   }
1873
1874   /*
1875   * Cleanup
1876   */
1877   SelectObject(hdc, hOldFont);
1878   SetBkMode(hdc, oldBkMode);
1879   SelectObject(hdc, holdPen);
1880   DeleteObject( htextPen );
1881 }
1882
1883 /******************************************************************************
1884  * TAB_DrawItem
1885  *
1886  * This method is used to draw a single tab into the tab control.
1887  */
1888 static void TAB_DrawItem(
1889   HWND hwnd,
1890   HDC  hdc,
1891   INT  iItem)
1892 {
1893   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1894   LONG      lStyle  = GetWindowLongA(hwnd, GWL_STYLE);
1895   RECT      itemRect;
1896   RECT      selectedRect;
1897   BOOL      isVisible;
1898   RECT      r, fillRect, r1;
1899   INT       clRight = 0;
1900   INT       clBottom = 0;
1901   COLORREF  bkgnd, corner;
1902
1903   /*
1904    * Get the rectangle for the item.
1905    */
1906   isVisible = TAB_InternalGetItemRect(hwnd,
1907                                       infoPtr,
1908                                       iItem,
1909                                       &itemRect,
1910                                       &selectedRect);
1911
1912   if (isVisible)
1913   {
1914     RECT rUD, rC;
1915
1916     /* Clip UpDown control to not draw over it */
1917     if (infoPtr->needsScrolling)
1918     {
1919       GetWindowRect(hwnd, &rC);
1920       GetWindowRect(infoPtr->hwndUpDown, &rUD);
1921       ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
1922     }
1923
1924     /* If you need to see what the control is doing,
1925      * then override these variables. They will change what
1926      * fill colors are used for filling the tabs, and the
1927      * corners when drawing the edge.
1928      */
1929     bkgnd = comctl32_color.clrBtnFace;
1930     corner = comctl32_color.clrBtnFace;
1931
1932     if (lStyle & TCS_BUTTONS)
1933     {
1934       /* Get item rectangle */
1935       r = itemRect;
1936
1937       /* Separators between flat buttons */
1938       if (lStyle & TCS_FLATBUTTONS)
1939       {
1940         r1 = r;
1941         r1.right += (FLAT_BTN_SPACINGX -2);
1942         DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1943       }
1944
1945       if (iItem == infoPtr->iSelected)
1946       {
1947         DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1948         
1949         OffsetRect(&r, 1, 1);
1950       }
1951       else  /* ! selected */
1952       {
1953         if (!(lStyle & TCS_FLATBUTTONS))
1954           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1955       }
1956     }
1957     else /* !TCS_BUTTONS */
1958     {
1959       /* We draw a rectangle of different sizes depending on the selection
1960        * state. */
1961       if (iItem == infoPtr->iSelected) {
1962         RECT rect;
1963         GetClientRect (hwnd, &rect);
1964         clRight = rect.right;
1965         clBottom = rect.bottom;
1966         r = selectedRect;
1967       }
1968       else
1969         r = itemRect;
1970
1971       /*
1972        * Erase the background. (Delay it but setup rectangle.)
1973        * This is necessary when drawing the selected item since it is larger
1974        * than the others, it might overlap with stuff already drawn by the
1975        * other tabs
1976        */
1977       fillRect = r;
1978
1979       if(lStyle & TCS_VERTICAL)
1980       {
1981         /* These are for adjusting the drawing of a Selected tab      */
1982         /* The initial values are for the normal case of non-Selected */
1983         int ZZ = 1;   /* Do not strech if selected */
1984         if (iItem == infoPtr->iSelected) {
1985             ZZ = 0;
1986
1987             /* if leftmost draw the line longer */
1988             if(selectedRect.top == 0)
1989                 fillRect.top += CONTROL_BORDER_SIZEY;
1990             /* if rightmost draw the line longer */
1991             if(selectedRect.bottom == clBottom)
1992                 fillRect.bottom -= CONTROL_BORDER_SIZEY;
1993         }
1994
1995         if (lStyle & TCS_BOTTOM)
1996         {
1997           /* Adjust both rectangles to match native */
1998           r.left += (1-ZZ);
1999
2000           TRACE("<right> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2001                 iItem,
2002                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2003                 r.left,r.top,r.right,r.bottom);
2004
2005           /* Clear interior */
2006           SetBkColor(hdc, bkgnd);
2007           ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2008
2009           /* Draw rectangular edge around tab */
2010           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
2011
2012           /* Now erase the top corner and draw diagonal edge */
2013           SetBkColor(hdc, corner);
2014           r1.left = r.right - ROUND_CORNER_SIZE - 1;
2015           r1.top = r.top;
2016           r1.right = r.right;
2017           r1.bottom = r1.top + ROUND_CORNER_SIZE;
2018           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2019           r1.right--;
2020           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2021
2022           /* Now erase the bottom corner and draw diagonal edge */
2023           r1.left = r.right - ROUND_CORNER_SIZE - 1;
2024           r1.bottom = r.bottom;
2025           r1.right = r.right;
2026           r1.top = r1.bottom - ROUND_CORNER_SIZE;
2027           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2028           r1.right--;
2029           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2030
2031           if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
2032               r1 = r;
2033               r1.right = r1.left;
2034               r1.left--;
2035               DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
2036           }
2037
2038         }
2039         else
2040         {
2041           TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2042                 iItem,
2043                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2044                 r.left,r.top,r.right,r.bottom);
2045
2046           /* Clear interior */
2047           SetBkColor(hdc, bkgnd);
2048           ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2049
2050           /* Draw rectangular edge around tab */
2051           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2052
2053           /* Now erase the top corner and draw diagonal edge */
2054           SetBkColor(hdc, corner);
2055           r1.left = r.left;
2056           r1.top = r.top;
2057           r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2058           r1.bottom = r1.top + ROUND_CORNER_SIZE;
2059           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2060           r1.left++;
2061           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2062
2063           /* Now erase the bottom corner and draw diagonal edge */
2064           r1.left = r.left;
2065           r1.bottom = r.bottom;
2066           r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2067           r1.top = r1.bottom - ROUND_CORNER_SIZE;
2068           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2069           r1.left++;
2070           DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2071         }
2072       }
2073       else  /* ! TCS_VERTICAL */
2074       {
2075         /* These are for adjusting the drawing of a Selected tab      */
2076         /* The initial values are for the normal case of non-Selected */
2077         if (iItem == infoPtr->iSelected) {
2078             /* if leftmost draw the line longer */
2079             if(selectedRect.left == 0)
2080                 fillRect.left += CONTROL_BORDER_SIZEX;
2081             /* if rightmost draw the line longer */
2082             if(selectedRect.right == clRight)
2083                 fillRect.right -= CONTROL_BORDER_SIZEX;
2084         }
2085
2086         if (lStyle & TCS_BOTTOM)
2087         {
2088           /* Adjust both rectangles for topmost row */
2089           if (infoPtr->items[iItem].rect.top == infoPtr->uNumRows-1)
2090           {
2091             fillRect.top -= 2;
2092             r.top -= 1;
2093           }
2094
2095           TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2096                 iItem,
2097                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2098                 r.left,r.top,r.right,r.bottom);
2099
2100           /* Clear interior */
2101           SetBkColor(hdc, bkgnd);
2102           ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2103
2104           /* Draw rectangular edge around tab */
2105           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2106
2107           /* Now erase the righthand corner and draw diagonal edge */
2108           SetBkColor(hdc, corner);
2109           r1.left = r.right - ROUND_CORNER_SIZE;
2110           r1.bottom = r.bottom;
2111           r1.right = r.right;
2112           r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2113           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2114           r1.bottom--;
2115           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2116
2117           /* Now erase the lefthand corner and draw diagonal edge */
2118           r1.left = r.left;
2119           r1.bottom = r.bottom;
2120           r1.right = r1.left + ROUND_CORNER_SIZE;
2121           r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2122           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2123           r1.bottom--;
2124           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2125
2126           if (iItem == infoPtr->iSelected)
2127           {
2128             r.top += 2;
2129             r.left += 1;
2130             if (selectedRect.left == 0)
2131             {
2132               r1 = r;
2133               r1.bottom = r1.top;
2134               r1.top--;
2135               DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2136             }
2137           }
2138
2139         }
2140         else
2141         {
2142           /* Adjust both rectangles for bottommost row */
2143           if (infoPtr->items[iItem].rect.top == infoPtr->uNumRows-1)
2144           {
2145             fillRect.bottom += 3;
2146             r.bottom += 2;
2147           }
2148
2149           TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2150                 iItem,
2151                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2152                 r.left,r.top,r.right,r.bottom);
2153
2154           /* Clear interior */
2155           SetBkColor(hdc, bkgnd);
2156           ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2157
2158           /* Draw rectangular edge around tab */
2159           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2160
2161           /* Now erase the righthand corner and draw diagonal edge */
2162           SetBkColor(hdc, corner);
2163           r1.left = r.right - ROUND_CORNER_SIZE;
2164           r1.top = r.top;
2165           r1.right = r.right;
2166           r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2167           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2168           r1.top++;
2169           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2170
2171           /* Now erase the lefthand corner and draw diagonal edge */
2172           r1.left = r.left;
2173           r1.top = r.top;
2174           r1.right = r1.left + ROUND_CORNER_SIZE;
2175           r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2176           ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2177           r1.top++;
2178           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2179         }
2180       }
2181     }
2182
2183     TAB_DumpItemInternal(infoPtr, iItem);
2184
2185     /* This modifies r to be the text rectangle. */
2186     TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
2187   }
2188 }
2189
2190 /******************************************************************************
2191  * TAB_DrawBorder
2192  *
2193  * This method is used to draw the raised border around the tab control
2194  * "content" area.
2195  */
2196 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
2197 {
2198   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2199   RECT rect;
2200   DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2201
2202   GetClientRect (hwnd, &rect);
2203
2204   /*
2205    * Adjust for the style
2206    */
2207
2208   if (infoPtr->uNumItem)
2209   {
2210     if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2211       rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2212     else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2213       rect.right  -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2214     else if(lStyle & TCS_VERTICAL)
2215       rect.left   += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2216     else /* not TCS_VERTICAL and not TCS_BOTTOM */
2217       rect.top    += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2218   }
2219
2220   TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2221         rect.left, rect.top, rect.right, rect.bottom);
2222
2223   DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2224 }
2225
2226 /******************************************************************************
2227  * TAB_Refresh
2228  *
2229  * This method repaints the tab control..
2230  */
2231 static void TAB_Refresh (HWND hwnd, HDC hdc)
2232 {
2233   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2234   HFONT hOldFont;
2235   INT i;
2236
2237   if (!infoPtr->DoRedraw)
2238     return;
2239
2240   hOldFont = SelectObject (hdc, infoPtr->hFont);
2241
2242   if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
2243   {
2244     for (i = 0; i < infoPtr->uNumItem; i++)
2245       TAB_DrawItem (hwnd, hdc, i);
2246   }
2247   else
2248   {
2249     /* Draw all the non selected item first */
2250     for (i = 0; i < infoPtr->uNumItem; i++)
2251     {
2252       if (i != infoPtr->iSelected)
2253         TAB_DrawItem (hwnd, hdc, i);
2254     }
2255
2256     /* Now, draw the border, draw it before the selected item
2257      * since the selected item overwrites part of the border. */
2258     TAB_DrawBorder (hwnd, hdc);
2259
2260     /* Then, draw the selected item */
2261     TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
2262
2263     /* If we haven't set the current focus yet, set it now.
2264      * Only happens when we first paint the tab controls */
2265     if (infoPtr->uFocus == -1)
2266       TAB_SetCurFocus(hwnd, infoPtr->iSelected);
2267   }
2268
2269   SelectObject (hdc, hOldFont);
2270 }
2271
2272 static DWORD
2273 TAB_GetRowCount (HWND hwnd )
2274 {
2275   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2276
2277   return infoPtr->uNumRows;
2278 }
2279
2280 static LRESULT
2281 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
2282 {
2283     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2284
2285   infoPtr->DoRedraw=(BOOL) wParam;
2286   return 0;
2287 }
2288
2289 static LRESULT TAB_EraseBackground(
2290   HWND hwnd,
2291   HDC  givenDC)
2292 {
2293   HDC  hdc;
2294   RECT clientRect;
2295
2296   HBRUSH brush = CreateSolidBrush(comctl32_color.clrBtnFace);
2297
2298   hdc = givenDC ? givenDC : GetDC(hwnd);
2299
2300   GetClientRect(hwnd, &clientRect);
2301
2302   FillRect(hdc, &clientRect, brush);
2303
2304   if (givenDC==0)
2305     ReleaseDC(hwnd, hdc);
2306
2307   DeleteObject(brush);
2308
2309   return 0;
2310 }
2311
2312 /******************************************************************************
2313  * TAB_EnsureSelectionVisible
2314  *
2315  * This method will make sure that the current selection is completely
2316  * visible by scrolling until it is.
2317  */
2318 static void TAB_EnsureSelectionVisible(
2319   HWND      hwnd,
2320   TAB_INFO* infoPtr)
2321 {
2322   INT iSelected = infoPtr->iSelected;
2323   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2324   INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2325
2326   /* set the items row to the bottommost row or topmost row depending on
2327    * style */
2328   if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2329   {
2330       INT newselected;
2331       INT iTargetRow;
2332
2333       if(lStyle & TCS_VERTICAL)
2334         newselected = infoPtr->items[iSelected].rect.left;
2335       else
2336         newselected = infoPtr->items[iSelected].rect.top;
2337
2338       /* the target row is always (number of rows - 1)
2339          as row 0 is furthest from the clientRect */
2340       iTargetRow = infoPtr->uNumRows - 1;
2341
2342       if (newselected != iTargetRow)
2343       {
2344          UINT i;
2345          if(lStyle & TCS_VERTICAL)
2346          {
2347            for (i=0; i < infoPtr->uNumItem; i++)
2348            {
2349              /* move everything in the row of the selected item to the iTargetRow */
2350              if (infoPtr->items[i].rect.left == newselected )
2351                  infoPtr->items[i].rect.left = iTargetRow;
2352              else
2353              {
2354                if (infoPtr->items[i].rect.left > newselected)
2355                  infoPtr->items[i].rect.left-=1;
2356              }
2357            }
2358          }
2359          else
2360          {
2361            for (i=0; i < infoPtr->uNumItem; i++)
2362            {
2363              if (infoPtr->items[i].rect.top == newselected )
2364                  infoPtr->items[i].rect.top = iTargetRow;
2365              else
2366              {
2367                if (infoPtr->items[i].rect.top > newselected)
2368                  infoPtr->items[i].rect.top-=1;
2369              }
2370           }
2371         }
2372         TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2373       }
2374   }
2375
2376   /*
2377    * Do the trivial cases first.
2378    */
2379   if ( (!infoPtr->needsScrolling) ||
2380        (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2381     return;
2382
2383   if (infoPtr->leftmostVisible >= iSelected)
2384   {
2385     infoPtr->leftmostVisible = iSelected;
2386   }
2387   else
2388   {
2389      RECT r;
2390      INT width;
2391      UINT i;
2392
2393      /* Calculate the part of the client area that is visible */
2394      GetClientRect(hwnd, &r);
2395      width = r.right;
2396
2397      GetClientRect(infoPtr->hwndUpDown, &r);
2398      width -= r.right;
2399
2400      if ((infoPtr->items[iSelected].rect.right -
2401           infoPtr->items[iSelected].rect.left) >= width )
2402      {
2403         /* Special case: width of selected item is greater than visible
2404          * part of control.
2405          */
2406         infoPtr->leftmostVisible = iSelected;
2407      }
2408      else
2409      {
2410         for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2411         {
2412            if ((infoPtr->items[iSelected].rect.right -
2413                 infoPtr->items[i].rect.left) < width)
2414               break;
2415         }
2416         infoPtr->leftmostVisible = i;
2417      }
2418   }
2419
2420   if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2421     TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2422
2423   SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2424                MAKELONG(infoPtr->leftmostVisible, 0));
2425 }
2426
2427 /******************************************************************************
2428  * TAB_InvalidateTabArea
2429  *
2430  * This method will invalidate the portion of the control that contains the
2431  * tabs. It is called when the state of the control changes and needs
2432  * to be redisplayed
2433  */
2434 static void TAB_InvalidateTabArea(
2435   HWND      hwnd,
2436   TAB_INFO* infoPtr)
2437 {
2438   RECT clientRect, rInvalidate, rAdjClient;
2439   DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2440   INT lastRow = infoPtr->uNumRows - 1;
2441   RECT rect;
2442
2443   if (lastRow < 0) return;
2444
2445   GetClientRect(hwnd, &clientRect);
2446   rInvalidate = clientRect;
2447   rAdjClient = clientRect;
2448
2449   TAB_AdjustRect(hwnd, 0, &rAdjClient);
2450
2451   TAB_InternalGetItemRect(hwnd, infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2452   if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2453   {
2454     rInvalidate.left = rAdjClient.right;
2455     if (infoPtr->uNumRows == 1)
2456       rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2457   }
2458   else if(lStyle & TCS_VERTICAL)
2459   {
2460     rInvalidate.right = rAdjClient.left;
2461     if (infoPtr->uNumRows == 1)
2462       rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2463   }
2464   else if (lStyle & TCS_BOTTOM)
2465   {
2466     rInvalidate.top = rAdjClient.bottom;
2467     if (infoPtr->uNumRows == 1)
2468       rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2469   }
2470   else 
2471   {
2472     rInvalidate.bottom = rAdjClient.top;
2473     if (infoPtr->uNumRows == 1)
2474       rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2475   }
2476   
2477   /* Punch out the updown control */
2478   if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2479     RECT r;
2480     GetClientRect(infoPtr->hwndUpDown, &r);
2481     if (rInvalidate.right > clientRect.right - r.left)
2482       rInvalidate.right = rInvalidate.right - (r.right - r.left);
2483     else
2484       rInvalidate.right = clientRect.right - r.left;
2485   }
2486   
2487   TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2488         rInvalidate.left,  rInvalidate.top,
2489         rInvalidate.right, rInvalidate.bottom);
2490  
2491   InvalidateRect(hwnd, &rInvalidate, TRUE);
2492 }
2493
2494 static LRESULT
2495 TAB_Paint (HWND hwnd, WPARAM wParam)
2496 {
2497   HDC hdc;
2498   PAINTSTRUCT ps;
2499
2500   if (wParam == 0)
2501   {
2502     hdc = BeginPaint (hwnd, &ps);
2503     TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2504          ps.fErase,
2505          ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2506
2507     if (ps.fErase)
2508       TAB_EraseBackground (hwnd, hdc);
2509
2510   } else {
2511     hdc = (HDC)wParam;
2512   }
2513     
2514   TAB_Refresh (hwnd, hdc);
2515
2516   if(!wParam)
2517     EndPaint (hwnd, &ps);
2518
2519   return 0;
2520 }
2521
2522 static LRESULT
2523 TAB_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2524 {
2525   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2526   TCITEMA *pti;
2527   INT iItem;
2528   RECT rect;
2529
2530   GetClientRect (hwnd, &rect);
2531   TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2532         rect.top, rect.left, rect.bottom, rect.right);
2533
2534   pti = (TCITEMA *)lParam;
2535   iItem = (INT)wParam;
2536
2537   if (iItem < 0) return -1;
2538   if (iItem > infoPtr->uNumItem)
2539     iItem = infoPtr->uNumItem;
2540
2541   TAB_DumpItemExternalA(pti, iItem);
2542
2543
2544   if (infoPtr->uNumItem == 0) {
2545     infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2546     infoPtr->uNumItem++;
2547     infoPtr->iSelected = 0;
2548   }
2549   else {
2550     TAB_ITEM *oldItems = infoPtr->items;
2551
2552     infoPtr->uNumItem++;
2553     infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2554
2555     /* pre insert copy */
2556     if (iItem > 0) {
2557       memcpy (&infoPtr->items[0], &oldItems[0],
2558               iItem * TAB_ITEM_SIZE(infoPtr));
2559     }
2560
2561     /* post insert copy */
2562     if (iItem < infoPtr->uNumItem - 1) {
2563       memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2564               (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2565
2566     }
2567
2568     if (iItem <= infoPtr->iSelected)
2569       infoPtr->iSelected++;
2570
2571     Free (oldItems);
2572   }
2573
2574   infoPtr->items[iItem].mask = pti->mask;
2575   if (pti->mask & TCIF_TEXT)
2576     Str_SetPtrAtoW (&infoPtr->items[iItem].pszText, pti->pszText);
2577
2578   if (pti->mask & TCIF_IMAGE)
2579     infoPtr->items[iItem].iImage = pti->iImage;
2580
2581   if (pti->mask & TCIF_PARAM)
2582     memcpy(infoPtr->items[iItem].extra, &pti->lParam, infoPtr->cbInfo);
2583   else
2584     memset(infoPtr->items[iItem].extra, 0, infoPtr->cbInfo);
2585   
2586   TAB_SetItemBounds(hwnd);
2587   if (infoPtr->uNumItem > 1)
2588     TAB_InvalidateTabArea(hwnd, infoPtr);
2589   else
2590     InvalidateRect(hwnd, NULL, TRUE);
2591
2592   TRACE("[%p]: added item %d %s\n",
2593         hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2594
2595   return iItem;
2596 }
2597
2598
2599 static LRESULT
2600 TAB_InsertItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2601 {
2602   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2603   TCITEMW *pti;
2604   INT iItem;
2605   RECT rect;
2606
2607   GetClientRect (hwnd, &rect);
2608   TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2609         rect.top, rect.left, rect.bottom, rect.right);
2610
2611   pti = (TCITEMW *)lParam;
2612   iItem = (INT)wParam;
2613
2614   if (iItem < 0) return -1;
2615   if (iItem > infoPtr->uNumItem)
2616     iItem = infoPtr->uNumItem;
2617
2618   TAB_DumpItemExternalW(pti, iItem);
2619
2620   if (infoPtr->uNumItem == 0) {
2621     infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2622     infoPtr->uNumItem++;
2623     infoPtr->iSelected = 0;
2624   }
2625   else {
2626     TAB_ITEM *oldItems = infoPtr->items;
2627
2628     infoPtr->uNumItem++;
2629     infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2630
2631     /* pre insert copy */
2632     if (iItem > 0) {
2633       memcpy (&infoPtr->items[0], &oldItems[0],
2634               iItem * TAB_ITEM_SIZE(infoPtr));
2635     }
2636
2637     /* post insert copy */
2638     if (iItem < infoPtr->uNumItem - 1) {
2639       memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2640               (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2641
2642   }
2643
2644     if (iItem <= infoPtr->iSelected)
2645       infoPtr->iSelected++;
2646
2647     Free (oldItems);
2648   }
2649
2650   infoPtr->items[iItem].mask = pti->mask;
2651   if (pti->mask & TCIF_TEXT)
2652     Str_SetPtrW (&infoPtr->items[iItem].pszText, pti->pszText);
2653
2654   if (pti->mask & TCIF_IMAGE)
2655     infoPtr->items[iItem].iImage = pti->iImage;
2656
2657   if (pti->mask & TCIF_PARAM)
2658     memcpy(infoPtr->items[iItem].extra, &pti->lParam, infoPtr->cbInfo);
2659   else
2660     memset(infoPtr->items[iItem].extra, 0, infoPtr->cbInfo);
2661
2662   TAB_SetItemBounds(hwnd);
2663   if (infoPtr->uNumItem > 1)
2664     TAB_InvalidateTabArea(hwnd, infoPtr);
2665   else
2666     InvalidateRect(hwnd, NULL, TRUE);
2667
2668   TRACE("[%p]: added item %d %s\n",
2669         hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2670
2671   return iItem;
2672 }
2673
2674
2675 static LRESULT
2676 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2677 {
2678   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2679   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2680   LONG lResult = 0;
2681   BOOL bNeedPaint = FALSE;
2682
2683   lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2684
2685   /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2686   if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2687   {
2688     infoPtr->tabWidth = max((INT)LOWORD(lParam), infoPtr->tabMinWidth);
2689     bNeedPaint = TRUE;
2690   }
2691
2692   if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2693   {
2694     if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2695       infoPtr->tabHeight = (INT)HIWORD(lParam);
2696
2697     bNeedPaint = TRUE;
2698   }
2699   TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2700        HIWORD(lResult), LOWORD(lResult),
2701        infoPtr->tabHeight, infoPtr->tabWidth);
2702
2703   if (bNeedPaint)
2704   {
2705     TAB_SetItemBounds(hwnd);
2706     RedrawWindow(hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2707   }
2708     
2709   return lResult;
2710 }
2711
2712 static LRESULT
2713 TAB_SetMinTabWidth (HWND hwnd, LPARAM lParam)
2714 {
2715   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2716   INT cx = (INT)lParam;
2717   INT oldcx;
2718
2719   if (infoPtr) {
2720     oldcx = infoPtr->tabMinWidth;
2721     infoPtr->tabMinWidth = (cx==-1)?DEFAULT_TAB_WIDTH:cx;
2722   } else
2723     return 0;
2724
2725   return oldcx;
2726 }
2727
2728 static LRESULT 
2729 TAB_HighlightItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2730 {
2731   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2732   INT iItem = (INT)wParam;
2733   BOOL fHighlight = (BOOL)LOWORD(lParam);
2734
2735   if ((infoPtr) && (iItem>=0) && (iItem<infoPtr->uNumItem)) {
2736     if (fHighlight)
2737       infoPtr->items[iItem].dwState |= TCIS_HIGHLIGHTED;
2738     else
2739       infoPtr->items[iItem].dwState &= ~TCIS_HIGHLIGHTED;
2740   } else
2741     return FALSE;
2742
2743   return TRUE;
2744 }
2745
2746 static LRESULT
2747 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2748 {
2749   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2750   TCITEMA *tabItem;
2751   TAB_ITEM *wineItem;
2752   INT    iItem;
2753
2754   iItem = (INT)wParam;
2755   tabItem = (LPTCITEMA)lParam;
2756
2757   TRACE("%d %p\n", iItem, tabItem);
2758   if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2759
2760   TAB_DumpItemExternalA(tabItem, iItem);
2761
2762   wineItem = &infoPtr->items[iItem];
2763
2764   if (tabItem->mask & TCIF_IMAGE)
2765     wineItem->iImage = tabItem->iImage;
2766
2767   if (tabItem->mask & TCIF_PARAM)
2768     memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2769
2770   if (tabItem->mask & TCIF_RTLREADING)
2771     FIXME("TCIF_RTLREADING\n");
2772
2773   if (tabItem->mask & TCIF_STATE)
2774     wineItem->dwState = tabItem->dwState;
2775
2776   if (tabItem->mask & TCIF_TEXT)
2777    Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2778
2779   /* Update and repaint tabs */
2780   TAB_SetItemBounds(hwnd);
2781   TAB_InvalidateTabArea(hwnd,infoPtr);
2782
2783   return TRUE;
2784   }
2785
2786
2787 static LRESULT
2788 TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2789 {
2790   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2791   TCITEMW *tabItem;
2792   TAB_ITEM *wineItem;
2793   INT    iItem;
2794
2795   iItem = (INT)wParam;
2796   tabItem = (LPTCITEMW)lParam;
2797
2798   TRACE("%d %p\n", iItem, tabItem);
2799   if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2800
2801   TAB_DumpItemExternalW(tabItem, iItem);
2802
2803   wineItem = &infoPtr->items[iItem];
2804
2805   if (tabItem->mask & TCIF_IMAGE)
2806     wineItem->iImage = tabItem->iImage;
2807
2808   if (tabItem->mask & TCIF_PARAM)
2809     memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2810
2811   if (tabItem->mask & TCIF_RTLREADING)
2812     FIXME("TCIF_RTLREADING\n");
2813
2814   if (tabItem->mask & TCIF_STATE)
2815     wineItem->dwState = tabItem->dwState;
2816
2817   if (tabItem->mask & TCIF_TEXT)
2818    Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2819
2820   /* Update and repaint tabs */
2821   TAB_SetItemBounds(hwnd);
2822   TAB_InvalidateTabArea(hwnd,infoPtr);
2823
2824   return TRUE;
2825 }
2826
2827
2828 static LRESULT
2829 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2830 {
2831    TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2832
2833    return infoPtr->uNumItem;
2834 }
2835
2836
2837 static LRESULT
2838 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2839 {
2840    TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2841    TCITEMA *tabItem;
2842    TAB_ITEM *wineItem;
2843    INT    iItem;
2844
2845   iItem = (INT)wParam;
2846   tabItem = (LPTCITEMA)lParam;
2847   TRACE("\n");
2848   if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2849     return FALSE;
2850
2851   wineItem = &infoPtr->items[iItem];
2852
2853   if (tabItem->mask & TCIF_IMAGE)
2854     tabItem->iImage = wineItem->iImage;
2855
2856   if (tabItem->mask & TCIF_PARAM)
2857     memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2858
2859   if (tabItem->mask & TCIF_RTLREADING)
2860     FIXME("TCIF_RTLREADING\n");
2861
2862   if (tabItem->mask & TCIF_STATE)
2863     tabItem->dwState = wineItem->dwState;
2864
2865   if (tabItem->mask & TCIF_TEXT)
2866    Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2867
2868   TAB_DumpItemExternalA(tabItem, iItem);
2869
2870   return TRUE;
2871 }
2872
2873
2874 static LRESULT
2875 TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2876 {
2877   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2878   TCITEMW *tabItem;
2879   TAB_ITEM *wineItem;
2880   INT    iItem;
2881
2882   iItem = (INT)wParam;
2883   tabItem = (LPTCITEMW)lParam;
2884   TRACE("\n");
2885   if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2886     return FALSE;
2887
2888   wineItem=& infoPtr->items[iItem];
2889
2890   if (tabItem->mask & TCIF_IMAGE)
2891     tabItem->iImage = wineItem->iImage;
2892
2893   if (tabItem->mask & TCIF_PARAM)
2894     memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2895
2896   if (tabItem->mask & TCIF_RTLREADING)
2897     FIXME("TCIF_RTLREADING\n");
2898
2899   if (tabItem->mask & TCIF_STATE)
2900     tabItem->dwState = wineItem->dwState;
2901
2902   if (tabItem->mask & TCIF_TEXT)
2903    Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2904
2905   TAB_DumpItemExternalW(tabItem, iItem);
2906
2907   return TRUE;
2908 }
2909
2910
2911 static LRESULT
2912 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2913 {
2914     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2915     INT iItem = (INT) wParam;
2916     BOOL bResult = FALSE;
2917
2918     if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2919     {
2920         TAB_ITEM *oldItems = infoPtr->items;
2921         
2922         TAB_InvalidateTabArea(hwnd, infoPtr);
2923
2924         infoPtr->uNumItem--;
2925         infoPtr->items = Alloc(TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2926
2927         if (iItem > 0)
2928             memcpy(&infoPtr->items[0], &oldItems[0], iItem * TAB_ITEM_SIZE(infoPtr));
2929
2930         if (iItem < infoPtr->uNumItem)
2931             memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
2932                    (infoPtr->uNumItem - iItem) * TAB_ITEM_SIZE(infoPtr));
2933
2934         Free(oldItems);
2935
2936         /* Readjust the selected index */
2937         if ((iItem == infoPtr->iSelected) && (iItem > 0))
2938             infoPtr->iSelected--;
2939
2940         if (iItem < infoPtr->iSelected)
2941             infoPtr->iSelected--;
2942
2943         if (infoPtr->uNumItem == 0)
2944             infoPtr->iSelected = -1;
2945
2946         /* Reposition and repaint tabs */
2947         TAB_SetItemBounds(hwnd);
2948
2949         bResult = TRUE;
2950     }
2951
2952     return bResult;
2953 }
2954
2955 static LRESULT
2956 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2957 {
2958     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2959    
2960     TAB_InvalidateTabArea(hwnd,infoPtr);  
2961
2962     Free (infoPtr->items);
2963     infoPtr->uNumItem = 0;
2964     infoPtr->iSelected = -1;
2965     if (infoPtr->iHotTracked >= 0)
2966         KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2967     infoPtr->iHotTracked = -1;
2968     
2969     TAB_SetItemBounds(hwnd);
2970     return TRUE;
2971 }
2972
2973
2974 static LRESULT
2975 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2976 {
2977   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2978
2979   TRACE("\n");
2980   return (LRESULT)infoPtr->hFont;
2981 }
2982
2983 static LRESULT
2984 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2985
2986 {
2987   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2988
2989   TRACE("%x %lx\n",wParam, lParam);
2990
2991   infoPtr->hFont = (HFONT)wParam;
2992
2993   TAB_SetItemBounds(hwnd);
2994
2995   TAB_InvalidateTabArea(hwnd, infoPtr);
2996
2997   return 0;
2998 }
2999
3000
3001 static LRESULT
3002 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
3003 {
3004   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3005
3006   TRACE("\n");
3007   return (LRESULT)infoPtr->himl;
3008 }
3009
3010 static LRESULT
3011 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
3012 {
3013     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3014     HIMAGELIST himlPrev;
3015
3016     TRACE("\n");
3017     himlPrev = infoPtr->himl;
3018     infoPtr->himl= (HIMAGELIST)lParam;
3019     return (LRESULT)himlPrev;
3020 }
3021
3022 static LRESULT
3023 TAB_GetUnicodeFormat (HWND hwnd)
3024 {
3025     TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
3026     return infoPtr->bUnicode;
3027 }
3028
3029 static LRESULT
3030 TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
3031 {
3032     TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
3033     BOOL bTemp = infoPtr->bUnicode;
3034
3035     infoPtr->bUnicode = (BOOL)wParam;
3036
3037     return bTemp;
3038 }
3039
3040 static LRESULT
3041 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
3042
3043 {
3044 /* I'm not really sure what the following code was meant to do.
3045    This is what it is doing:
3046    When WM_SIZE is sent with SIZE_RESTORED, the control
3047    gets positioned in the top left corner.
3048
3049   RECT parent_rect;
3050   HWND parent;
3051   UINT uPosFlags,cx,cy;
3052
3053   uPosFlags=0;
3054   if (!wParam) {
3055     parent = GetParent (hwnd);
3056     GetClientRect(parent, &parent_rect);
3057     cx=LOWORD (lParam);
3058     cy=HIWORD (lParam);
3059     if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
3060         uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
3061
3062     SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
3063             cx, cy, uPosFlags | SWP_NOZORDER);
3064   } else {
3065     FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
3066   } */
3067
3068   /* Recompute the size/position of the tabs. */
3069   TAB_SetItemBounds (hwnd);
3070
3071   /* Force a repaint of the control. */
3072   InvalidateRect(hwnd, NULL, TRUE);
3073
3074   return 0;
3075 }
3076
3077
3078 static LRESULT
3079 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
3080 {
3081   TAB_INFO *infoPtr;
3082   TEXTMETRICA fontMetrics;
3083   HDC hdc;
3084   HFONT hOldFont;
3085   DWORD dwStyle;
3086
3087   infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
3088
3089   SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
3090
3091   infoPtr->hwndNotify      = ((LPCREATESTRUCTW)lParam)->hwndParent;
3092   infoPtr->uNumItem        = 0;
3093   infoPtr->uNumRows        = 0;
3094   infoPtr->uHItemPadding   = 6;
3095   infoPtr->uVItemPadding   = 3;
3096   infoPtr->uHItemPadding_s = 6;
3097   infoPtr->uVItemPadding_s = 3;
3098   infoPtr->hFont           = 0;
3099   infoPtr->items           = 0;
3100   infoPtr->hcurArrow       = LoadCursorA (0, (LPSTR)IDC_ARROW);
3101   infoPtr->iSelected       = -1;
3102   infoPtr->iHotTracked     = -1;
3103   infoPtr->uFocus          = -1;
3104   infoPtr->hwndToolTip     = 0;
3105   infoPtr->DoRedraw        = TRUE;
3106   infoPtr->needsScrolling  = FALSE;
3107   infoPtr->hwndUpDown      = 0;
3108   infoPtr->leftmostVisible = 0;
3109   infoPtr->fHeightSet      = FALSE;
3110   infoPtr->bUnicode        = IsWindowUnicode (hwnd);
3111   infoPtr->cbInfo          = sizeof(LPARAM);
3112
3113   TRACE("Created tab control, hwnd [%p]\n", hwnd);
3114
3115   /* The tab control always has the WS_CLIPSIBLINGS style. Even
3116      if you don't specify it in CreateWindow. This is necessary in
3117      order for paint to work correctly. This follows windows behaviour. */
3118   dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
3119   SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
3120
3121   if (dwStyle & TCS_TOOLTIPS) {
3122     /* Create tooltip control */
3123     infoPtr->hwndToolTip =
3124       CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
3125                        CW_USEDEFAULT, CW_USEDEFAULT,
3126                        CW_USEDEFAULT, CW_USEDEFAULT,
3127                        hwnd, 0, 0, 0);
3128
3129     /* Send NM_TOOLTIPSCREATED notification */
3130     if (infoPtr->hwndToolTip) {
3131       NMTOOLTIPSCREATED nmttc;
3132
3133       nmttc.hdr.hwndFrom = hwnd;
3134       nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
3135       nmttc.hdr.code = NM_TOOLTIPSCREATED;
3136       nmttc.hwndToolTips = infoPtr->hwndToolTip;
3137
3138       SendMessageA (infoPtr->hwndNotify, WM_NOTIFY,
3139                     (WPARAM)GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc);
3140     }
3141   }
3142
3143   /*
3144    * We need to get text information so we need a DC and we need to select
3145    * a font.
3146    */
3147   hdc = GetDC(hwnd);
3148   hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3149
3150   /* Use the system font to determine the initial height of a tab. */
3151   GetTextMetricsA(hdc, &fontMetrics);
3152
3153   /*
3154    * Make sure there is enough space for the letters + growing the
3155    * selected item + extra space for the selected item.
3156    */
3157   infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3158                        ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
3159                         infoPtr->uVItemPadding;
3160
3161   /* Initialize the width of a tab. */
3162   infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
3163   infoPtr->tabMinWidth = 0;
3164
3165   TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3166
3167   SelectObject (hdc, hOldFont);
3168   ReleaseDC(hwnd, hdc);
3169
3170   return 0;
3171 }
3172
3173 static LRESULT
3174 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
3175 {
3176   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3177   UINT iItem;
3178
3179   if (!infoPtr)
3180       return 0;
3181
3182   if (infoPtr->items) {
3183     for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3184       if (infoPtr->items[iItem].pszText)
3185         Free (infoPtr->items[iItem].pszText);
3186     }
3187     Free (infoPtr->items);
3188   }
3189
3190   if (infoPtr->hwndToolTip)
3191     DestroyWindow (infoPtr->hwndToolTip);
3192
3193   if (infoPtr->hwndUpDown)
3194     DestroyWindow(infoPtr->hwndUpDown);
3195
3196   if (infoPtr->iHotTracked >= 0)
3197     KillTimer(hwnd, TAB_HOTTRACK_TIMER);
3198
3199   Free (infoPtr);
3200   SetWindowLongA(hwnd, 0, 0);
3201   return 0;
3202 }
3203
3204 static LRESULT
3205 TAB_SetItemExtra (HWND hwnd, WPARAM wParam, LPARAM lParam)
3206 {
3207   TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3208   INT cbInfo = wParam;
3209    
3210   if (!infoPtr || cbInfo <= 0)
3211     return FALSE;
3212
3213   if (infoPtr->uNumItem)
3214   {
3215     /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
3216     return FALSE;
3217   }
3218     
3219   infoPtr->cbInfo = cbInfo;
3220   return TRUE;
3221 }
3222
3223 static LRESULT WINAPI
3224 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3225 {
3226     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3227
3228     TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3229     if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
3230       return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3231
3232     switch (uMsg)
3233     {
3234     case TCM_GETIMAGELIST:
3235       return TAB_GetImageList (hwnd, wParam, lParam);
3236
3237     case TCM_SETIMAGELIST:
3238       return TAB_SetImageList (hwnd, wParam, lParam);
3239
3240     case TCM_GETITEMCOUNT:
3241       return TAB_GetItemCount (hwnd, wParam, lParam);
3242
3243     case TCM_GETITEMA:
3244       return TAB_GetItemA (hwnd, wParam, lParam);
3245
3246     case TCM_GETITEMW:
3247       return TAB_GetItemW (hwnd, wParam, lParam);
3248
3249     case TCM_SETITEMA:
3250       return TAB_SetItemA (hwnd, wParam, lParam);
3251
3252     case TCM_SETITEMW:
3253       return TAB_SetItemW (hwnd, wParam, lParam);
3254
3255     case TCM_DELETEITEM:
3256       return TAB_DeleteItem (hwnd, wParam, lParam);
3257
3258     case TCM_DELETEALLITEMS:
3259      return TAB_DeleteAllItems (hwnd, wParam, lParam);
3260
3261     case TCM_GETITEMRECT:
3262      return TAB_GetItemRect (hwnd, wParam, lParam);
3263
3264     case TCM_GETCURSEL:
3265       return TAB_GetCurSel (hwnd);
3266
3267     case TCM_HITTEST:
3268       return TAB_HitTest (hwnd, wParam, lParam);
3269
3270     case TCM_SETCURSEL:
3271       return TAB_SetCurSel (hwnd, wParam);
3272
3273     case TCM_INSERTITEMA:
3274       return TAB_InsertItemA (hwnd, wParam, lParam);
3275
3276     case TCM_INSERTITEMW:
3277       return TAB_InsertItemW (hwnd, wParam, lParam);
3278
3279     case TCM_SETITEMEXTRA:
3280       return TAB_SetItemExtra (hwnd, wParam, lParam);
3281
3282     case TCM_ADJUSTRECT:
3283       return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
3284
3285     case TCM_SETITEMSIZE:
3286       return TAB_SetItemSize (hwnd, wParam, lParam);
3287
3288     case TCM_REMOVEIMAGE:
3289       FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3290       return 0;
3291
3292     case TCM_SETPADDING:
3293       return TAB_SetPadding (hwnd, wParam, lParam);
3294
3295     case TCM_GETROWCOUNT:
3296       return TAB_GetRowCount(hwnd);
3297
3298     case TCM_GETUNICODEFORMAT:
3299       return TAB_GetUnicodeFormat (hwnd);
3300
3301     case TCM_SETUNICODEFORMAT:
3302       return TAB_SetUnicodeFormat (hwnd, wParam);
3303
3304     case TCM_HIGHLIGHTITEM:
3305       return TAB_HighlightItem (hwnd, wParam, lParam);
3306
3307     case TCM_GETTOOLTIPS:
3308       return TAB_GetToolTips (hwnd, wParam, lParam);
3309
3310     case TCM_SETTOOLTIPS:
3311       return TAB_SetToolTips (hwnd, wParam, lParam);
3312
3313     case TCM_GETCURFOCUS:
3314       return TAB_GetCurFocus (hwnd);
3315
3316     case TCM_SETCURFOCUS:
3317       return TAB_SetCurFocus (hwnd, wParam);
3318
3319     case TCM_SETMINTABWIDTH:
3320       return TAB_SetMinTabWidth(hwnd, lParam);
3321
3322     case TCM_DESELECTALL:
3323       FIXME("Unimplemented msg TCM_DESELECTALL\n");
3324       return 0;
3325
3326     case TCM_GETEXTENDEDSTYLE:
3327       FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3328       return 0;
3329
3330     case TCM_SETEXTENDEDSTYLE:
3331       FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3332       return 0;
3333
3334     case WM_GETFONT:
3335       return TAB_GetFont (hwnd, wParam, lParam);
3336
3337     case WM_SETFONT:
3338       return TAB_SetFont (hwnd, wParam, lParam);
3339
3340     case WM_CREATE:
3341       return TAB_Create (hwnd, wParam, lParam);
3342
3343     case WM_NCDESTROY:
3344       return TAB_Destroy (hwnd, wParam, lParam);
3345
3346     case WM_GETDLGCODE:
3347       return DLGC_WANTARROWS | DLGC_WANTCHARS;
3348
3349     case WM_LBUTTONDOWN:
3350       return TAB_LButtonDown (hwnd, wParam, lParam);
3351
3352     case WM_LBUTTONUP:
3353       return TAB_LButtonUp (hwnd, wParam, lParam);
3354
3355     case WM_NOTIFY:
3356       return SendMessageA(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3357
3358     case WM_RBUTTONDOWN:
3359       return TAB_RButtonDown (hwnd, wParam, lParam);
3360
3361     case WM_MOUSEMOVE:
3362       return TAB_MouseMove (hwnd, wParam, lParam);
3363
3364     case WM_ERASEBKGND:
3365       return TAB_EraseBackground (hwnd, (HDC)wParam);
3366
3367     case WM_PAINT:
3368       return TAB_Paint (hwnd, wParam);
3369
3370     case WM_SIZE:
3371       return TAB_Size (hwnd, wParam, lParam);
3372
3373     case WM_SETREDRAW:
3374       return TAB_SetRedraw (hwnd, wParam);
3375
3376     case WM_HSCROLL:
3377       return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3378
3379     case WM_STYLECHANGED:
3380       TAB_SetItemBounds (hwnd);
3381       InvalidateRect(hwnd, NULL, TRUE);
3382       return 0;
3383
3384     case WM_SYSCOLORCHANGE:
3385       COMCTL32_RefreshSysColors();
3386       return 0;
3387
3388     case WM_KILLFOCUS:
3389     case WM_SETFOCUS:
3390       return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
3391
3392     case WM_KEYUP:
3393       return TAB_KeyUp(hwnd, wParam);
3394     case WM_NCHITTEST:
3395       return TAB_NCHitTest(hwnd, lParam);
3396
3397     default:
3398       if ((uMsg >= WM_USER) && (uMsg < WM_APP))
3399         WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3400              uMsg, wParam, lParam);
3401       return DefWindowProcA(hwnd, uMsg, wParam, lParam);
3402     }
3403
3404     return 0;
3405 }
3406
3407
3408 VOID
3409 TAB_Register (void)
3410 {
3411   WNDCLASSA wndClass;
3412
3413   ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3414   wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3415   wndClass.lpfnWndProc   = (WNDPROC)TAB_WindowProc;
3416   wndClass.cbClsExtra    = 0;
3417   wndClass.cbWndExtra    = sizeof(TAB_INFO *);
3418   wndClass.hCursor       = LoadCursorA (0, (LPSTR)IDC_ARROW);
3419   wndClass.hbrBackground = NULL;
3420   wndClass.lpszClassName = WC_TABCONTROLA;
3421
3422   RegisterClassA (&wndClass);
3423 }
3424
3425
3426 VOID
3427 TAB_Unregister (void)
3428 {
3429     UnregisterClassA (WC_TABCONTROLA, NULL);
3430 }