d3dx8: Implement D3DXMatrixfDeterminant.
[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           infoPtr->uFocus=iItem;
259           TAB_EnsureSelectionVisible(infoPtr);
260           TAB_InvalidateTabArea(infoPtr);
261       }
262   }
263   return prevItem;
264 }
265
266 static LRESULT TAB_SetCurFocus (TAB_INFO *infoPtr, INT iItem)
267 {
268   if (iItem < 0)
269       infoPtr->uFocus = -1;
270   else if (iItem < infoPtr->uNumItem) {
271     if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS) {
272       FIXME("Should set input focus\n");
273     } else {
274       int oldFocus = infoPtr->uFocus;
275       if (infoPtr->iSelected != iItem || oldFocus == -1 ) {
276         infoPtr->uFocus = iItem;
277         if (oldFocus != -1) {
278           if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))  {
279             infoPtr->iSelected = iItem;
280             TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
281           }
282           else
283             infoPtr->iSelected = iItem;
284           TAB_EnsureSelectionVisible(infoPtr);
285           TAB_InvalidateTabArea(infoPtr);
286         }
287       }
288     }
289   }
290   return 0;
291 }
292
293 static inline LRESULT
294 TAB_SetToolTips (TAB_INFO *infoPtr, HWND hwndToolTip)
295 {
296     if (infoPtr)
297         infoPtr->hwndToolTip = hwndToolTip;
298     return 0;
299 }
300
301 static inline LRESULT
302 TAB_SetPadding (TAB_INFO *infoPtr, LPARAM lParam)
303 {
304     if (infoPtr)
305     {
306         infoPtr->uHItemPadding_s=LOWORD(lParam);
307         infoPtr->uVItemPadding_s=HIWORD(lParam);
308     }
309     return 0;
310 }
311
312 /******************************************************************************
313  * TAB_InternalGetItemRect
314  *
315  * This method will calculate the rectangle representing a given tab item in
316  * client coordinates. This method takes scrolling into account.
317  *
318  * This method returns TRUE if the item is visible in the window and FALSE
319  * if it is completely outside the client area.
320  */
321 static BOOL TAB_InternalGetItemRect(
322   const TAB_INFO* infoPtr,
323   INT         itemIndex,
324   RECT*       itemRect,
325   RECT*       selectedRect)
326 {
327   RECT tmpItemRect,clientRect;
328   LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
329
330   /* Perform a sanity check and a trivial visibility check. */
331   if ( (infoPtr->uNumItem <= 0) ||
332        (itemIndex >= infoPtr->uNumItem) ||
333        (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
334     {
335         TRACE("Not Visible\n");
336         /* need to initialize these to empty rects */
337         if (itemRect)
338         {
339             memset(itemRect,0,sizeof(RECT));
340             itemRect->bottom = infoPtr->tabHeight;
341         }
342         if (selectedRect)
343             memset(selectedRect,0,sizeof(RECT));
344         return FALSE;
345     }
346
347   /*
348    * Avoid special cases in this procedure by assigning the "out"
349    * parameters if the caller didn't supply them
350    */
351   if (itemRect == NULL)
352     itemRect = &tmpItemRect;
353
354   /* Retrieve the unmodified item rect. */
355   *itemRect = TAB_GetItem(infoPtr,itemIndex)->rect;
356
357   /* calculate the times bottom and top based on the row */
358   GetClientRect(infoPtr->hwnd, &clientRect);
359
360   if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
361   {
362     itemRect->right  = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * infoPtr->tabHeight -
363                        ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
364     itemRect->left   = itemRect->right - infoPtr->tabHeight;
365   }
366   else if (lStyle & TCS_VERTICAL)
367   {
368     itemRect->left   = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * infoPtr->tabHeight +
369                        ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
370     itemRect->right  = itemRect->left + infoPtr->tabHeight;
371   }
372   else if (lStyle & TCS_BOTTOM)
373   {
374     itemRect->bottom = clientRect.bottom - itemRect->top * infoPtr->tabHeight -
375                        ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
376     itemRect->top    = itemRect->bottom - infoPtr->tabHeight;
377   }
378   else /* not TCS_BOTTOM and not TCS_VERTICAL */
379   {
380     itemRect->top    = clientRect.top + itemRect->top * infoPtr->tabHeight +
381                        ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
382     itemRect->bottom = itemRect->top + infoPtr->tabHeight;
383  }
384
385   /*
386    * "scroll" it to make sure the item at the very left of the
387    * tab control is the leftmost visible tab.
388    */
389   if(lStyle & TCS_VERTICAL)
390   {
391     OffsetRect(itemRect,
392              0,
393              -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.top);
394
395     /*
396      * Move the rectangle so the first item is slightly offset from
397      * the bottom of the tab control.
398      */
399     OffsetRect(itemRect,
400              0,
401              SELECTED_TAB_OFFSET);
402
403   } else
404   {
405     OffsetRect(itemRect,
406              -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.left,
407              0);
408
409     /*
410      * Move the rectangle so the first item is slightly offset from
411      * the left of the tab control.
412      */
413     OffsetRect(itemRect,
414              SELECTED_TAB_OFFSET,
415              0);
416   }
417   TRACE("item %d tab h=%d, rect=(%d,%d)-(%d,%d)\n",
418         itemIndex, infoPtr->tabHeight,
419         itemRect->left, itemRect->top, itemRect->right, itemRect->bottom);
420
421   /* Now, calculate the position of the item as if it were selected. */
422   if (selectedRect!=NULL)
423   {
424     CopyRect(selectedRect, itemRect);
425
426     /* The rectangle of a selected item is a bit wider. */
427     if(lStyle & TCS_VERTICAL)
428       InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
429     else
430       InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
431
432     /* If it also a bit higher. */
433     if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
434     {
435       selectedRect->left   -= 2; /* the border is thicker on the right */
436       selectedRect->right  += SELECTED_TAB_OFFSET;
437     }
438     else if (lStyle & TCS_VERTICAL)
439     {
440       selectedRect->left   -= SELECTED_TAB_OFFSET;
441       selectedRect->right  += 1;
442     }
443     else if (lStyle & TCS_BOTTOM)
444     {
445       selectedRect->bottom += SELECTED_TAB_OFFSET;
446     }
447     else /* not TCS_BOTTOM and not TCS_VERTICAL */
448     {
449       selectedRect->top    -= SELECTED_TAB_OFFSET;
450       selectedRect->bottom -= 1;
451     }
452   }
453
454   /* Check for visibility */
455   if (lStyle & TCS_VERTICAL)
456     return (itemRect->top < clientRect.bottom) && (itemRect->bottom > clientRect.top);
457   else
458     return (itemRect->left < clientRect.right) && (itemRect->right > clientRect.left);
459 }
460
461 static inline BOOL
462 TAB_GetItemRect(const TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
463 {
464   return TAB_InternalGetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam, (LPRECT)NULL);
465 }
466
467 /******************************************************************************
468  * TAB_KeyUp
469  *
470  * This method is called to handle keyboard input
471  */
472 static LRESULT TAB_KeyUp(TAB_INFO* infoPtr, WPARAM keyCode)
473 {
474   int       newItem = -1;
475
476   switch (keyCode)
477   {
478     case VK_LEFT:
479       newItem = infoPtr->uFocus - 1;
480       break;
481     case VK_RIGHT:
482       newItem = infoPtr->uFocus + 1;
483       break;
484   }
485
486   /*
487    * If we changed to a valid item, change the selection
488    */
489   if (newItem >= 0 &&
490       newItem < infoPtr->uNumItem &&
491       infoPtr->uFocus != newItem)
492   {
493     if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
494     {
495       infoPtr->iSelected = newItem;
496       infoPtr->uFocus    = newItem;
497       TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
498
499       TAB_EnsureSelectionVisible(infoPtr);
500       TAB_InvalidateTabArea(infoPtr);
501     }
502   }
503
504   return 0;
505 }
506
507 /******************************************************************************
508  * TAB_FocusChanging
509  *
510  * This method is called whenever the focus goes in or out of this control
511  * it is used to update the visual state of the control.
512  */
513 static void TAB_FocusChanging(const TAB_INFO *infoPtr)
514 {
515   RECT      selectedRect;
516   BOOL      isVisible;
517
518   /*
519    * Get the rectangle for the item.
520    */
521   isVisible = TAB_InternalGetItemRect(infoPtr,
522                                       infoPtr->uFocus,
523                                       NULL,
524                                       &selectedRect);
525
526   /*
527    * If the rectangle is not completely invisible, invalidate that
528    * portion of the window.
529    */
530   if (isVisible)
531   {
532       TRACE("invalidate (%d,%d)-(%d,%d)\n",
533             selectedRect.left,selectedRect.top,
534             selectedRect.right,selectedRect.bottom);
535     InvalidateRect(infoPtr->hwnd, &selectedRect, TRUE);
536   }
537 }
538
539 static INT TAB_InternalHitTest (const TAB_INFO *infoPtr, POINT pt, UINT *flags)
540 {
541   RECT rect;
542   INT iCount;
543
544   for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
545   {
546     TAB_InternalGetItemRect(infoPtr, iCount, &rect, NULL);
547
548     if (PtInRect(&rect, pt))
549     {
550       *flags = TCHT_ONITEM;
551       return iCount;
552     }
553   }
554
555   *flags = TCHT_NOWHERE;
556   return -1;
557 }
558
559 static inline LRESULT
560 TAB_HitTest (const TAB_INFO *infoPtr, LPTCHITTESTINFO lptest)
561 {
562   return TAB_InternalHitTest (infoPtr, lptest->pt, &lptest->flags);
563 }
564
565 /******************************************************************************
566  * TAB_NCHitTest
567  *
568  * Napster v2b5 has a tab control for its main navigation which has a client
569  * area that covers the whole area of the dialog pages.
570  * That's why it receives all msgs for that area and the underlying dialog ctrls
571  * are dead.
572  * So I decided that we should handle WM_NCHITTEST here and return
573  * HTTRANSPARENT if we don't hit the tab control buttons.
574  * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
575  * doesn't do it that way. Maybe depends on tab control styles ?
576  */
577 static inline LRESULT
578 TAB_NCHitTest (const TAB_INFO *infoPtr, LPARAM lParam)
579 {
580   POINT pt;
581   UINT dummyflag;
582
583   pt.x = (short)LOWORD(lParam);
584   pt.y = (short)HIWORD(lParam);
585   ScreenToClient(infoPtr->hwnd, &pt);
586
587   if (TAB_InternalHitTest(infoPtr, pt, &dummyflag) == -1)
588     return HTTRANSPARENT;
589   else
590     return HTCLIENT;
591 }
592
593 static LRESULT
594 TAB_LButtonDown (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
595 {
596   POINT pt;
597   INT newItem;
598   UINT dummy;
599
600   if (infoPtr->hwndToolTip)
601     TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
602                     WM_LBUTTONDOWN, wParam, lParam);
603
604   if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
605     SetFocus (infoPtr->hwnd);
606   }
607
608   if (infoPtr->hwndToolTip)
609     TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
610                     WM_LBUTTONDOWN, wParam, lParam);
611
612   pt.x = (short)LOWORD(lParam);
613   pt.y = (short)HIWORD(lParam);
614
615   newItem = TAB_InternalHitTest (infoPtr, pt, &dummy);
616
617   TRACE("On Tab, item %d\n", newItem);
618
619   if (newItem != -1 && infoPtr->iSelected != newItem)
620   {
621     if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
622     {
623       infoPtr->iSelected = newItem;
624       infoPtr->uFocus    = newItem;
625       TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
626
627       TAB_EnsureSelectionVisible(infoPtr);
628
629       TAB_InvalidateTabArea(infoPtr);
630     }
631   }
632   return 0;
633 }
634
635 static inline LRESULT
636 TAB_LButtonUp (const TAB_INFO *infoPtr)
637 {
638   TAB_SendSimpleNotify(infoPtr, NM_CLICK);
639
640   return 0;
641 }
642
643 static inline LRESULT
644 TAB_RButtonDown (const TAB_INFO *infoPtr)
645 {
646   TAB_SendSimpleNotify(infoPtr, NM_RCLICK);
647   return 0;
648 }
649
650 /******************************************************************************
651  * TAB_DrawLoneItemInterior
652  *
653  * This calls TAB_DrawItemInterior.  However, TAB_DrawItemInterior is normally
654  * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
655  * up the device context and font.  This routine does the same setup but
656  * only calls TAB_DrawItemInterior for the single specified item.
657  */
658 static void
659 TAB_DrawLoneItemInterior(const TAB_INFO* infoPtr, int iItem)
660 {
661   HDC hdc = GetDC(infoPtr->hwnd);
662   RECT r, rC;
663
664   /* Clip UpDown control to not draw over it */
665   if (infoPtr->needsScrolling)
666   {
667     GetWindowRect(infoPtr->hwnd, &rC);
668     GetWindowRect(infoPtr->hwndUpDown, &r);
669     ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top);
670   }
671   TAB_DrawItemInterior(infoPtr, hdc, iItem, NULL);
672   ReleaseDC(infoPtr->hwnd, hdc);
673 }
674
675 /* update a tab after hottracking - invalidate it or just redraw the interior,
676  * based on whether theming is used or not */
677 static inline void hottrack_refresh(const TAB_INFO *infoPtr, int tabIndex)
678 {
679     if (tabIndex == -1) return;
680
681     if (GetWindowTheme (infoPtr->hwnd))
682     {
683         RECT rect;
684         TAB_InternalGetItemRect(infoPtr, tabIndex, &rect, NULL);
685         InvalidateRect (infoPtr->hwnd, &rect, FALSE);
686     }
687     else
688         TAB_DrawLoneItemInterior(infoPtr, tabIndex);
689 }
690
691 /******************************************************************************
692  * TAB_HotTrackTimerProc
693  *
694  * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
695  * timer is setup so we can check if the mouse is moved out of our window.
696  * (We don't get an event when the mouse leaves, the mouse-move events just
697  * stop being delivered to our window and just start being delivered to
698  * another window.)  This function is called when the timer triggers so
699  * we can check if the mouse has left our window.  If so, we un-highlight
700  * the hot-tracked tab.
701  */
702 static void CALLBACK
703 TAB_HotTrackTimerProc
704   (
705   HWND hwnd,    /* handle of window for timer messages */
706   UINT uMsg,    /* WM_TIMER message */
707   UINT_PTR idEvent, /* timer identifier */
708   DWORD dwTime  /* current system time */
709   )
710 {
711   TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
712
713   if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
714   {
715     POINT pt;
716
717     /*
718     ** If we can't get the cursor position, or if the cursor is outside our
719     ** window, we un-highlight the hot-tracked tab.  Note that the cursor is
720     ** "outside" even if it is within our bounding rect if another window
721     ** overlaps.  Note also that the case where the cursor stayed within our
722     ** window but has moved off the hot-tracked tab will be handled by the
723     ** WM_MOUSEMOVE event.
724     */
725     if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
726     {
727       /* Redraw iHotTracked to look normal */
728       INT iRedraw = infoPtr->iHotTracked;
729       infoPtr->iHotTracked = -1;
730       hottrack_refresh (infoPtr, iRedraw);
731
732       /* Kill this timer */
733       KillTimer(hwnd, TAB_HOTTRACK_TIMER);
734     }
735   }
736 }
737
738 /******************************************************************************
739  * TAB_RecalcHotTrack
740  *
741  * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
742  * should be highlighted.  This function determines which tab in a tab control,
743  * if any, is under the mouse and records that information.  The caller may
744  * supply output parameters to receive the item number of the tab item which
745  * was highlighted but isn't any longer and of the tab item which is now
746  * highlighted but wasn't previously.  The caller can use this information to
747  * selectively redraw those tab items.
748  *
749  * If the caller has a mouse position, it can supply it through the pos
750  * parameter.  For example, TAB_MouseMove does this.  Otherwise, the caller
751  * supplies NULL and this function determines the current mouse position
752  * itself.
753  */
754 static void
755 TAB_RecalcHotTrack
756   (
757   TAB_INFO*       infoPtr,
758   const LPARAM*   pos,
759   int*            out_redrawLeave,
760   int*            out_redrawEnter
761   )
762 {
763   int item = -1;
764
765
766   if (out_redrawLeave != NULL)
767     *out_redrawLeave = -1;
768   if (out_redrawEnter != NULL)
769     *out_redrawEnter = -1;
770
771   if ((GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_HOTTRACK)
772       || GetWindowTheme (infoPtr->hwnd))
773   {
774     POINT pt;
775     UINT  flags;
776
777     if (pos == NULL)
778     {
779       GetCursorPos(&pt);
780       ScreenToClient(infoPtr->hwnd, &pt);
781     }
782     else
783     {
784       pt.x = (short)LOWORD(*pos);
785       pt.y = (short)HIWORD(*pos);
786     }
787
788     item = TAB_InternalHitTest(infoPtr, pt, &flags);
789   }
790
791   if (item != infoPtr->iHotTracked)
792   {
793     if (infoPtr->iHotTracked >= 0)
794     {
795       /* Mark currently hot-tracked to be redrawn to look normal */
796       if (out_redrawLeave != NULL)
797         *out_redrawLeave = infoPtr->iHotTracked;
798
799       if (item < 0)
800       {
801         /* Kill timer which forces recheck of mouse pos */
802         KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
803       }
804     }
805     else
806     {
807       /* Start timer so we recheck mouse pos */
808       UINT timerID = SetTimer
809         (
810         infoPtr->hwnd,
811         TAB_HOTTRACK_TIMER,
812         TAB_HOTTRACK_TIMER_INTERVAL,
813         TAB_HotTrackTimerProc
814         );
815
816       if (timerID == 0)
817         return; /* Hot tracking not available */
818     }
819
820     infoPtr->iHotTracked = item;
821
822     if (item >= 0)
823     {
824         /* Mark new hot-tracked to be redrawn to look highlighted */
825       if (out_redrawEnter != NULL)
826         *out_redrawEnter = item;
827     }
828   }
829 }
830
831 /******************************************************************************
832  * TAB_MouseMove
833  *
834  * Handles the mouse-move event.  Updates tooltips.  Updates hot-tracking.
835  */
836 static LRESULT
837 TAB_MouseMove (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
838 {
839   int redrawLeave;
840   int redrawEnter;
841
842   if (infoPtr->hwndToolTip)
843     TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
844                     WM_LBUTTONDOWN, wParam, lParam);
845
846   /* Determine which tab to highlight.  Redraw tabs which change highlight
847   ** status. */
848   TAB_RecalcHotTrack(infoPtr, &lParam, &redrawLeave, &redrawEnter);
849
850   hottrack_refresh (infoPtr, redrawLeave);
851   hottrack_refresh (infoPtr, redrawEnter);
852
853   return 0;
854 }
855
856 /******************************************************************************
857  * TAB_AdjustRect
858  *
859  * Calculates the tab control's display area given the window rectangle or
860  * the window rectangle given the requested display rectangle.
861  */
862 static LRESULT TAB_AdjustRect(const TAB_INFO *infoPtr, WPARAM fLarger, LPRECT prc)
863 {
864     DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
865     LONG *iRightBottom, *iLeftTop;
866
867     TRACE ("hwnd=%p fLarger=%ld (%d,%d)-(%d,%d)\n", infoPtr->hwnd, fLarger, prc->left, prc->top, prc->right, prc->bottom);
868
869     if(lStyle & TCS_VERTICAL)
870     {
871         iRightBottom = &(prc->right);
872         iLeftTop     = &(prc->left);
873     }
874     else
875     {
876         iRightBottom = &(prc->bottom);
877         iLeftTop     = &(prc->top);
878     }
879
880     if (fLarger) /* Go from display rectangle */
881     {
882         /* Add the height of the tabs. */
883         if (lStyle & TCS_BOTTOM)
884             *iRightBottom += infoPtr->tabHeight * infoPtr->uNumRows;
885         else
886             *iLeftTop -= infoPtr->tabHeight * infoPtr->uNumRows +
887                          ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
888
889         /* Inflate the rectangle for the padding */
890         InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY); 
891
892         /* Inflate for the border */
893         InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEY);
894     }
895     else /* Go from window rectangle. */
896     {
897         /* Deflate the rectangle for the border */
898         InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEY);
899
900         /* Deflate the rectangle for the padding */
901         InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
902
903         /* Remove the height of the tabs. */
904         if (lStyle & TCS_BOTTOM)
905             *iRightBottom -= infoPtr->tabHeight * infoPtr->uNumRows;
906         else
907             *iLeftTop += (infoPtr->tabHeight) * infoPtr->uNumRows +
908                          ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
909     }
910
911   return 0;
912 }
913
914 /******************************************************************************
915  * TAB_OnHScroll
916  *
917  * This method will handle the notification from the scroll control and
918  * perform the scrolling operation on the tab control.
919  */
920 static LRESULT TAB_OnHScroll(TAB_INFO *infoPtr, int nScrollCode, int nPos, HWND hwndScroll)
921 {
922   if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
923   {
924      if(nPos < infoPtr->leftmostVisible)
925         infoPtr->leftmostVisible--;
926      else
927         infoPtr->leftmostVisible++;
928
929      TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
930      TAB_InvalidateTabArea(infoPtr);
931      SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
932                    MAKELONG(infoPtr->leftmostVisible, 0));
933    }
934
935    return 0;
936 }
937
938 /******************************************************************************
939  * TAB_SetupScrolling
940  *
941  * This method will check the current scrolling state and make sure the
942  * scrolling control is displayed (or not).
943  */
944 static void TAB_SetupScrolling(
945   HWND        hwnd,
946   TAB_INFO*   infoPtr,
947   const RECT* clientRect)
948 {
949   static const WCHAR msctls_updown32W[] = { 'm','s','c','t','l','s','_','u','p','d','o','w','n','3','2',0 };
950   static const WCHAR emptyW[] = { 0 };
951   INT maxRange = 0;
952   DWORD lStyle = GetWindowLongW(hwnd, GWL_STYLE);
953
954   if (infoPtr->needsScrolling)
955   {
956     RECT controlPos;
957     INT vsize, tabwidth;
958
959     /*
960      * Calculate the position of the scroll control.
961      */
962     if(lStyle & TCS_VERTICAL)
963     {
964       controlPos.right = clientRect->right;
965       controlPos.left  = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
966
967       if (lStyle & TCS_BOTTOM)
968       {
969         controlPos.top    = clientRect->bottom - infoPtr->tabHeight;
970         controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
971       }
972       else
973       {
974         controlPos.bottom = clientRect->top + infoPtr->tabHeight;
975         controlPos.top    = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
976       }
977     }
978     else
979     {
980       controlPos.right = clientRect->right;
981       controlPos.left  = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
982
983       if (lStyle & TCS_BOTTOM)
984       {
985         controlPos.top    = clientRect->bottom - infoPtr->tabHeight;
986         controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
987       }
988       else
989       {
990         controlPos.bottom = clientRect->top + infoPtr->tabHeight;
991         controlPos.top    = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
992       }
993     }
994
995     /*
996      * If we don't have a scroll control yet, we want to create one.
997      * If we have one, we want to make sure it's positioned properly.
998      */
999     if (infoPtr->hwndUpDown==0)
1000     {
1001       infoPtr->hwndUpDown = CreateWindowW(msctls_updown32W, emptyW,
1002                                           WS_VISIBLE | WS_CHILD | UDS_HORZ,
1003                                           controlPos.left, controlPos.top,
1004                                           controlPos.right - controlPos.left,
1005                                           controlPos.bottom - controlPos.top,
1006                                           hwnd, NULL, NULL, NULL);
1007     }
1008     else
1009     {
1010       SetWindowPos(infoPtr->hwndUpDown,
1011                    NULL,
1012                    controlPos.left, controlPos.top,
1013                    controlPos.right - controlPos.left,
1014                    controlPos.bottom - controlPos.top,
1015                    SWP_SHOWWINDOW | SWP_NOZORDER);
1016     }
1017
1018     /* Now calculate upper limit of the updown control range.
1019      * We do this by calculating how many tabs will be offscreen when the
1020      * last tab is visible.
1021      */
1022     if(infoPtr->uNumItem)
1023     {
1024        vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
1025        maxRange = infoPtr->uNumItem;
1026        tabwidth = TAB_GetItem(infoPtr, infoPtr->uNumItem - 1)->rect.right;
1027
1028        for(; maxRange > 0; maxRange--)
1029        {
1030           if(tabwidth - TAB_GetItem(infoPtr,maxRange - 1)->rect.left > vsize)
1031              break;
1032        }
1033
1034        if(maxRange == infoPtr->uNumItem)
1035           maxRange--;
1036     }
1037   }
1038   else
1039   {
1040     /* If we once had a scroll control... hide it */
1041     if (infoPtr->hwndUpDown!=0)
1042       ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1043   }
1044   if (infoPtr->hwndUpDown)
1045      SendMessageW(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1046 }
1047
1048 /******************************************************************************
1049  * TAB_SetItemBounds
1050  *
1051  * This method will calculate the position rectangles of all the items in the
1052  * control. The rectangle calculated starts at 0 for the first item in the
1053  * list and ignores scrolling and selection.
1054  * It also uses the current font to determine the height of the tab row and
1055  * it checks if all the tabs fit in the client area of the window. If they
1056  * don't, a scrolling control is added.
1057  */
1058 static void TAB_SetItemBounds (TAB_INFO *infoPtr)
1059 {
1060   LONG        lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1061   TEXTMETRICW fontMetrics;
1062   UINT        curItem;
1063   INT         curItemLeftPos;
1064   INT         curItemRowCount;
1065   HFONT       hFont, hOldFont;
1066   HDC         hdc;
1067   RECT        clientRect;
1068   INT         iTemp;
1069   RECT*       rcItem;
1070   INT         iIndex;
1071   INT         icon_width = 0;
1072
1073   /*
1074    * We need to get text information so we need a DC and we need to select
1075    * a font.
1076    */
1077   hdc = GetDC(infoPtr->hwnd);
1078
1079   hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1080   hOldFont = SelectObject (hdc, hFont);
1081
1082   /*
1083    * We will base the rectangle calculations on the client rectangle
1084    * of the control.
1085    */
1086   GetClientRect(infoPtr->hwnd, &clientRect);
1087
1088   /* if TCS_VERTICAL then swap the height and width so this code places the
1089      tabs along the top of the rectangle and we can just rotate them after
1090      rather than duplicate all of the below code */
1091   if(lStyle & TCS_VERTICAL)
1092   {
1093      iTemp = clientRect.bottom;
1094      clientRect.bottom = clientRect.right;
1095      clientRect.right = iTemp;
1096   }
1097
1098   /* Now use hPadding and vPadding */
1099   infoPtr->uHItemPadding = infoPtr->uHItemPadding_s;
1100   infoPtr->uVItemPadding = infoPtr->uVItemPadding_s;
1101   
1102   /* The leftmost item will be "0" aligned */
1103   curItemLeftPos = 0;
1104   curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1105
1106   if (!(infoPtr->fHeightSet))
1107   {
1108     int item_height;
1109     int icon_height = 0;
1110
1111     /* Use the current font to determine the height of a tab. */
1112     GetTextMetricsW(hdc, &fontMetrics);
1113
1114     /* Get the icon height */
1115     if (infoPtr->himl)
1116       ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1117
1118     /* Take the highest between font or icon */
1119     if (fontMetrics.tmHeight > icon_height)
1120       item_height = fontMetrics.tmHeight + 2;
1121     else
1122       item_height = icon_height;
1123
1124     /*
1125      * Make sure there is enough space for the letters + icon + growing the
1126      * selected item + extra space for the selected item.
1127      */
1128     infoPtr->tabHeight = item_height + 
1129                          ((lStyle & TCS_BUTTONS) ? 2 : 1) *
1130                           infoPtr->uVItemPadding;
1131
1132     TRACE("tabH=%d, tmH=%d, iconh=%d\n",
1133           infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1134   }
1135
1136   TRACE("client right=%d\n", clientRect.right);
1137
1138   /* Get the icon width */
1139   if (infoPtr->himl)
1140   {
1141     ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1142
1143     if (lStyle & TCS_FIXEDWIDTH)
1144       icon_width += 4;
1145     else
1146       /* Add padding if icon is present */
1147       icon_width += infoPtr->uHItemPadding;
1148   }
1149
1150   for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1151   {
1152     TAB_ITEM *curr = TAB_GetItem(infoPtr, curItem);
1153         
1154     /* Set the leftmost position of the tab. */
1155     curr->rect.left = curItemLeftPos;
1156
1157     if (lStyle & TCS_FIXEDWIDTH)
1158     {
1159       curr->rect.right = curr->rect.left +
1160         max(infoPtr->tabWidth, icon_width);
1161     }
1162     else if (!curr->pszText)
1163     {
1164       /* If no text use minimum tab width including padding. */
1165       if (infoPtr->tabMinWidth < 0)
1166         curr->rect.right = curr->rect.left + GET_DEFAULT_MIN_TAB_WIDTH(infoPtr);
1167       else
1168       {
1169         curr->rect.right = curr->rect.left + infoPtr->tabMinWidth;
1170
1171         /* Add extra padding if icon is present */
1172         if (infoPtr->himl && infoPtr->tabMinWidth > 0 && infoPtr->tabMinWidth < DEFAULT_MIN_TAB_WIDTH
1173             && infoPtr->uHItemPadding > 1)
1174           curr->rect.right += EXTRA_ICON_PADDING * (infoPtr->uHItemPadding-1);
1175       }
1176     }
1177     else
1178     {
1179       int tabwidth;
1180       SIZE size;
1181       /* Calculate how wide the tab is depending on the text it contains */
1182       GetTextExtentPoint32W(hdc, curr->pszText,
1183                             lstrlenW(curr->pszText), &size);
1184
1185       tabwidth = size.cx + icon_width + 2 * infoPtr->uHItemPadding;
1186
1187       if (infoPtr->tabMinWidth < 0)
1188         tabwidth = max(tabwidth, GET_DEFAULT_MIN_TAB_WIDTH(infoPtr));
1189       else
1190         tabwidth = max(tabwidth, infoPtr->tabMinWidth);
1191
1192       curr->rect.right = curr->rect.left + tabwidth;
1193       TRACE("for <%s>, l,r=%d,%d\n",
1194           debugstr_w(curr->pszText), curr->rect.left, curr->rect.right);
1195     }
1196
1197     /*
1198      * Check if this is a multiline tab control and if so
1199      * check to see if we should wrap the tabs
1200      *
1201      * Wrap all these tabs. We will arrange them evenly later.
1202      *
1203      */
1204
1205     if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1206         (curr->rect.right > 
1207         (clientRect.right - CONTROL_BORDER_SIZEX - DISPLAY_AREA_PADDINGX)))
1208     {
1209         curr->rect.right -= curr->rect.left;
1210
1211         curr->rect.left = 0;
1212         curItemRowCount++;
1213         TRACE("wrapping <%s>, l,r=%d,%d\n", debugstr_w(curr->pszText),
1214             curr->rect.left, curr->rect.right);
1215     }
1216
1217     curr->rect.bottom = 0;
1218     curr->rect.top = curItemRowCount - 1;
1219
1220     TRACE("Rect: T %i, L %i, B %i, R %i\n", curr->rect.top,
1221           curr->rect.left, curr->rect.bottom, curr->rect.right);
1222
1223     /*
1224      * The leftmost position of the next item is the rightmost position
1225      * of this one.
1226      */
1227     if (lStyle & TCS_BUTTONS)
1228     {
1229       curItemLeftPos = curr->rect.right + BUTTON_SPACINGX;
1230       if (lStyle & TCS_FLATBUTTONS)
1231         curItemLeftPos += FLAT_BTN_SPACINGX;
1232     }
1233     else
1234       curItemLeftPos = curr->rect.right;
1235   }
1236
1237   if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1238   {
1239     /*
1240      * Check if we need a scrolling control.
1241      */
1242     infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1243                                clientRect.right);
1244
1245     /* Don't need scrolling, then update infoPtr->leftmostVisible */
1246     if(!infoPtr->needsScrolling)
1247       infoPtr->leftmostVisible = 0;
1248   }
1249   else
1250   {
1251     /*
1252      * No scrolling in Multiline or Vertical styles.
1253      */
1254     infoPtr->needsScrolling = FALSE;
1255     infoPtr->leftmostVisible = 0;
1256   }
1257   TAB_SetupScrolling(infoPtr->hwnd, infoPtr, &clientRect);
1258
1259   /* Set the number of rows */
1260   infoPtr->uNumRows = curItemRowCount;
1261
1262   /* Arrange all tabs evenly if style says so */
1263    if (!(lStyle & TCS_RAGGEDRIGHT) &&
1264        ((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1265        (infoPtr->uNumItem > 0) &&
1266        (infoPtr->uNumRows > 1))
1267    {
1268       INT tabPerRow,remTab,iRow;
1269       UINT iItm;
1270       INT iCount=0;
1271
1272       /*
1273        * Ok windows tries to even out the rows. place the same
1274        * number of tabs in each row. So lets give that a shot
1275        */
1276
1277       tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1278       remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1279
1280       for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1281            iItm<infoPtr->uNumItem;
1282            iItm++,iCount++)
1283       {
1284           /* normalize the current rect */
1285           TAB_ITEM *curr = TAB_GetItem(infoPtr, iItm);
1286  
1287           /* shift the item to the left side of the clientRect */
1288           curr->rect.right -= curr->rect.left;
1289           curr->rect.left = 0;
1290
1291           TRACE("r=%d, cl=%d, cl.r=%d, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1292               curr->rect.right, curItemLeftPos, clientRect.right,
1293               iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1294
1295           /* if we have reached the maximum number of tabs on this row */
1296           /* move to the next row, reset our current item left position and */
1297           /* the count of items on this row */
1298
1299           if (lStyle & TCS_VERTICAL) {
1300               /* Vert: Add the remaining tabs in the *last* remainder rows */
1301               if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1302                   iRow++;
1303                   curItemLeftPos = 0;
1304                   iCount = 0;
1305               }
1306           } else {
1307               /* Horz: Add the remaining tabs in the *first* remainder rows */
1308               if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1309                   iRow++;
1310                   curItemLeftPos = 0;
1311                   iCount = 0;
1312               }
1313           }
1314
1315           /* shift the item to the right to place it as the next item in this row */
1316           curr->rect.left += curItemLeftPos;
1317           curr->rect.right += curItemLeftPos;
1318           curr->rect.top = iRow;
1319           if (lStyle & TCS_BUTTONS)
1320           {
1321             curItemLeftPos = curr->rect.right + 1;
1322             if (lStyle & TCS_FLATBUTTONS)
1323               curItemLeftPos += FLAT_BTN_SPACINGX;
1324           }
1325           else
1326             curItemLeftPos = curr->rect.right;
1327
1328           TRACE("arranging <%s>, l,r=%d,%d, row=%d\n",
1329               debugstr_w(curr->pszText), curr->rect.left,
1330               curr->rect.right, curr->rect.top);
1331       }
1332
1333       /*
1334        * Justify the rows
1335        */
1336       {
1337         INT widthDiff, iIndexStart=0, iIndexEnd=0;
1338         INT remainder;
1339         INT iCount=0;
1340
1341         while(iIndexStart < infoPtr->uNumItem)
1342         {
1343           TAB_ITEM *start = TAB_GetItem(infoPtr, iIndexStart);
1344
1345           /*
1346            * find the index of the row
1347            */
1348           /* find the first item on the next row */
1349           for (iIndexEnd=iIndexStart;
1350               (iIndexEnd < infoPtr->uNumItem) &&
1351               (TAB_GetItem(infoPtr, iIndexEnd)->rect.top ==
1352                 start->rect.top) ;
1353               iIndexEnd++)
1354           /* intentionally blank */;
1355
1356           /*
1357            * we need to justify these tabs so they fill the whole given
1358            * client area
1359            *
1360            */
1361           /* find the amount of space remaining on this row */
1362           widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1363                         TAB_GetItem(infoPtr, iIndexEnd - 1)->rect.right;
1364
1365           /* iCount is the number of tab items on this row */
1366           iCount = iIndexEnd - iIndexStart;
1367
1368           if (iCount > 1)
1369           {
1370             remainder = widthDiff % iCount;
1371             widthDiff = widthDiff / iCount;
1372             /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1373             for (iIndex=iIndexStart, iCount=0; iIndex < iIndexEnd; iIndex++, iCount++)
1374             {
1375               TAB_ITEM *item = TAB_GetItem(infoPtr, iIndex);
1376
1377               item->rect.left += iCount * widthDiff;
1378               item->rect.right += (iCount + 1) * widthDiff;
1379
1380               TRACE("adjusting 1 <%s>, l,r=%d,%d\n",
1381                   debugstr_w(item->pszText),
1382                   item->rect.left, item->rect.right);
1383
1384             }
1385             TAB_GetItem(infoPtr, iIndex - 1)->rect.right += remainder;
1386           }
1387           else /* we have only one item on this row, make it take up the entire row */
1388           {
1389             start->rect.left = clientRect.left;
1390             start->rect.right = clientRect.right - 4;
1391
1392             TRACE("adjusting 2 <%s>, l,r=%d,%d\n",
1393                 debugstr_w(start->pszText),
1394                 start->rect.left, start->rect.right);
1395
1396           }
1397
1398
1399           iIndexStart = iIndexEnd;
1400         }
1401       }
1402   }
1403
1404   /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1405   if(lStyle & TCS_VERTICAL)
1406   {
1407     RECT rcOriginal;
1408     for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1409     {
1410       rcItem = &TAB_GetItem(infoPtr, iIndex)->rect;
1411
1412       rcOriginal = *rcItem;
1413
1414       /* this is rotating the items by 90 degrees clockwise around the center of the control */
1415       rcItem->top = (rcOriginal.left - clientRect.left);
1416       rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1417       rcItem->left = rcOriginal.top;
1418       rcItem->right = rcOriginal.bottom;
1419     }
1420   }
1421
1422   TAB_EnsureSelectionVisible(infoPtr);
1423   TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
1424
1425   /* Cleanup */
1426   SelectObject (hdc, hOldFont);
1427   ReleaseDC (infoPtr->hwnd, hdc);
1428 }
1429
1430
1431 static void
1432 TAB_EraseTabInterior(const TAB_INFO *infoPtr, HDC hdc, INT iItem, RECT *drawRect)
1433 {
1434     LONG     lStyle  = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1435     HBRUSH   hbr = CreateSolidBrush (comctl32_color.clrBtnFace);
1436     BOOL     deleteBrush = TRUE;
1437     RECT     rTemp = *drawRect;
1438
1439     InflateRect(&rTemp, -2, -2);
1440     if (lStyle & TCS_BUTTONS)
1441     {
1442         if (iItem == infoPtr->iSelected)
1443         {
1444             /* Background color */
1445             if (!(lStyle & TCS_OWNERDRAWFIXED))
1446             {
1447                 DeleteObject(hbr);
1448                 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1449
1450                 SetTextColor(hdc, comctl32_color.clr3dFace);
1451                 SetBkColor(hdc, comctl32_color.clr3dHilight);
1452
1453                 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1454                 * we better use 0x55aa bitmap brush to make scrollbar's background
1455                 * look different from the window background.
1456                 */
1457                 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1458                     hbr = COMCTL32_hPattern55AABrush;
1459
1460                 deleteBrush = FALSE;
1461             }
1462             FillRect(hdc, &rTemp, hbr);
1463         }
1464         else  /* ! selected */
1465         {
1466             if (lStyle & TCS_FLATBUTTONS)
1467             {
1468                 FillRect(hdc, drawRect, hbr);
1469                 if (iItem == infoPtr->iHotTracked)
1470                     DrawEdge(hdc, drawRect, EDGE_RAISED, BF_SOFT|BF_RECT);
1471             }
1472             else
1473                 FillRect(hdc, &rTemp, hbr);
1474         }
1475
1476     }
1477     else /* !TCS_BUTTONS */
1478     {
1479         if (!GetWindowTheme (infoPtr->hwnd))
1480             FillRect(hdc, &rTemp, hbr);
1481     }
1482
1483     /* Cleanup */
1484     if (deleteBrush) DeleteObject(hbr);
1485 }
1486
1487 /******************************************************************************
1488  * TAB_DrawItemInterior
1489  *
1490  * This method is used to draw the interior (text and icon) of a single tab
1491  * into the tab control.
1492  */
1493 static void
1494 TAB_DrawItemInterior(const TAB_INFO *infoPtr, HDC hdc, INT iItem, RECT *drawRect)
1495 {
1496   LONG      lStyle  = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1497
1498   RECT localRect;
1499
1500   HPEN   htextPen;
1501   HPEN   holdPen;
1502   INT    oldBkMode;
1503   HFONT  hOldFont;
1504   
1505 /*  if (drawRect == NULL) */
1506   {
1507     BOOL isVisible;
1508     RECT itemRect;
1509     RECT selectedRect;
1510
1511     /*
1512      * Get the rectangle for the item.
1513      */
1514     isVisible = TAB_InternalGetItemRect(infoPtr, iItem, &itemRect, &selectedRect);
1515     if (!isVisible)
1516       return;
1517
1518     /*
1519      * Make sure drawRect points to something valid; simplifies code.
1520      */
1521     drawRect = &localRect;
1522
1523     /*
1524      * This logic copied from the part of TAB_DrawItem which draws
1525      * the tab background.  It's important to keep it in sync.  I
1526      * would have liked to avoid code duplication, but couldn't figure
1527      * out how without making spaghetti of TAB_DrawItem.
1528      */
1529     if (iItem == infoPtr->iSelected)
1530       *drawRect = selectedRect;
1531     else
1532       *drawRect = itemRect;
1533         
1534     if (lStyle & TCS_BUTTONS)
1535     {
1536       if (iItem == infoPtr->iSelected)
1537       {
1538         drawRect->left   += 4;
1539         drawRect->top    += 4;
1540         drawRect->right  -= 4;
1541         drawRect->bottom -= 1;
1542       }
1543       else
1544       {
1545         drawRect->left   += 2;
1546         drawRect->top    += 2;
1547         drawRect->right  -= 2;
1548         drawRect->bottom -= 2;
1549       }
1550     }
1551     else
1552     {
1553       if ((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1554       {
1555         if (iItem != infoPtr->iSelected)
1556         {
1557           drawRect->left   += 2;
1558           drawRect->top    += 2;
1559           drawRect->bottom -= 2;
1560         }
1561       }
1562       else if (lStyle & TCS_VERTICAL)
1563       {
1564         if (iItem == infoPtr->iSelected)
1565         {
1566           drawRect->right  += 1;
1567         }
1568         else
1569         {
1570           drawRect->top    += 2;
1571           drawRect->right  -= 2;
1572           drawRect->bottom -= 2;
1573         }
1574       }
1575       else if (lStyle & TCS_BOTTOM)
1576       {
1577         if (iItem == infoPtr->iSelected)
1578         {
1579           drawRect->top    -= 2;
1580         }
1581         else
1582         {
1583           InflateRect(drawRect, -2, -2);
1584           drawRect->bottom += 2;
1585         }
1586       }
1587       else
1588       {
1589         if (iItem == infoPtr->iSelected)
1590         {
1591           drawRect->bottom += 3;
1592         }
1593         else
1594         {
1595           drawRect->bottom -= 2;
1596           InflateRect(drawRect, -2, 0);
1597         }
1598       }
1599     }
1600   }
1601   TRACE("drawRect=(%d,%d)-(%d,%d)\n",
1602           drawRect->left, drawRect->top, drawRect->right, drawRect->bottom);
1603
1604   /* Clear interior */
1605   TAB_EraseTabInterior (infoPtr, hdc, iItem, drawRect);
1606
1607   /* Draw the focus rectangle */
1608   if (!(lStyle & TCS_FOCUSNEVER) &&
1609       (GetFocus() == infoPtr->hwnd) &&
1610       (iItem == infoPtr->uFocus) )
1611   {
1612     RECT rFocus = *drawRect;
1613     InflateRect(&rFocus, -3, -3);
1614     if (lStyle & TCS_BOTTOM && !(lStyle & TCS_VERTICAL))
1615       rFocus.top -= 3;
1616     if (lStyle & TCS_BUTTONS)
1617     {
1618       rFocus.left -= 3;
1619       rFocus.top -= 3;
1620     }
1621
1622     DrawFocusRect(hdc, &rFocus);
1623   }
1624
1625   /*
1626    * Text pen
1627    */
1628   htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1629   holdPen  = SelectObject(hdc, htextPen);
1630   hOldFont = SelectObject(hdc, infoPtr->hFont);
1631
1632   /*
1633    * Setup for text output
1634   */
1635   oldBkMode = SetBkMode(hdc, TRANSPARENT);
1636   if (!GetWindowTheme (infoPtr->hwnd) || (lStyle & TCS_BUTTONS))
1637       SetTextColor(hdc, (((lStyle & TCS_HOTTRACK) && (iItem == infoPtr->iHotTracked) 
1638                           && !(lStyle & TCS_FLATBUTTONS)) 
1639                         | (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED)) ?
1640                         comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1641
1642   /*
1643    * if owner draw, tell the owner to draw
1644    */
1645   if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(infoPtr->hwnd))
1646   {
1647     DRAWITEMSTRUCT dis;
1648     UINT id;
1649
1650     drawRect->top += 2;
1651     drawRect->right -= 1;
1652     if ( iItem == infoPtr->iSelected )
1653     {
1654         drawRect->right -= 1;
1655         drawRect->left += 1;
1656     }
1657
1658     /*
1659      * get the control id
1660      */
1661     id = (UINT)GetWindowLongPtrW( infoPtr->hwnd, GWLP_ID );
1662
1663     /*
1664      * put together the DRAWITEMSTRUCT
1665      */
1666     dis.CtlType    = ODT_TAB;
1667     dis.CtlID      = id;
1668     dis.itemID     = iItem;
1669     dis.itemAction = ODA_DRAWENTIRE;
1670     dis.itemState = 0;
1671     if ( iItem == infoPtr->iSelected )
1672       dis.itemState |= ODS_SELECTED;
1673     if (infoPtr->uFocus == iItem) 
1674       dis.itemState |= ODS_FOCUS;
1675     dis.hwndItem = infoPtr->hwnd;
1676     dis.hDC      = hdc;
1677     CopyRect(&dis.rcItem,drawRect);
1678     dis.itemData = (ULONG_PTR)TAB_GetItem(infoPtr, iItem)->extra;
1679
1680     /*
1681      * send the draw message
1682      */
1683     SendMessageW( infoPtr->hwndNotify, WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1684   }
1685   else
1686   {
1687     TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
1688     RECT rcTemp;
1689     RECT rcImage;
1690
1691     /* used to center the icon and text in the tab */
1692     RECT rcText;
1693     INT center_offset_h, center_offset_v;
1694
1695     /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1696     rcImage = *drawRect;
1697
1698     rcTemp = *drawRect;
1699
1700     rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1701
1702     /* get the rectangle that the text fits in */
1703     if (item->pszText)
1704     {
1705       DrawTextW(hdc, item->pszText, -1, &rcText, DT_CALCRECT);
1706     }
1707     /*
1708      * If not owner draw, then do the drawing ourselves.
1709      *
1710      * Draw the icon.
1711      */
1712     if (infoPtr->himl && item->iImage != -1)
1713     {
1714       INT cx;
1715       INT cy;
1716       
1717       ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1718
1719       if(lStyle & TCS_VERTICAL)
1720       {
1721         center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right  - rcText.left))) / 2;
1722         center_offset_v = (drawRect->left + (drawRect->right - drawRect->left) - cx) / 2;
1723       }
1724       else
1725       {
1726         center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right  - rcText.left))) / 2;
1727         center_offset_v = (drawRect->top + (drawRect->bottom - drawRect->top) - cy) / 2;
1728       }
1729
1730       /* if an item is selected, the icon is shifted up instead of down */
1731       if (iItem == infoPtr->iSelected)
1732         center_offset_v -= infoPtr->uVItemPadding / 2;
1733       else
1734         center_offset_v += infoPtr->uVItemPadding / 2;
1735
1736       if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1737         center_offset_h = infoPtr->uHItemPadding;
1738
1739       if (center_offset_h < 2)
1740         center_offset_h = 2;
1741         
1742       if (center_offset_v < 0)
1743         center_offset_v = 0;
1744         
1745       TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%d,%d)-(%d,%d), textlen=%d\n",
1746           debugstr_w(item->pszText), center_offset_h, center_offset_v,
1747           drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1748           (rcText.right-rcText.left));
1749
1750       if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1751       {
1752         rcImage.top = drawRect->top + center_offset_h;
1753         /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1754         /* right side of the tab, but the image still uses the left as its x position */
1755         /* this keeps the image always drawn off of the same side of the tab */
1756         rcImage.left = drawRect->right - cx - center_offset_v;
1757         drawRect->top += cy + infoPtr->uHItemPadding;
1758       }
1759       else if(lStyle & TCS_VERTICAL)
1760       {
1761         rcImage.top  = drawRect->bottom - cy - center_offset_h;
1762         rcImage.left = drawRect->left + center_offset_v;
1763         drawRect->bottom -= cy + infoPtr->uHItemPadding;
1764       }
1765       else /* normal style, whether TCS_BOTTOM or not */
1766       {
1767         rcImage.left = drawRect->left + center_offset_h;
1768         rcImage.top = drawRect->top + center_offset_v;
1769         drawRect->left += cx + infoPtr->uHItemPadding;
1770       }
1771
1772       TRACE("drawing image=%d, left=%d, top=%d\n",
1773             item->iImage, rcImage.left, rcImage.top-1);
1774       ImageList_Draw
1775         (
1776         infoPtr->himl,
1777         item->iImage,
1778         hdc,
1779         rcImage.left,
1780         rcImage.top,
1781         ILD_NORMAL
1782         );
1783     }
1784
1785     /* Now position text */
1786     if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT)
1787       center_offset_h = infoPtr->uHItemPadding;
1788     else
1789       if(lStyle & TCS_VERTICAL)
1790         center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1791       else
1792         center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1793
1794     if(lStyle & TCS_VERTICAL)
1795     {
1796       if(lStyle & TCS_BOTTOM)
1797         drawRect->top+=center_offset_h;
1798       else
1799         drawRect->bottom-=center_offset_h;
1800
1801       center_offset_v = ((drawRect->right - drawRect->left) - (rcText.bottom - rcText.top)) / 2;
1802     }
1803     else
1804     {
1805       drawRect->left += center_offset_h;
1806       center_offset_v = ((drawRect->bottom - drawRect->top) - (rcText.bottom - rcText.top)) / 2;
1807     }
1808
1809     /* if an item is selected, the text is shifted up instead of down */
1810     if (iItem == infoPtr->iSelected)
1811         center_offset_v -= infoPtr->uVItemPadding / 2;
1812     else
1813         center_offset_v += infoPtr->uVItemPadding / 2;
1814
1815     if (center_offset_v < 0)
1816       center_offset_v = 0;
1817
1818     if(lStyle & TCS_VERTICAL)
1819       drawRect->left += center_offset_v;
1820     else
1821       drawRect->top += center_offset_v;
1822
1823     /* Draw the text */
1824     if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1825     {
1826       static const WCHAR ArialW[] = { 'A','r','i','a','l',0 };
1827       LOGFONTW logfont;
1828       HFONT hFont = 0;
1829       INT nEscapement = 900;
1830       INT nOrientation = 900;
1831
1832       if(lStyle & TCS_BOTTOM)
1833       {
1834         nEscapement = -900;
1835         nOrientation = -900;
1836       }
1837
1838       /* to get a font with the escapement and orientation we are looking for, we need to */
1839       /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1840       if (!GetObjectW((infoPtr->hFont) ?
1841                 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1842                 sizeof(LOGFONTW),&logfont))
1843       {
1844         INT iPointSize = 9;
1845
1846         lstrcpyW(logfont.lfFaceName, ArialW);
1847         logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1848                                     72);
1849         logfont.lfWeight = FW_NORMAL;
1850         logfont.lfItalic = 0;
1851         logfont.lfUnderline = 0;
1852         logfont.lfStrikeOut = 0;
1853       }
1854
1855       logfont.lfEscapement = nEscapement;
1856       logfont.lfOrientation = nOrientation;
1857       hFont = CreateFontIndirectW(&logfont);
1858       SelectObject(hdc, hFont);
1859
1860       if (item->pszText)
1861       {
1862         ExtTextOutW(hdc,
1863         (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1864         (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1865         ETO_CLIPPED,
1866         drawRect,
1867         item->pszText,
1868         lstrlenW(item->pszText),
1869         0);
1870       }
1871
1872       DeleteObject(hFont);
1873     }
1874     else
1875     {
1876       TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%d,%d)-(%d,%d), textlen=%d\n",
1877           debugstr_w(item->pszText), center_offset_h, center_offset_v,
1878           drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1879           (rcText.right-rcText.left));
1880       if (item->pszText)
1881       {
1882         DrawTextW
1883         (
1884           hdc,
1885           item->pszText,
1886           lstrlenW(item->pszText),
1887           drawRect,
1888           DT_LEFT | DT_SINGLELINE
1889         );
1890       }
1891     }
1892
1893     *drawRect = rcTemp; /* restore drawRect */
1894   }
1895
1896   /*
1897   * Cleanup
1898   */
1899   SelectObject(hdc, hOldFont);
1900   SetBkMode(hdc, oldBkMode);
1901   SelectObject(hdc, holdPen);
1902   DeleteObject( htextPen );
1903 }
1904
1905 /******************************************************************************
1906  * TAB_DrawItem
1907  *
1908  * This method is used to draw a single tab into the tab control.
1909  */
1910 static void TAB_DrawItem(const TAB_INFO *infoPtr, HDC  hdc, INT  iItem)
1911 {
1912   LONG      lStyle  = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1913   RECT      itemRect;
1914   RECT      selectedRect;
1915   BOOL      isVisible;
1916   RECT      r, fillRect, r1;
1917   INT       clRight = 0;
1918   INT       clBottom = 0;
1919   COLORREF  bkgnd, corner;
1920   HTHEME    theme;
1921
1922   /*
1923    * Get the rectangle for the item.
1924    */
1925   isVisible = TAB_InternalGetItemRect(infoPtr,
1926                                       iItem,
1927                                       &itemRect,
1928                                       &selectedRect);
1929
1930   if (isVisible)
1931   {
1932     RECT rUD, rC;
1933
1934     /* Clip UpDown control to not draw over it */
1935     if (infoPtr->needsScrolling)
1936     {
1937       GetWindowRect(infoPtr->hwnd, &rC);
1938       GetWindowRect(infoPtr->hwndUpDown, &rUD);
1939       ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
1940     }
1941
1942     /* If you need to see what the control is doing,
1943      * then override these variables. They will change what
1944      * fill colors are used for filling the tabs, and the
1945      * corners when drawing the edge.
1946      */
1947     bkgnd = comctl32_color.clrBtnFace;
1948     corner = comctl32_color.clrBtnFace;
1949
1950     if (lStyle & TCS_BUTTONS)
1951     {
1952       /* Get item rectangle */
1953       r = itemRect;
1954
1955       /* Separators between flat buttons */
1956       if (lStyle & TCS_FLATBUTTONS)
1957       {
1958         r1 = r;
1959         r1.right += (FLAT_BTN_SPACINGX -2);
1960         DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1961       }
1962
1963       if (iItem == infoPtr->iSelected)
1964       {
1965         DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1966         
1967         OffsetRect(&r, 1, 1);
1968       }
1969       else  /* ! selected */
1970       {
1971         if (!(lStyle & TCS_FLATBUTTONS))
1972           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1973       }
1974     }
1975     else /* !TCS_BUTTONS */
1976     {
1977       /* We draw a rectangle of different sizes depending on the selection
1978        * state. */
1979       if (iItem == infoPtr->iSelected) {
1980         RECT rect;
1981         GetClientRect (infoPtr->hwnd, &rect);
1982         clRight = rect.right;
1983         clBottom = rect.bottom;
1984         r = selectedRect;
1985       }
1986       else
1987         r = itemRect;
1988
1989       /*
1990        * Erase the background. (Delay it but setup rectangle.)
1991        * This is necessary when drawing the selected item since it is larger
1992        * than the others, it might overlap with stuff already drawn by the
1993        * other tabs
1994        */
1995       fillRect = r;
1996
1997       /* Draw themed tabs - but only if they are at the top.
1998        * Windows draws even side or bottom tabs themed, with wacky results.
1999        * However, since in Wine apps may get themed that did not opt in via
2000        * a manifest avoid theming when we know the result will be wrong */
2001       if ((theme = GetWindowTheme (infoPtr->hwnd)) 
2002           && ((lStyle & (TCS_VERTICAL | TCS_BOTTOM)) == 0))
2003       {
2004           static const int partIds[8] = {
2005               /* Normal item */
2006               TABP_TABITEM,
2007               TABP_TABITEMLEFTEDGE,
2008               TABP_TABITEMRIGHTEDGE,
2009               TABP_TABITEMBOTHEDGE,
2010               /* Selected tab */
2011               TABP_TOPTABITEM,
2012               TABP_TOPTABITEMLEFTEDGE,
2013               TABP_TOPTABITEMRIGHTEDGE,
2014               TABP_TOPTABITEMBOTHEDGE,
2015           };
2016           int partIndex = 0;
2017           int stateId = TIS_NORMAL;
2018
2019           /* selected and unselected tabs have different parts */
2020           if (iItem == infoPtr->iSelected)
2021               partIndex += 4;
2022           /* The part also differs on the position of a tab on a line.
2023            * "Visually" determining the position works well enough. */
2024           if(selectedRect.left == 0)
2025               partIndex += 1;
2026           if(selectedRect.right == clRight)
2027               partIndex += 2;
2028
2029           if (iItem == infoPtr->iSelected)
2030               stateId = TIS_SELECTED;
2031           else if (iItem == infoPtr->iHotTracked)
2032               stateId = TIS_HOT;
2033           else if (iItem == infoPtr->uFocus)
2034               stateId = TIS_FOCUSED;
2035
2036           /* Adjust rectangle for bottommost row */
2037           if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2038             r.bottom += 3;
2039
2040           DrawThemeBackground (theme, hdc, partIds[partIndex], stateId, &r, NULL);
2041           GetThemeBackgroundContentRect (theme, hdc, partIds[partIndex], stateId, &r, &r);
2042       }
2043       else if(lStyle & TCS_VERTICAL)
2044       {
2045         /* These are for adjusting the drawing of a Selected tab      */
2046         /* The initial values are for the normal case of non-Selected */
2047         int ZZ = 1;   /* Do not strech if selected */
2048         if (iItem == infoPtr->iSelected) {
2049             ZZ = 0;
2050
2051             /* if leftmost draw the line longer */
2052             if(selectedRect.top == 0)
2053                 fillRect.top += CONTROL_BORDER_SIZEY;
2054             /* if rightmost draw the line longer */
2055             if(selectedRect.bottom == clBottom)
2056                 fillRect.bottom -= CONTROL_BORDER_SIZEY;
2057         }
2058
2059         if (lStyle & TCS_BOTTOM)
2060         {
2061           /* Adjust both rectangles to match native */
2062           r.left += (1-ZZ);
2063
2064           TRACE("<right> item=%d, fill=(%d,%d)-(%d,%d), edge=(%d,%d)-(%d,%d)\n",
2065                 iItem,
2066                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2067                 r.left,r.top,r.right,r.bottom);
2068
2069           /* Clear interior */
2070           SetBkColor(hdc, bkgnd);
2071           ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2072
2073           /* Draw rectangular edge around tab */
2074           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
2075
2076           /* Now erase the top corner and draw diagonal edge */
2077           SetBkColor(hdc, corner);
2078           r1.left = r.right - ROUND_CORNER_SIZE - 1;
2079           r1.top = r.top;
2080           r1.right = r.right;
2081           r1.bottom = r1.top + ROUND_CORNER_SIZE;
2082           ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2083           r1.right--;
2084           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2085
2086           /* Now erase the bottom corner and draw diagonal edge */
2087           r1.left = r.right - ROUND_CORNER_SIZE - 1;
2088           r1.bottom = r.bottom;
2089           r1.right = r.right;
2090           r1.top = r1.bottom - ROUND_CORNER_SIZE;
2091           ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2092           r1.right--;
2093           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2094
2095           if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
2096               r1 = r;
2097               r1.right = r1.left;
2098               r1.left--;
2099               DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
2100           }
2101
2102         }
2103         else
2104         {
2105           TRACE("<left> item=%d, fill=(%d,%d)-(%d,%d), edge=(%d,%d)-(%d,%d)\n",
2106                 iItem,
2107                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2108                 r.left,r.top,r.right,r.bottom);
2109
2110           /* Clear interior */
2111           SetBkColor(hdc, bkgnd);
2112           ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2113
2114           /* Draw rectangular edge around tab */
2115           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2116
2117           /* Now erase the top corner and draw diagonal edge */
2118           SetBkColor(hdc, corner);
2119           r1.left = r.left;
2120           r1.top = r.top;
2121           r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2122           r1.bottom = r1.top + ROUND_CORNER_SIZE;
2123           ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2124           r1.left++;
2125           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2126
2127           /* Now erase the bottom corner and draw diagonal edge */
2128           r1.left = r.left;
2129           r1.bottom = r.bottom;
2130           r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2131           r1.top = r1.bottom - ROUND_CORNER_SIZE;
2132           ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2133           r1.left++;
2134           DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2135         }
2136       }
2137       else  /* ! TCS_VERTICAL */
2138       {
2139         /* These are for adjusting the drawing of a Selected tab      */
2140         /* The initial values are for the normal case of non-Selected */
2141         if (iItem == infoPtr->iSelected) {
2142             /* if leftmost draw the line longer */
2143             if(selectedRect.left == 0)
2144                 fillRect.left += CONTROL_BORDER_SIZEX;
2145             /* if rightmost draw the line longer */
2146             if(selectedRect.right == clRight)
2147                 fillRect.right -= CONTROL_BORDER_SIZEX;
2148         }
2149
2150         if (lStyle & TCS_BOTTOM)
2151         {
2152           /* Adjust both rectangles for topmost row */
2153           if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2154           {
2155             fillRect.top -= 2;
2156             r.top -= 1;
2157           }
2158
2159           TRACE("<bottom> item=%d, fill=(%d,%d)-(%d,%d), edge=(%d,%d)-(%d,%d)\n",
2160                 iItem,
2161                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2162                 r.left,r.top,r.right,r.bottom);
2163
2164           /* Clear interior */
2165           SetBkColor(hdc, bkgnd);
2166           ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2167
2168           /* Draw rectangular edge around tab */
2169           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2170
2171           /* Now erase the righthand corner and draw diagonal edge */
2172           SetBkColor(hdc, corner);
2173           r1.left = r.right - ROUND_CORNER_SIZE;
2174           r1.bottom = r.bottom;
2175           r1.right = r.right;
2176           r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2177           ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2178           r1.bottom--;
2179           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2180
2181           /* Now erase the lefthand corner and draw diagonal edge */
2182           r1.left = r.left;
2183           r1.bottom = r.bottom;
2184           r1.right = r1.left + ROUND_CORNER_SIZE;
2185           r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2186           ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2187           r1.bottom--;
2188           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2189
2190           if (iItem == infoPtr->iSelected)
2191           {
2192             r.top += 2;
2193             r.left += 1;
2194             if (selectedRect.left == 0)
2195             {
2196               r1 = r;
2197               r1.bottom = r1.top;
2198               r1.top--;
2199               DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2200             }
2201           }
2202
2203         }
2204         else
2205         {
2206           /* Adjust both rectangles for bottommost row */
2207           if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2208           {
2209             fillRect.bottom += 3;
2210             r.bottom += 2;
2211           }
2212
2213           TRACE("<top> item=%d, fill=(%d,%d)-(%d,%d), edge=(%d,%d)-(%d,%d)\n",
2214                 iItem,
2215                 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2216                 r.left,r.top,r.right,r.bottom);
2217
2218           /* Clear interior */
2219           SetBkColor(hdc, bkgnd);
2220           ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2221
2222           /* Draw rectangular edge around tab */
2223           DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2224
2225           /* Now erase the righthand corner and draw diagonal edge */
2226           SetBkColor(hdc, corner);
2227           r1.left = r.right - ROUND_CORNER_SIZE;
2228           r1.top = r.top;
2229           r1.right = r.right;
2230           r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2231           ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2232           r1.top++;
2233           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2234
2235           /* Now erase the lefthand corner and draw diagonal edge */
2236           r1.left = r.left;
2237           r1.top = r.top;
2238           r1.right = r1.left + ROUND_CORNER_SIZE;
2239           r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2240           ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2241           r1.top++;
2242           DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2243         }
2244       }
2245     }
2246
2247     TAB_DumpItemInternal(infoPtr, iItem);
2248
2249     /* This modifies r to be the text rectangle. */
2250     TAB_DrawItemInterior(infoPtr, hdc, iItem, &r);
2251   }
2252 }
2253
2254 /******************************************************************************
2255  * TAB_DrawBorder
2256  *
2257  * This method is used to draw the raised border around the tab control
2258  * "content" area.
2259  */
2260 static void TAB_DrawBorder(const TAB_INFO *infoPtr, HDC hdc)
2261 {
2262   RECT rect;
2263   DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2264   HTHEME theme = GetWindowTheme (infoPtr->hwnd);
2265
2266   GetClientRect (infoPtr->hwnd, &rect);
2267
2268   /*
2269    * Adjust for the style
2270    */
2271
2272   if (infoPtr->uNumItem)
2273   {
2274     if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2275       rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2276     else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2277       rect.right  -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2278     else if(lStyle & TCS_VERTICAL)
2279       rect.left   += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2280     else /* not TCS_VERTICAL and not TCS_BOTTOM */
2281       rect.top    += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2282   }
2283
2284   TRACE("border=(%d,%d)-(%d,%d)\n",
2285         rect.left, rect.top, rect.right, rect.bottom);
2286
2287   if (theme)
2288       DrawThemeBackground (theme, hdc, TABP_PANE, 0, &rect, NULL);
2289   else
2290       DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2291 }
2292
2293 /******************************************************************************
2294  * TAB_Refresh
2295  *
2296  * This method repaints the tab control..
2297  */
2298 static void TAB_Refresh (TAB_INFO *infoPtr, HDC hdc)
2299 {
2300   HFONT hOldFont;
2301   INT i;
2302
2303   if (!infoPtr->DoRedraw)
2304     return;
2305
2306   hOldFont = SelectObject (hdc, infoPtr->hFont);
2307
2308   if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS)
2309   {
2310     for (i = 0; i < infoPtr->uNumItem; i++)
2311       TAB_DrawItem (infoPtr, hdc, i);
2312   }
2313   else
2314   {
2315     /* Draw all the non selected item first */
2316     for (i = 0; i < infoPtr->uNumItem; i++)
2317     {
2318       if (i != infoPtr->iSelected)
2319         TAB_DrawItem (infoPtr, hdc, i);
2320     }
2321
2322     /* Now, draw the border, draw it before the selected item
2323      * since the selected item overwrites part of the border. */
2324     TAB_DrawBorder (infoPtr, hdc);
2325
2326     /* Then, draw the selected item */
2327     TAB_DrawItem (infoPtr, hdc, infoPtr->iSelected);
2328   }
2329
2330   SelectObject (hdc, hOldFont);
2331 }
2332
2333 static inline DWORD TAB_GetRowCount (const TAB_INFO *infoPtr)
2334 {
2335   return infoPtr->uNumRows;
2336 }
2337
2338 static inline LRESULT TAB_SetRedraw (TAB_INFO *infoPtr, BOOL doRedraw)
2339 {
2340   infoPtr->DoRedraw = doRedraw;
2341   return 0;
2342 }
2343
2344 /******************************************************************************
2345  * TAB_EnsureSelectionVisible
2346  *
2347  * This method will make sure that the current selection is completely
2348  * visible by scrolling until it is.
2349  */
2350 static void TAB_EnsureSelectionVisible(
2351   TAB_INFO* infoPtr)
2352 {
2353   INT iSelected = infoPtr->iSelected;
2354   LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2355   INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2356
2357   /* set the items row to the bottommost row or topmost row depending on
2358    * style */
2359   if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2360   {
2361       TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2362       INT newselected;
2363       INT iTargetRow;
2364
2365       if(lStyle & TCS_VERTICAL)
2366         newselected = selected->rect.left;
2367       else
2368         newselected = selected->rect.top;
2369
2370       /* the target row is always (number of rows - 1)
2371          as row 0 is furthest from the clientRect */
2372       iTargetRow = infoPtr->uNumRows - 1;
2373
2374       if (newselected != iTargetRow)
2375       {
2376          UINT i;
2377          if(lStyle & TCS_VERTICAL)
2378          {
2379            for (i=0; i < infoPtr->uNumItem; i++)
2380            {
2381              /* move everything in the row of the selected item to the iTargetRow */
2382              TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2383
2384              if (item->rect.left == newselected )
2385                  item->rect.left = iTargetRow;
2386              else
2387              {
2388                if (item->rect.left > newselected)
2389                  item->rect.left-=1;
2390              }
2391            }
2392          }
2393          else
2394          {
2395            for (i=0; i < infoPtr->uNumItem; i++)
2396            {
2397              TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2398
2399              if (item->rect.top == newselected )
2400                  item->rect.top = iTargetRow;
2401              else
2402              {
2403                if (item->rect.top > newselected)
2404                  item->rect.top-=1;
2405              }
2406           }
2407         }
2408         TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2409       }
2410   }
2411
2412   /*
2413    * Do the trivial cases first.
2414    */
2415   if ( (!infoPtr->needsScrolling) ||
2416        (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2417     return;
2418
2419   if (infoPtr->leftmostVisible >= iSelected)
2420   {
2421     infoPtr->leftmostVisible = iSelected;
2422   }
2423   else
2424   {
2425      TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2426      RECT r;
2427      INT width;
2428      UINT i;
2429
2430      /* Calculate the part of the client area that is visible */
2431      GetClientRect(infoPtr->hwnd, &r);
2432      width = r.right;
2433
2434      GetClientRect(infoPtr->hwndUpDown, &r);
2435      width -= r.right;
2436
2437      if ((selected->rect.right -
2438           selected->rect.left) >= width )
2439      {
2440         /* Special case: width of selected item is greater than visible
2441          * part of control.
2442          */
2443         infoPtr->leftmostVisible = iSelected;
2444      }
2445      else
2446      {
2447         for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2448         {
2449            if ((selected->rect.right - TAB_GetItem(infoPtr, i)->rect.left) < width)
2450               break;
2451         }
2452         infoPtr->leftmostVisible = i;
2453      }
2454   }
2455
2456   if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2457     TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2458
2459   SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2460                MAKELONG(infoPtr->leftmostVisible, 0));
2461 }
2462
2463 /******************************************************************************
2464  * TAB_InvalidateTabArea
2465  *
2466  * This method will invalidate the portion of the control that contains the
2467  * tabs. It is called when the state of the control changes and needs
2468  * to be redisplayed
2469  */
2470 static void TAB_InvalidateTabArea(const TAB_INFO *infoPtr)
2471 {
2472   RECT clientRect, rInvalidate, rAdjClient;
2473   DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2474   INT lastRow = infoPtr->uNumRows - 1;
2475   RECT rect;
2476
2477   if (lastRow < 0) return;
2478
2479   GetClientRect(infoPtr->hwnd, &clientRect);
2480   rInvalidate = clientRect;
2481   rAdjClient = clientRect;
2482
2483   TAB_AdjustRect(infoPtr, 0, &rAdjClient);
2484
2485   TAB_InternalGetItemRect(infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2486   if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2487   {
2488     rInvalidate.left = rAdjClient.right;
2489     if (infoPtr->uNumRows == 1)
2490       rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2491   }
2492   else if(lStyle & TCS_VERTICAL)
2493   {
2494     rInvalidate.right = rAdjClient.left;
2495     if (infoPtr->uNumRows == 1)
2496       rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2497   }
2498   else if (lStyle & TCS_BOTTOM)
2499   {
2500     rInvalidate.top = rAdjClient.bottom;
2501     if (infoPtr->uNumRows == 1)
2502       rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2503   }
2504   else 
2505   {
2506     rInvalidate.bottom = rAdjClient.top;
2507     if (infoPtr->uNumRows == 1)
2508       rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2509   }
2510   
2511   /* Punch out the updown control */
2512   if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2513     RECT r;
2514     GetClientRect(infoPtr->hwndUpDown, &r);
2515     if (rInvalidate.right > clientRect.right - r.left)
2516       rInvalidate.right = rInvalidate.right - (r.right - r.left);
2517     else
2518       rInvalidate.right = clientRect.right - r.left;
2519   }
2520   
2521   TRACE("invalidate (%d,%d)-(%d,%d)\n",
2522         rInvalidate.left,  rInvalidate.top,
2523         rInvalidate.right, rInvalidate.bottom);
2524  
2525   InvalidateRect(infoPtr->hwnd, &rInvalidate, TRUE);
2526 }
2527
2528 static inline LRESULT TAB_Paint (TAB_INFO *infoPtr, HDC hdcPaint)
2529 {
2530   HDC hdc;
2531   PAINTSTRUCT ps;
2532
2533   if (hdcPaint)
2534     hdc = hdcPaint;
2535   else
2536   {
2537     hdc = BeginPaint (infoPtr->hwnd, &ps);
2538     TRACE("erase %d, rect=(%d,%d)-(%d,%d)\n",
2539          ps.fErase,
2540          ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2541   }
2542
2543   TAB_Refresh (infoPtr, hdc);
2544
2545   if (!hdcPaint)
2546     EndPaint (infoPtr->hwnd, &ps);
2547
2548   return 0;
2549 }
2550
2551 static LRESULT
2552 TAB_InsertItemT (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2553 {
2554   TAB_ITEM *item;
2555   TCITEMW *pti;
2556   INT iItem;
2557   RECT rect;
2558
2559   GetClientRect (infoPtr->hwnd, &rect);
2560   TRACE("Rect: %p T %i, L %i, B %i, R %i\n", infoPtr->hwnd,
2561         rect.top, rect.left, rect.bottom, rect.right);
2562
2563   pti = (TCITEMW *)lParam;
2564   iItem = (INT)wParam;
2565
2566   if (iItem < 0) return -1;
2567   if (iItem > infoPtr->uNumItem)
2568     iItem = infoPtr->uNumItem;
2569
2570   TAB_DumpItemExternalT(pti, iItem, bUnicode);
2571
2572
2573   if (infoPtr->uNumItem == 0) {
2574     infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2575     infoPtr->uNumItem++;
2576     infoPtr->iSelected = 0;
2577   }
2578   else {
2579     LPBYTE oldItems = (LPBYTE)infoPtr->items;
2580
2581     infoPtr->uNumItem++;
2582     infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2583
2584     /* pre insert copy */
2585     if (iItem > 0) {
2586       memcpy (infoPtr->items, oldItems,
2587               iItem * TAB_ITEM_SIZE(infoPtr));
2588     }
2589
2590     /* post insert copy */
2591     if (iItem < infoPtr->uNumItem - 1) {
2592       memcpy (TAB_GetItem(infoPtr, iItem + 1),
2593               oldItems + iItem * TAB_ITEM_SIZE(infoPtr),
2594               (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2595
2596     }
2597
2598     if (iItem <= infoPtr->iSelected)
2599       infoPtr->iSelected++;
2600
2601     Free (oldItems);
2602   }
2603
2604   item = TAB_GetItem(infoPtr, iItem);
2605
2606   item->pszText = NULL;
2607
2608   if (pti->mask & TCIF_TEXT)
2609   {
2610     if (bUnicode)
2611       Str_SetPtrW (&item->pszText, pti->pszText);
2612     else
2613       Str_SetPtrAtoW (&item->pszText, (LPSTR)pti->pszText);
2614   }
2615
2616   if (pti->mask & TCIF_IMAGE)
2617     item->iImage = pti->iImage;
2618   else
2619     item->iImage = -1;
2620
2621   if (pti->mask & TCIF_PARAM)
2622     memcpy(item->extra, &pti->lParam, infoPtr->cbInfo);
2623   else
2624     memset(item->extra, 0, infoPtr->cbInfo);
2625   
2626   TAB_SetItemBounds(infoPtr);
2627   if (infoPtr->uNumItem > 1)
2628     TAB_InvalidateTabArea(infoPtr);
2629   else
2630     InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2631
2632   TRACE("[%p]: added item %d %s\n",
2633         infoPtr->hwnd, iItem, debugstr_w(item->pszText));
2634
2635   /* If we haven't set the current focus yet, set it now. */
2636   if (infoPtr->uFocus == -1)
2637     TAB_SetCurFocus(infoPtr, iItem);
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       Free (TAB_GetItem(infoPtr, iItem)->pszText);
3067     }
3068     Free (infoPtr->items);
3069   }
3070
3071   if (infoPtr->hwndToolTip)
3072     DestroyWindow (infoPtr->hwndToolTip);
3073
3074   if (infoPtr->hwndUpDown)
3075     DestroyWindow(infoPtr->hwndUpDown);
3076
3077   if (infoPtr->iHotTracked >= 0)
3078     KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
3079
3080   CloseThemeData (GetWindowTheme (infoPtr->hwnd));
3081   
3082   Free (infoPtr);
3083   return 0;
3084 }
3085
3086 /* update theme after a WM_THEMECHANGED message */
3087 static LRESULT theme_changed(const TAB_INFO *infoPtr)
3088 {
3089     HTHEME theme = GetWindowTheme (infoPtr->hwnd);
3090     CloseThemeData (theme);
3091     OpenThemeData (infoPtr->hwnd, themeClass);
3092     return 0;
3093 }
3094
3095 static LRESULT TAB_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
3096 {
3097   if (!wParam)
3098     return 0;
3099   return WVR_ALIGNTOP;
3100 }
3101
3102 static inline LRESULT
3103 TAB_SetItemExtra (TAB_INFO *infoPtr, INT cbInfo)
3104 {
3105   if (!infoPtr || cbInfo <= 0)
3106     return FALSE;
3107
3108   if (infoPtr->uNumItem)
3109   {
3110     /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
3111     return FALSE;
3112   }
3113     
3114   infoPtr->cbInfo = cbInfo;
3115   return TRUE;
3116 }
3117
3118 static LRESULT WINAPI
3119 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3120 {
3121     TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3122
3123     TRACE("hwnd=%p msg=%x wParam=%lx lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3124     if (!infoPtr && (uMsg != WM_CREATE))
3125       return DefWindowProcW (hwnd, uMsg, wParam, lParam);
3126
3127     switch (uMsg)
3128     {
3129     case TCM_GETIMAGELIST:
3130       return TAB_GetImageList (infoPtr);
3131
3132     case TCM_SETIMAGELIST:
3133       return TAB_SetImageList (infoPtr, (HIMAGELIST)lParam);
3134
3135     case TCM_GETITEMCOUNT:
3136       return TAB_GetItemCount (infoPtr);
3137
3138     case TCM_GETITEMA:
3139     case TCM_GETITEMW:
3140       return TAB_GetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_GETITEMW);
3141
3142     case TCM_SETITEMA:
3143     case TCM_SETITEMW:
3144       return TAB_SetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_SETITEMW);
3145
3146     case TCM_DELETEITEM:
3147       return TAB_DeleteItem (infoPtr, (INT)wParam);
3148
3149     case TCM_DELETEALLITEMS:
3150      return TAB_DeleteAllItems (infoPtr);
3151
3152     case TCM_GETITEMRECT:
3153      return TAB_GetItemRect (infoPtr, wParam, lParam);
3154
3155     case TCM_GETCURSEL:
3156       return TAB_GetCurSel (infoPtr);
3157
3158     case TCM_HITTEST:
3159       return TAB_HitTest (infoPtr, (LPTCHITTESTINFO)lParam);
3160
3161     case TCM_SETCURSEL:
3162       return TAB_SetCurSel (infoPtr, (INT)wParam);
3163
3164     case TCM_INSERTITEMA:
3165     case TCM_INSERTITEMW:
3166       return TAB_InsertItemT (infoPtr, wParam, lParam, uMsg == TCM_INSERTITEMW);
3167
3168     case TCM_SETITEMEXTRA:
3169       return TAB_SetItemExtra (infoPtr, (int)wParam);
3170
3171     case TCM_ADJUSTRECT:
3172       return TAB_AdjustRect (infoPtr, (BOOL)wParam, (LPRECT)lParam);
3173
3174     case TCM_SETITEMSIZE:
3175       return TAB_SetItemSize (infoPtr, lParam);
3176
3177     case TCM_REMOVEIMAGE:
3178       FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3179       return 0;
3180
3181     case TCM_SETPADDING:
3182       return TAB_SetPadding (infoPtr, lParam);
3183
3184     case TCM_GETROWCOUNT:
3185       return TAB_GetRowCount(infoPtr);
3186
3187     case TCM_GETUNICODEFORMAT:
3188       return TAB_GetUnicodeFormat (infoPtr);
3189
3190     case TCM_SETUNICODEFORMAT:
3191       return TAB_SetUnicodeFormat (infoPtr, (BOOL)wParam);
3192
3193     case TCM_HIGHLIGHTITEM:
3194       return TAB_HighlightItem (infoPtr, (INT)wParam, (BOOL)LOWORD(lParam));
3195
3196     case TCM_GETTOOLTIPS:
3197       return TAB_GetToolTips (infoPtr);
3198
3199     case TCM_SETTOOLTIPS:
3200       return TAB_SetToolTips (infoPtr, (HWND)wParam);
3201
3202     case TCM_GETCURFOCUS:
3203       return TAB_GetCurFocus (infoPtr);
3204
3205     case TCM_SETCURFOCUS:
3206       return TAB_SetCurFocus (infoPtr, (INT)wParam);
3207
3208     case TCM_SETMINTABWIDTH:
3209       return TAB_SetMinTabWidth(infoPtr, (INT)lParam);
3210
3211     case TCM_DESELECTALL:
3212       FIXME("Unimplemented msg TCM_DESELECTALL\n");
3213       return 0;
3214
3215     case TCM_GETEXTENDEDSTYLE:
3216       FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3217       return 0;
3218
3219     case TCM_SETEXTENDEDSTYLE:
3220       FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3221       return 0;
3222
3223     case WM_GETFONT:
3224       return TAB_GetFont (infoPtr);
3225
3226     case WM_SETFONT:
3227       return TAB_SetFont (infoPtr, (HFONT)wParam);
3228
3229     case WM_CREATE:
3230       return TAB_Create (hwnd, wParam, lParam);
3231
3232     case WM_NCDESTROY:
3233       return TAB_Destroy (infoPtr);
3234
3235     case WM_GETDLGCODE:
3236       return DLGC_WANTARROWS | DLGC_WANTCHARS;
3237
3238     case WM_LBUTTONDOWN:
3239       return TAB_LButtonDown (infoPtr, wParam, lParam);
3240
3241     case WM_LBUTTONUP:
3242       return TAB_LButtonUp (infoPtr);
3243
3244     case WM_NOTIFY:
3245       return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3246
3247     case WM_RBUTTONDOWN:
3248       return TAB_RButtonDown (infoPtr);
3249
3250     case WM_MOUSEMOVE:
3251       return TAB_MouseMove (infoPtr, wParam, lParam);
3252
3253     case WM_PRINTCLIENT:
3254     case WM_PAINT:
3255       return TAB_Paint (infoPtr, (HDC)wParam);
3256
3257     case WM_SIZE:
3258       return TAB_Size (infoPtr);
3259
3260     case WM_SETREDRAW:
3261       return TAB_SetRedraw (infoPtr, (BOOL)wParam);
3262
3263     case WM_HSCROLL:
3264       return TAB_OnHScroll(infoPtr, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3265
3266     case WM_STYLECHANGED:
3267       TAB_SetItemBounds (infoPtr);
3268       InvalidateRect(hwnd, NULL, TRUE);
3269       return 0;
3270
3271     case WM_SYSCOLORCHANGE:
3272       COMCTL32_RefreshSysColors();
3273       return 0;
3274
3275     case WM_THEMECHANGED:
3276       return theme_changed (infoPtr);
3277
3278     case WM_KILLFOCUS:
3279     case WM_SETFOCUS:
3280       TAB_FocusChanging(infoPtr);
3281       break;   /* Don't disturb normal focus behavior */
3282
3283     case WM_KEYUP:
3284       return TAB_KeyUp(infoPtr, wParam);
3285     case WM_NCHITTEST:
3286       return TAB_NCHitTest(infoPtr, lParam);
3287
3288     case WM_NCCALCSIZE:
3289       return TAB_NCCalcSize(hwnd, wParam, lParam);
3290
3291     default:
3292       if (uMsg >= WM_USER && uMsg < WM_APP)
3293         WARN("unknown msg %04x wp=%08lx lp=%08lx\n",
3294              uMsg, wParam, lParam);
3295       break;
3296     }
3297     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
3298 }
3299
3300
3301 void
3302 TAB_Register (void)
3303 {
3304   WNDCLASSW wndClass;
3305
3306   ZeroMemory (&wndClass, sizeof(WNDCLASSW));
3307   wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3308   wndClass.lpfnWndProc   = TAB_WindowProc;
3309   wndClass.cbClsExtra    = 0;
3310   wndClass.cbWndExtra    = sizeof(TAB_INFO *);
3311   wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3312   wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
3313   wndClass.lpszClassName = WC_TABCONTROLW;
3314
3315   RegisterClassW (&wndClass);
3316 }
3317
3318
3319 void
3320 TAB_Unregister (void)
3321 {
3322     UnregisterClassW (WC_TABCONTROLW, NULL);
3323 }