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

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

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

~ [ 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.