~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/comctl32/tab.c

Version: ~ [ wine-1.1.38 ] ~ [ wine-1.1.37 ] ~ [ wine-1.1.36 ] ~ [ wine-1.1.35 ] ~ [ wine-1.1.34 ] ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.