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