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