4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
7 * Copyright 2003 Vitaliy Margolen
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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.
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.
43 * TCS_EX_FLATSEPARATORS
58 * TCM_GETEXTENDEDSTYLE
59 * TCM_SETEXTENDEDSTYLE
78 #include "wine/debug.h"
81 WINE_DEFAULT_DEBUG_CHANNEL(tab);
89 RECT rect; /* bounding rectangle of the item relative to the
90 * leftmost item (the leftmost item, 0, would have a
91 * "left" member of 0 in this rectangle)
93 * additionally the top member holds the row number
94 * and bottom is unused and should be 0 */
95 BYTE extra[1]; /* Space for caller supplied info, variable size */
98 /* The size of a tab item depends on how much extra data is requested */
99 #define TAB_ITEM_SIZE(infoPtr) (sizeof(TAB_ITEM) - sizeof(BYTE) + infoPtr->cbInfo)
103 HWND hwnd; /* Tab control window */
104 HWND hwndNotify; /* notification window (parent) */
105 UINT uNumItem; /* number of tab items */
106 UINT uNumRows; /* number of tab rows */
107 INT tabHeight; /* height of the tab row */
108 INT tabWidth; /* width of tabs */
109 INT tabMinWidth; /* minimum width of items */
110 USHORT uHItemPadding; /* amount of horizontal padding, in pixels */
111 USHORT uVItemPadding; /* amount of vertical padding, in pixels */
112 USHORT uHItemPadding_s; /* Set amount of horizontal padding, in pixels */
113 USHORT uVItemPadding_s; /* Set amount of vertical padding, in pixels */
114 HFONT hFont; /* handle to the current font */
115 HCURSOR hcurArrow; /* handle to the current cursor */
116 HIMAGELIST himl; /* handle to an image list (may be 0) */
117 HWND hwndToolTip; /* handle to tab's tooltip */
118 INT leftmostVisible; /* Used for scrolling, this member contains
119 * the index of the first visible item */
120 INT iSelected; /* the currently selected item */
121 INT iHotTracked; /* the highlighted item under the mouse */
122 INT uFocus; /* item which has the focus */
123 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
124 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
125 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
126 * the size of the control */
127 BOOL fHeightSet; /* was the height of the tabs explicitly set? */
128 BOOL bUnicode; /* Unicode control? */
129 HWND hwndUpDown; /* Updown control used for scrolling */
130 INT cbInfo; /* Number of bytes of caller supplied info per tab */
133 /******************************************************************************
134 * Positioning constants
136 #define SELECTED_TAB_OFFSET 2
137 #define ROUND_CORNER_SIZE 2
138 #define DISPLAY_AREA_PADDINGX 2
139 #define DISPLAY_AREA_PADDINGY 2
140 #define CONTROL_BORDER_SIZEX 2
141 #define CONTROL_BORDER_SIZEY 2
142 #define BUTTON_SPACINGX 3
143 #define BUTTON_SPACINGY 3
144 #define FLAT_BTN_SPACINGX 8
145 #define DEFAULT_TAB_WIDTH 96
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)))
152 /******************************************************************************
153 * Hot-tracking timer constants
155 #define TAB_HOTTRACK_TIMER 1
156 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
158 static const WCHAR themeClass[] = { 'T','a','b',0 };
160 /******************************************************************************
163 static void TAB_InvalidateTabArea(TAB_INFO *);
164 static void TAB_EnsureSelectionVisible(TAB_INFO *);
165 static void TAB_DrawItemInterior(TAB_INFO *, HDC, INT, RECT*);
168 TAB_SendSimpleNotify (const TAB_INFO *infoPtr, UINT code)
172 nmhdr.hwndFrom = infoPtr->hwnd;
173 nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID);
176 return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
177 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
181 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
182 WPARAM wParam, LPARAM lParam)
190 msg.time = GetMessageTime ();
191 msg.pt.x = LOWORD(GetMessagePos ());
192 msg.pt.y = HIWORD(GetMessagePos ());
194 SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
198 TAB_DumpItemExternalT(TCITEMW *pti, UINT iItem, BOOL isW)
201 TRACE("external tab %d, mask=0x%08x, dwState=0x%08lx, dwStateMask=0x%08lx, cchTextMax=0x%08x\n",
202 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
203 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
204 iItem, pti->iImage, pti->lParam, isW ? debugstr_w(pti->pszText) : debugstr_a((LPSTR)pti->pszText));
209 TAB_DumpItemInternal(TAB_INFO *infoPtr, UINT iItem)
214 ti = TAB_GetItem(infoPtr, iItem);
215 TRACE("tab %d, mask=0x%08x, dwState=0x%08lx, pszText=%s, iImage=%d\n",
216 iItem, ti->mask, ti->dwState, debugstr_w(ti->pszText),
218 TRACE("tab %d, rect.left=%ld, rect.top(row)=%ld\n",
219 iItem, ti->rect.left, ti->rect.top);
224 * the index of the selected tab, or -1 if no tab is selected. */
225 static inline LRESULT TAB_GetCurSel (const TAB_INFO *infoPtr)
227 return infoPtr->iSelected;
231 * the index of the tab item that has the focus
233 * we have not to return negative value
235 * test for windows */
236 static inline LRESULT
237 TAB_GetCurFocus (const TAB_INFO *infoPtr)
239 if (infoPtr->uFocus<0)
241 FIXME("we have not to return negative value");
244 return infoPtr->uFocus;
247 static inline LRESULT TAB_GetToolTips (const TAB_INFO *infoPtr)
249 if (infoPtr == NULL) return 0;
250 return (LRESULT)infoPtr->hwndToolTip;
253 static inline LRESULT TAB_SetCurSel (TAB_INFO *infoPtr, INT iItem)
257 if (iItem >= 0 && iItem < infoPtr->uNumItem) {
258 prevItem=infoPtr->iSelected;
259 infoPtr->iSelected=iItem;
260 TAB_EnsureSelectionVisible(infoPtr);
261 TAB_InvalidateTabArea(infoPtr);
266 static LRESULT TAB_SetCurFocus (TAB_INFO *infoPtr, INT iItem)
268 if (iItem < 0 || iItem >= infoPtr->uNumItem) return 0;
270 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS) {
271 FIXME("Should set input focus\n");
273 int oldFocus = infoPtr->uFocus;
274 if (infoPtr->iSelected != iItem || oldFocus == -1 ) {
275 infoPtr->uFocus = iItem;
276 if (oldFocus != -1) {
277 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING)) {
278 infoPtr->iSelected = iItem;
279 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
282 infoPtr->iSelected = iItem;
283 TAB_EnsureSelectionVisible(infoPtr);
284 TAB_InvalidateTabArea(infoPtr);
291 static inline LRESULT
292 TAB_SetToolTips (TAB_INFO *infoPtr, HWND hwndToolTip)
295 infoPtr->hwndToolTip = hwndToolTip;
299 static inline LRESULT
300 TAB_SetPadding (TAB_INFO *infoPtr, LPARAM lParam)
304 infoPtr->uHItemPadding_s=LOWORD(lParam);
305 infoPtr->uVItemPadding_s=HIWORD(lParam);
310 /******************************************************************************
311 * TAB_InternalGetItemRect
313 * This method will calculate the rectangle representing a given tab item in
314 * client coordinates. This method takes scrolling into account.
316 * This method returns TRUE if the item is visible in the window and FALSE
317 * if it is completely outside the client area.
319 static BOOL TAB_InternalGetItemRect(
320 const TAB_INFO* infoPtr,
325 RECT tmpItemRect,clientRect;
326 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
328 /* Perform a sanity check and a trivial visibility check. */
329 if ( (infoPtr->uNumItem <= 0) ||
330 (itemIndex >= infoPtr->uNumItem) ||
331 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
335 * Avoid special cases in this procedure by assigning the "out"
336 * parameters if the caller didn't supply them
338 if (itemRect == NULL)
339 itemRect = &tmpItemRect;
341 /* Retrieve the unmodified item rect. */
342 *itemRect = TAB_GetItem(infoPtr,itemIndex)->rect;
344 /* calculate the times bottom and top based on the row */
345 GetClientRect(infoPtr->hwnd, &clientRect);
347 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
349 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * infoPtr->tabHeight -
350 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
351 itemRect->left = itemRect->right - infoPtr->tabHeight;
353 else if (lStyle & TCS_VERTICAL)
355 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * infoPtr->tabHeight +
356 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
357 itemRect->right = itemRect->left + infoPtr->tabHeight;
359 else if (lStyle & TCS_BOTTOM)
361 itemRect->bottom = clientRect.bottom - itemRect->top * infoPtr->tabHeight -
362 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
363 itemRect->top = itemRect->bottom - infoPtr->tabHeight;
365 else /* not TCS_BOTTOM and not TCS_VERTICAL */
367 itemRect->top = clientRect.top + itemRect->top * infoPtr->tabHeight +
368 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
369 itemRect->bottom = itemRect->top + infoPtr->tabHeight;
373 * "scroll" it to make sure the item at the very left of the
374 * tab control is the leftmost visible tab.
376 if(lStyle & TCS_VERTICAL)
380 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.top);
383 * Move the rectangle so the first item is slightly offset from
384 * the bottom of the tab control.
388 SELECTED_TAB_OFFSET);
393 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.left,
397 * Move the rectangle so the first item is slightly offset from
398 * the left of the tab control.
404 TRACE("item %d tab h=%d, rect=(%ld,%ld)-(%ld,%ld)\n",
405 itemIndex, infoPtr->tabHeight,
406 itemRect->left, itemRect->top, itemRect->right, itemRect->bottom);
408 /* Now, calculate the position of the item as if it were selected. */
409 if (selectedRect!=NULL)
411 CopyRect(selectedRect, itemRect);
413 /* The rectangle of a selected item is a bit wider. */
414 if(lStyle & TCS_VERTICAL)
415 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
417 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
419 /* If it also a bit higher. */
420 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
422 selectedRect->left -= 2; /* the border is thicker on the right */
423 selectedRect->right += SELECTED_TAB_OFFSET;
425 else if (lStyle & TCS_VERTICAL)
427 selectedRect->left -= SELECTED_TAB_OFFSET;
428 selectedRect->right += 1;
430 else if (lStyle & TCS_BOTTOM)
432 selectedRect->bottom += SELECTED_TAB_OFFSET;
434 else /* not TCS_BOTTOM and not TCS_VERTICAL */
436 selectedRect->top -= SELECTED_TAB_OFFSET;
437 selectedRect->bottom -= 1;
441 /* Check for visibility */
442 if (lStyle & TCS_VERTICAL)
443 return (itemRect->top < clientRect.bottom) && (itemRect->bottom > clientRect.top);
445 return (itemRect->left < clientRect.right) && (itemRect->right > clientRect.left);
449 TAB_GetItemRect(TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
451 return TAB_InternalGetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam, (LPRECT)NULL);
454 /******************************************************************************
457 * This method is called to handle keyboard input
459 static LRESULT TAB_KeyUp(TAB_INFO* infoPtr, WPARAM keyCode)
466 newItem = infoPtr->uFocus - 1;
469 newItem = infoPtr->uFocus + 1;
474 * If we changed to a valid item, change the selection
477 newItem < infoPtr->uNumItem &&
478 infoPtr->uFocus != newItem)
480 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
482 infoPtr->iSelected = newItem;
483 infoPtr->uFocus = newItem;
484 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
486 TAB_EnsureSelectionVisible(infoPtr);
487 TAB_InvalidateTabArea(infoPtr);
494 /******************************************************************************
497 * This method is called whenever the focus goes in or out of this control
498 * it is used to update the visual state of the control.
500 static void TAB_FocusChanging(const TAB_INFO *infoPtr)
506 * Get the rectangle for the item.
508 isVisible = TAB_InternalGetItemRect(infoPtr,
514 * If the rectangle is not completely invisible, invalidate that
515 * portion of the window.
519 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
520 selectedRect.left,selectedRect.top,
521 selectedRect.right,selectedRect.bottom);
522 InvalidateRect(infoPtr->hwnd, &selectedRect, TRUE);
526 static INT TAB_InternalHitTest (
535 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
537 TAB_InternalGetItemRect(infoPtr, iCount, &rect, NULL);
539 if (PtInRect(&rect, pt))
541 *flags = TCHT_ONITEM;
546 *flags = TCHT_NOWHERE;
550 static inline LRESULT
551 TAB_HitTest (TAB_INFO *infoPtr, LPTCHITTESTINFO lptest)
553 return TAB_InternalHitTest (infoPtr, lptest->pt, &lptest->flags);
556 /******************************************************************************
559 * Napster v2b5 has a tab control for its main navigation which has a client
560 * area that covers the whole area of the dialog pages.
561 * That's why it receives all msgs for that area and the underlying dialog ctrls
563 * So I decided that we should handle WM_NCHITTEST here and return
564 * HTTRANSPARENT if we don't hit the tab control buttons.
565 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
566 * doesn't do it that way. Maybe depends on tab control styles ?
568 static inline LRESULT
569 TAB_NCHitTest (TAB_INFO *infoPtr, LPARAM lParam)
574 pt.x = LOWORD(lParam);
575 pt.y = HIWORD(lParam);
576 ScreenToClient(infoPtr->hwnd, &pt);
578 if (TAB_InternalHitTest(infoPtr, pt, &dummyflag) == -1)
579 return HTTRANSPARENT;
585 TAB_LButtonDown (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
590 if (infoPtr->hwndToolTip)
591 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
592 WM_LBUTTONDOWN, wParam, lParam);
594 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
595 SetFocus (infoPtr->hwnd);
598 if (infoPtr->hwndToolTip)
599 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
600 WM_LBUTTONDOWN, wParam, lParam);
602 pt.x = (INT)LOWORD(lParam);
603 pt.y = (INT)HIWORD(lParam);
605 newItem = TAB_InternalHitTest (infoPtr, pt, &dummy);
607 TRACE("On Tab, item %d\n", newItem);
609 if (newItem != -1 && infoPtr->iSelected != newItem)
611 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
613 infoPtr->iSelected = newItem;
614 infoPtr->uFocus = newItem;
615 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
617 TAB_EnsureSelectionVisible(infoPtr);
619 TAB_InvalidateTabArea(infoPtr);
625 static inline LRESULT
626 TAB_LButtonUp (const TAB_INFO *infoPtr)
628 TAB_SendSimpleNotify(infoPtr, NM_CLICK);
633 static inline LRESULT
634 TAB_RButtonDown (const TAB_INFO *infoPtr)
636 TAB_SendSimpleNotify(infoPtr, NM_RCLICK);
640 /******************************************************************************
641 * TAB_DrawLoneItemInterior
643 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
644 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
645 * up the device context and font. This routine does the same setup but
646 * only calls TAB_DrawItemInterior for the single specified item.
649 TAB_DrawLoneItemInterior(TAB_INFO* infoPtr, int iItem)
651 HDC hdc = GetDC(infoPtr->hwnd);
654 /* Clip UpDown control to not draw over it */
655 if (infoPtr->needsScrolling)
657 GetWindowRect(infoPtr->hwnd, &rC);
658 GetWindowRect(infoPtr->hwndUpDown, &r);
659 ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top);
661 TAB_DrawItemInterior(infoPtr, hdc, iItem, NULL);
662 ReleaseDC(infoPtr->hwnd, hdc);
665 /* update a tab after hottracking - invalidate it or just redraw the interior,
666 * based on whether theming is used or not */
667 static inline void hottrack_refresh (TAB_INFO* infoPtr, int tabIndex)
669 if (tabIndex == -1) return;
671 if (GetWindowTheme (infoPtr->hwnd))
674 TAB_InternalGetItemRect(infoPtr, tabIndex, &rect, NULL);
675 InvalidateRect (infoPtr->hwnd, &rect, FALSE);
678 TAB_DrawLoneItemInterior(infoPtr, tabIndex);
681 /******************************************************************************
682 * TAB_HotTrackTimerProc
684 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
685 * timer is setup so we can check if the mouse is moved out of our window.
686 * (We don't get an event when the mouse leaves, the mouse-move events just
687 * stop being delivered to our window and just start being delivered to
688 * another window.) This function is called when the timer triggers so
689 * we can check if the mouse has left our window. If so, we un-highlight
690 * the hot-tracked tab.
693 TAB_HotTrackTimerProc
695 HWND hwnd, /* handle of window for timer messages */
696 UINT uMsg, /* WM_TIMER message */
697 UINT idEvent, /* timer identifier */
698 DWORD dwTime /* current system time */
701 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
703 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
708 ** If we can't get the cursor position, or if the cursor is outside our
709 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
710 ** "outside" even if it is within our bounding rect if another window
711 ** overlaps. Note also that the case where the cursor stayed within our
712 ** window but has moved off the hot-tracked tab will be handled by the
713 ** WM_MOUSEMOVE event.
715 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
717 /* Redraw iHotTracked to look normal */
718 INT iRedraw = infoPtr->iHotTracked;
719 infoPtr->iHotTracked = -1;
720 hottrack_refresh (infoPtr, iRedraw);
722 /* Kill this timer */
723 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
728 /******************************************************************************
731 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
732 * should be highlighted. This function determines which tab in a tab control,
733 * if any, is under the mouse and records that information. The caller may
734 * supply output parameters to receive the item number of the tab item which
735 * was highlighted but isn't any longer and of the tab item which is now
736 * highlighted but wasn't previously. The caller can use this information to
737 * selectively redraw those tab items.
739 * If the caller has a mouse position, it can supply it through the pos
740 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
741 * supplies NULL and this function determines the current mouse position
749 int* out_redrawLeave,
756 if (out_redrawLeave != NULL)
757 *out_redrawLeave = -1;
758 if (out_redrawEnter != NULL)
759 *out_redrawEnter = -1;
761 if ((GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_HOTTRACK)
762 || GetWindowTheme (infoPtr->hwnd))
770 ScreenToClient(infoPtr->hwnd, &pt);
778 item = TAB_InternalHitTest(infoPtr, pt, &flags);
781 if (item != infoPtr->iHotTracked)
783 if (infoPtr->iHotTracked >= 0)
785 /* Mark currently hot-tracked to be redrawn to look normal */
786 if (out_redrawLeave != NULL)
787 *out_redrawLeave = infoPtr->iHotTracked;
791 /* Kill timer which forces recheck of mouse pos */
792 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
797 /* Start timer so we recheck mouse pos */
798 UINT timerID = SetTimer
802 TAB_HOTTRACK_TIMER_INTERVAL,
803 TAB_HotTrackTimerProc
807 return; /* Hot tracking not available */
810 infoPtr->iHotTracked = item;
814 /* Mark new hot-tracked to be redrawn to look highlighted */
815 if (out_redrawEnter != NULL)
816 *out_redrawEnter = item;
821 /******************************************************************************
824 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
827 TAB_MouseMove (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
832 if (infoPtr->hwndToolTip)
833 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
834 WM_LBUTTONDOWN, wParam, lParam);
836 /* Determine which tab to highlight. Redraw tabs which change highlight
838 TAB_RecalcHotTrack(infoPtr, &lParam, &redrawLeave, &redrawEnter);
840 hottrack_refresh (infoPtr, redrawLeave);
841 hottrack_refresh (infoPtr, redrawEnter);
846 /******************************************************************************
849 * Calculates the tab control's display area given the window rectangle or
850 * the window rectangle given the requested display rectangle.
852 static LRESULT TAB_AdjustRect(
857 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
858 LONG *iRightBottom, *iLeftTop;
860 TRACE ("hwnd=%p fLarger=%d (%ld,%ld)-(%ld,%ld)\n", infoPtr->hwnd, fLarger, prc->left, prc->top, prc->right, prc->bottom);
862 if(lStyle & TCS_VERTICAL)
864 iRightBottom = &(prc->right);
865 iLeftTop = &(prc->left);
869 iRightBottom = &(prc->bottom);
870 iLeftTop = &(prc->top);
873 if (fLarger) /* Go from display rectangle */
875 /* Add the height of the tabs. */
876 if (lStyle & TCS_BOTTOM)
877 *iRightBottom += infoPtr->tabHeight * infoPtr->uNumRows;
879 *iLeftTop -= infoPtr->tabHeight * infoPtr->uNumRows +
880 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
882 /* Inflate the rectangle for the padding */
883 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
885 /* Inflate for the border */
886 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEY);
888 else /* Go from window rectangle. */
890 /* Deflate the rectangle for the border */
891 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEY);
893 /* Deflate the rectangle for the padding */
894 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
896 /* Remove the height of the tabs. */
897 if (lStyle & TCS_BOTTOM)
898 *iRightBottom -= infoPtr->tabHeight * infoPtr->uNumRows;
900 *iLeftTop += (infoPtr->tabHeight) * infoPtr->uNumRows +
901 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
907 /******************************************************************************
910 * This method will handle the notification from the scroll control and
911 * perform the scrolling operation on the tab control.
913 static LRESULT TAB_OnHScroll(
919 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
921 if(nPos < infoPtr->leftmostVisible)
922 infoPtr->leftmostVisible--;
924 infoPtr->leftmostVisible++;
926 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
927 TAB_InvalidateTabArea(infoPtr);
928 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
929 MAKELONG(infoPtr->leftmostVisible, 0));
935 /******************************************************************************
938 * This method will check the current scrolling state and make sure the
939 * scrolling control is displayed (or not).
941 static void TAB_SetupScrolling(
944 const RECT* clientRect)
946 static const WCHAR msctls_updown32W[] = { 'm','s','c','t','l','s','_','u','p','d','o','w','n','3','2',0 };
947 static const WCHAR emptyW[] = { 0 };
949 DWORD lStyle = GetWindowLongW(hwnd, GWL_STYLE);
951 if (infoPtr->needsScrolling)
957 * Calculate the position of the scroll control.
959 if(lStyle & TCS_VERTICAL)
961 controlPos.right = clientRect->right;
962 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
964 if (lStyle & TCS_BOTTOM)
966 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
967 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
971 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
972 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
977 controlPos.right = clientRect->right;
978 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
980 if (lStyle & TCS_BOTTOM)
982 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
983 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
987 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
988 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
993 * If we don't have a scroll control yet, we want to create one.
994 * If we have one, we want to make sure it's positioned properly.
996 if (infoPtr->hwndUpDown==0)
998 infoPtr->hwndUpDown = CreateWindowW(msctls_updown32W, emptyW,
999 WS_VISIBLE | WS_CHILD | UDS_HORZ,
1000 controlPos.left, controlPos.top,
1001 controlPos.right - controlPos.left,
1002 controlPos.bottom - controlPos.top,
1003 hwnd, NULL, NULL, NULL);
1007 SetWindowPos(infoPtr->hwndUpDown,
1009 controlPos.left, controlPos.top,
1010 controlPos.right - controlPos.left,
1011 controlPos.bottom - controlPos.top,
1012 SWP_SHOWWINDOW | SWP_NOZORDER);
1015 /* Now calculate upper limit of the updown control range.
1016 * We do this by calculating how many tabs will be offscreen when the
1017 * last tab is visible.
1019 if(infoPtr->uNumItem)
1021 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
1022 maxRange = infoPtr->uNumItem;
1023 tabwidth = TAB_GetItem(infoPtr, infoPtr->uNumItem - 1)->rect.right;
1025 for(; maxRange > 0; maxRange--)
1027 if(tabwidth - TAB_GetItem(infoPtr,maxRange - 1)->rect.left > vsize)
1031 if(maxRange == infoPtr->uNumItem)
1037 /* If we once had a scroll control... hide it */
1038 if (infoPtr->hwndUpDown!=0)
1039 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1041 if (infoPtr->hwndUpDown)
1042 SendMessageW(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1045 /******************************************************************************
1048 * This method will calculate the position rectangles of all the items in the
1049 * control. The rectangle calculated starts at 0 for the first item in the
1050 * list and ignores scrolling and selection.
1051 * It also uses the current font to determine the height of the tab row and
1052 * it checks if all the tabs fit in the client area of the window. If they
1053 * don't, a scrolling control is added.
1055 static void TAB_SetItemBounds (TAB_INFO *infoPtr)
1057 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1058 TEXTMETRICW fontMetrics;
1061 INT curItemRowCount;
1062 HFONT hFont, hOldFont;
1072 * We need to get text information so we need a DC and we need to select
1075 hdc = GetDC(infoPtr->hwnd);
1077 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1078 hOldFont = SelectObject (hdc, hFont);
1081 * We will base the rectangle calculations on the client rectangle
1084 GetClientRect(infoPtr->hwnd, &clientRect);
1086 /* if TCS_VERTICAL then swap the height and width so this code places the
1087 tabs along the top of the rectangle and we can just rotate them after
1088 rather than duplicate all of the below code */
1089 if(lStyle & TCS_VERTICAL)
1091 iTemp = clientRect.bottom;
1092 clientRect.bottom = clientRect.right;
1093 clientRect.right = iTemp;
1096 /* Now use hPadding and vPadding */
1097 infoPtr->uHItemPadding = infoPtr->uHItemPadding_s;
1098 infoPtr->uVItemPadding = infoPtr->uVItemPadding_s;
1100 /* The leftmost item will be "0" aligned */
1102 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1104 if (!(infoPtr->fHeightSet))
1107 int icon_height = 0;
1109 /* Use the current font to determine the height of a tab. */
1110 GetTextMetricsW(hdc, &fontMetrics);
1112 /* Get the icon height */
1114 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1116 /* Take the highest between font or icon */
1117 if (fontMetrics.tmHeight > icon_height)
1118 item_height = fontMetrics.tmHeight + 2;
1120 item_height = icon_height;
1123 * Make sure there is enough space for the letters + icon + growing the
1124 * selected item + extra space for the selected item.
1126 infoPtr->tabHeight = item_height +
1127 ((lStyle & TCS_BUTTONS) ? 2 : 1) *
1128 infoPtr->uVItemPadding;
1130 TRACE("tabH=%d, tmH=%ld, iconh=%d\n",
1131 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1134 TRACE("client right=%ld\n", clientRect.right);
1136 /* Get the icon width */
1139 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1141 if (lStyle & TCS_FIXEDWIDTH)
1144 /* Add padding if icon is present */
1145 icon_width += infoPtr->uHItemPadding;
1148 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1150 TAB_ITEM *curr = TAB_GetItem(infoPtr, curItem);
1152 /* Set the leftmost position of the tab. */
1153 curr->rect.left = curItemLeftPos;
1155 if ((lStyle & TCS_FIXEDWIDTH) || !curr->pszText)
1157 curr->rect.right = curr->rect.left +
1158 max(infoPtr->tabWidth, icon_width);
1164 /* Calculate how wide the tab is depending on the text it contains */
1165 GetTextExtentPoint32W(hdc, curr->pszText,
1166 lstrlenW(curr->pszText), &size);
1168 curr->rect.right = curr->rect.left + size.cx + icon_width +
1169 num * infoPtr->uHItemPadding;
1170 TRACE("for <%s>, l,r=%ld,%ld, num=%d\n",
1171 debugstr_w(curr->pszText), curr->rect.left, curr->rect.right, num);
1175 * Check if this is a multiline tab control and if so
1176 * check to see if we should wrap the tabs
1178 * Wrap all these tabs. We will arrange them evenly later.
1182 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1184 (clientRect.right - CONTROL_BORDER_SIZEX - DISPLAY_AREA_PADDINGX)))
1186 curr->rect.right -= curr->rect.left;
1188 curr->rect.left = 0;
1190 TRACE("wrapping <%s>, l,r=%ld,%ld\n", debugstr_w(curr->pszText),
1191 curr->rect.left, curr->rect.right);
1194 curr->rect.bottom = 0;
1195 curr->rect.top = curItemRowCount - 1;
1197 TRACE("TextSize: %li\n", size.cx);
1198 TRACE("Rect: T %li, L %li, B %li, R %li\n", curr->rect.top,
1199 curr->rect.left, curr->rect.bottom, curr->rect.right);
1202 * The leftmost position of the next item is the rightmost position
1205 if (lStyle & TCS_BUTTONS)
1207 curItemLeftPos = curr->rect.right + BUTTON_SPACINGX;
1208 if (lStyle & TCS_FLATBUTTONS)
1209 curItemLeftPos += FLAT_BTN_SPACINGX;
1212 curItemLeftPos = curr->rect.right;
1215 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1218 * Check if we need a scrolling control.
1220 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1223 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1224 if(!infoPtr->needsScrolling)
1225 infoPtr->leftmostVisible = 0;
1230 * No scrolling in Multiline or Vertical styles.
1232 infoPtr->needsScrolling = FALSE;
1233 infoPtr->leftmostVisible = 0;
1235 TAB_SetupScrolling(infoPtr->hwnd, infoPtr, &clientRect);
1237 /* Set the number of rows */
1238 infoPtr->uNumRows = curItemRowCount;
1240 /* Arrange all tabs evenly if style says so */
1241 if (!(lStyle & TCS_RAGGEDRIGHT) && ((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->uNumItem > 0))
1243 INT tabPerRow,remTab,iRow;
1248 * Ok windows tries to even out the rows. place the same
1249 * number of tabs in each row. So lets give that a shot
1252 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1253 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1255 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1256 iItm<infoPtr->uNumItem;
1259 /* normalize the current rect */
1260 TAB_ITEM *curr = TAB_GetItem(infoPtr, iItm);
1262 /* shift the item to the left side of the clientRect */
1263 curr->rect.right -= curr->rect.left;
1264 curr->rect.left = 0;
1266 TRACE("r=%ld, cl=%d, cl.r=%ld, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1267 curr->rect.right, curItemLeftPos, clientRect.right,
1268 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1270 /* if we have reached the maximum number of tabs on this row */
1271 /* move to the next row, reset our current item left position and */
1272 /* the count of items on this row */
1274 if (lStyle & TCS_VERTICAL) {
1275 /* Vert: Add the remaining tabs in the *last* remainder rows */
1276 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1282 /* Horz: Add the remaining tabs in the *first* remainder rows */
1283 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1290 /* shift the item to the right to place it as the next item in this row */
1291 curr->rect.left += curItemLeftPos;
1292 curr->rect.right += curItemLeftPos;
1293 curr->rect.top = iRow;
1294 if (lStyle & TCS_BUTTONS)
1296 curItemLeftPos = curr->rect.right + 1;
1297 if (lStyle & TCS_FLATBUTTONS)
1298 curItemLeftPos += FLAT_BTN_SPACINGX;
1301 curItemLeftPos = curr->rect.right;
1303 TRACE("arranging <%s>, l,r=%ld,%ld, row=%ld\n",
1304 debugstr_w(curr->pszText), curr->rect.left,
1305 curr->rect.right, curr->rect.top);
1312 INT widthDiff, iIndexStart=0, iIndexEnd=0;
1316 while(iIndexStart < infoPtr->uNumItem)
1318 TAB_ITEM *start = TAB_GetItem(infoPtr, iIndexStart);
1321 * find the index of the row
1323 /* find the first item on the next row */
1324 for (iIndexEnd=iIndexStart;
1325 (iIndexEnd < infoPtr->uNumItem) &&
1326 (TAB_GetItem(infoPtr, iIndexEnd)->rect.top ==
1329 /* intentionally blank */;
1332 * we need to justify these tabs so they fill the whole given
1336 /* find the amount of space remaining on this row */
1337 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1338 TAB_GetItem(infoPtr, iIndexEnd - 1)->rect.right;
1340 /* iCount is the number of tab items on this row */
1341 iCount = iIndexEnd - iIndexStart;
1345 remainder = widthDiff % iCount;
1346 widthDiff = widthDiff / iCount;
1347 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1348 for (iIndex=iIndexStart, iCount=0; iIndex < iIndexEnd; iIndex++, iCount++)
1350 TAB_ITEM *item = TAB_GetItem(infoPtr, iIndex);
1352 item->rect.left += iCount * widthDiff;
1353 item->rect.right += (iCount + 1) * widthDiff;
1355 TRACE("adjusting 1 <%s>, l,r=%ld,%ld\n",
1356 debugstr_w(item->pszText),
1357 item->rect.left, item->rect.right);
1360 TAB_GetItem(infoPtr, iIndex - 1)->rect.right += remainder;
1362 else /* we have only one item on this row, make it take up the entire row */
1364 start->rect.left = clientRect.left;
1365 start->rect.right = clientRect.right - 4;
1367 TRACE("adjusting 2 <%s>, l,r=%ld,%ld\n",
1368 debugstr_w(start->pszText),
1369 start->rect.left, start->rect.right);
1374 iIndexStart = iIndexEnd;
1379 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1380 if(lStyle & TCS_VERTICAL)
1383 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1385 rcItem = &TAB_GetItem(infoPtr, iIndex)->rect;
1387 rcOriginal = *rcItem;
1389 /* this is rotating the items by 90 degrees clockwise around the center of the control */
1390 rcItem->top = (rcOriginal.left - clientRect.left);
1391 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1392 rcItem->left = rcOriginal.top;
1393 rcItem->right = rcOriginal.bottom;
1397 TAB_EnsureSelectionVisible(infoPtr);
1398 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
1401 SelectObject (hdc, hOldFont);
1402 ReleaseDC (infoPtr->hwnd, hdc);
1407 TAB_EraseTabInterior
1415 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1416 HBRUSH hbr = CreateSolidBrush (comctl32_color.clrBtnFace);
1417 BOOL deleteBrush = TRUE;
1418 RECT rTemp = *drawRect;
1420 InflateRect(&rTemp, -2, -2);
1421 if (lStyle & TCS_BUTTONS)
1423 if (iItem == infoPtr->iSelected)
1425 /* Background color */
1426 if (!(lStyle & TCS_OWNERDRAWFIXED))
1429 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1431 SetTextColor(hdc, comctl32_color.clr3dFace);
1432 SetBkColor(hdc, comctl32_color.clr3dHilight);
1434 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1435 * we better use 0x55aa bitmap brush to make scrollbar's background
1436 * look different from the window background.
1438 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1439 hbr = COMCTL32_hPattern55AABrush;
1441 deleteBrush = FALSE;
1443 FillRect(hdc, &rTemp, hbr);
1445 else /* ! selected */
1447 if (lStyle & TCS_FLATBUTTONS)
1449 FillRect(hdc, drawRect, hbr);
1450 if (iItem == infoPtr->iHotTracked)
1451 DrawEdge(hdc, drawRect, EDGE_RAISED, BF_SOFT|BF_RECT);
1454 FillRect(hdc, &rTemp, hbr);
1458 else /* !TCS_BUTTONS */
1460 if (!GetWindowTheme (infoPtr->hwnd))
1461 FillRect(hdc, &rTemp, hbr);
1465 if (deleteBrush) DeleteObject(hbr);
1468 /******************************************************************************
1469 * TAB_DrawItemInterior
1471 * This method is used to draw the interior (text and icon) of a single tab
1472 * into the tab control.
1475 TAB_DrawItemInterior
1483 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1492 /* if (drawRect == NULL) */
1499 * Get the rectangle for the item.
1501 isVisible = TAB_InternalGetItemRect(infoPtr, iItem, &itemRect, &selectedRect);
1506 * Make sure drawRect points to something valid; simplifies code.
1508 drawRect = &localRect;
1511 * This logic copied from the part of TAB_DrawItem which draws
1512 * the tab background. It's important to keep it in sync. I
1513 * would have liked to avoid code duplication, but couldn't figure
1514 * out how without making spaghetti of TAB_DrawItem.
1516 if (iItem == infoPtr->iSelected)
1517 *drawRect = selectedRect;
1519 *drawRect = itemRect;
1521 if (lStyle & TCS_BUTTONS)
1523 if (iItem == infoPtr->iSelected)
1525 drawRect->left += 4;
1527 drawRect->right -= 4;
1528 drawRect->bottom -= 1;
1532 drawRect->left += 2;
1534 drawRect->right -= 2;
1535 drawRect->bottom -= 2;
1540 if ((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1542 if (iItem != infoPtr->iSelected)
1544 drawRect->left += 2;
1546 drawRect->bottom -= 2;
1549 else if (lStyle & TCS_VERTICAL)
1551 if (iItem == infoPtr->iSelected)
1553 drawRect->right += 1;
1558 drawRect->right -= 2;
1559 drawRect->bottom -= 2;
1562 else if (lStyle & TCS_BOTTOM)
1564 if (iItem == infoPtr->iSelected)
1570 InflateRect(drawRect, -2, -2);
1571 drawRect->bottom += 2;
1576 if (iItem == infoPtr->iSelected)
1578 drawRect->bottom += 3;
1582 drawRect->bottom -= 2;
1583 InflateRect(drawRect, -2, 0);
1588 TRACE("drawRect=(%ld,%ld)-(%ld,%ld)\n",
1589 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom);
1591 /* Clear interior */
1592 TAB_EraseTabInterior (infoPtr, hdc, iItem, drawRect);
1594 /* Draw the focus rectangle */
1595 if (!(lStyle & TCS_FOCUSNEVER) &&
1596 (GetFocus() == infoPtr->hwnd) &&
1597 (iItem == infoPtr->uFocus) )
1599 RECT rFocus = *drawRect;
1600 InflateRect(&rFocus, -3, -3);
1601 if (lStyle & TCS_BOTTOM && !(lStyle & TCS_VERTICAL))
1603 if (lStyle & TCS_BUTTONS)
1609 DrawFocusRect(hdc, &rFocus);
1615 htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1616 holdPen = SelectObject(hdc, htextPen);
1617 hOldFont = SelectObject(hdc, infoPtr->hFont);
1620 * Setup for text output
1622 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1623 if (!GetWindowTheme (infoPtr->hwnd))
1624 SetTextColor(hdc, (((iItem == infoPtr->iHotTracked) && !(lStyle & TCS_FLATBUTTONS)) |
1625 (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED)) ?
1626 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1629 * if owner draw, tell the owner to draw
1631 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(infoPtr->hwnd))
1637 drawRect->right -= 1;
1638 if ( iItem == infoPtr->iSelected )
1640 drawRect->right -= 1;
1641 drawRect->left += 1;
1645 * get the control id
1647 id = (UINT)GetWindowLongPtrW( infoPtr->hwnd, GWLP_ID );
1650 * put together the DRAWITEMSTRUCT
1652 dis.CtlType = ODT_TAB;
1655 dis.itemAction = ODA_DRAWENTIRE;
1657 if ( iItem == infoPtr->iSelected )
1658 dis.itemState |= ODS_SELECTED;
1659 if (infoPtr->uFocus == iItem)
1660 dis.itemState |= ODS_FOCUS;
1661 dis.hwndItem = infoPtr->hwnd;
1663 CopyRect(&dis.rcItem,drawRect);
1664 dis.itemData = (ULONG_PTR)TAB_GetItem(infoPtr, iItem)->extra;
1667 * send the draw message
1669 SendMessageW( infoPtr->hwndNotify, WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1673 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
1677 /* used to center the icon and text in the tab */
1679 INT center_offset_h, center_offset_v;
1681 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1682 rcImage = *drawRect;
1686 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1688 /* get the rectangle that the text fits in */
1691 DrawTextW(hdc, item->pszText, -1, &rcText, DT_CALCRECT);
1694 * If not owner draw, then do the drawing ourselves.
1698 if (infoPtr->himl && (item->mask & TCIF_IMAGE))
1703 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1705 if(lStyle & TCS_VERTICAL)
1707 center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1708 center_offset_v = ((drawRect->right - drawRect->left) - (cx + infoPtr->uVItemPadding)) / 2;
1712 center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1713 center_offset_v = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uVItemPadding)) / 2;
1716 if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1717 center_offset_h = infoPtr->uHItemPadding;
1719 if (center_offset_h < 2)
1720 center_offset_h = 2;
1722 if (center_offset_v < 0)
1723 center_offset_v = 0;
1725 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1726 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1727 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1728 (rcText.right-rcText.left));
1730 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1732 rcImage.top = drawRect->top + center_offset_h;
1733 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1734 /* right side of the tab, but the image still uses the left as its x position */
1735 /* this keeps the image always drawn off of the same side of the tab */
1736 rcImage.left = drawRect->right - cx - center_offset_v;
1737 drawRect->top += cy + infoPtr->uHItemPadding;
1739 else if(lStyle & TCS_VERTICAL)
1741 rcImage.top = drawRect->bottom - cy - center_offset_h;
1742 rcImage.left = drawRect->left + center_offset_v;
1743 drawRect->bottom -= cy + infoPtr->uHItemPadding;
1745 else /* normal style, whether TCS_BOTTOM or not */
1747 rcImage.left = drawRect->left + center_offset_h;
1748 rcImage.top = drawRect->top + center_offset_v;
1749 drawRect->left += cx + infoPtr->uHItemPadding;
1752 TRACE("drawing image=%d, left=%ld, top=%ld\n",
1753 item->iImage, rcImage.left, rcImage.top-1);
1765 /* Now position text */
1766 if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT)
1767 center_offset_h = infoPtr->uHItemPadding;
1769 if(lStyle & TCS_VERTICAL)
1770 center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1772 center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1774 if(lStyle & TCS_VERTICAL)
1776 if(lStyle & TCS_BOTTOM)
1777 drawRect->top+=center_offset_h;
1779 drawRect->bottom-=center_offset_h;
1781 center_offset_v = ((drawRect->right - drawRect->left) - (rcText.bottom - rcText.top)) / 2;
1785 drawRect->left += center_offset_h;
1786 center_offset_v = ((drawRect->bottom - drawRect->top) - (rcText.bottom - rcText.top)) / 2;
1789 /* if an item is selected, the text is shifted up instead of down */
1790 if (iItem == infoPtr->iSelected)
1791 center_offset_v -= infoPtr->uVItemPadding / 2;
1793 center_offset_v += infoPtr->uVItemPadding / 2;
1795 if (center_offset_v < 0)
1796 center_offset_v = 0;
1798 if(lStyle & TCS_VERTICAL)
1799 drawRect->left += center_offset_v;
1801 drawRect->top += center_offset_v;
1804 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1806 static const WCHAR ArialW[] = { 'A','r','i','a','l',0 };
1809 INT nEscapement = 900;
1810 INT nOrientation = 900;
1812 if(lStyle & TCS_BOTTOM)
1815 nOrientation = -900;
1818 /* to get a font with the escapement and orientation we are looking for, we need to */
1819 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1820 if (!GetObjectW((infoPtr->hFont) ?
1821 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1822 sizeof(LOGFONTW),&logfont))
1826 lstrcpyW(logfont.lfFaceName, ArialW);
1827 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1829 logfont.lfWeight = FW_NORMAL;
1830 logfont.lfItalic = 0;
1831 logfont.lfUnderline = 0;
1832 logfont.lfStrikeOut = 0;
1835 logfont.lfEscapement = nEscapement;
1836 logfont.lfOrientation = nOrientation;
1837 hFont = CreateFontIndirectW(&logfont);
1838 SelectObject(hdc, hFont);
1843 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1844 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1848 lstrlenW(item->pszText),
1852 DeleteObject(hFont);
1856 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1857 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1858 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1859 (rcText.right-rcText.left));
1866 lstrlenW(item->pszText),
1868 DT_LEFT | DT_SINGLELINE
1873 *drawRect = rcTemp; /* restore drawRect */
1879 SelectObject(hdc, hOldFont);
1880 SetBkMode(hdc, oldBkMode);
1881 SelectObject(hdc, holdPen);
1882 DeleteObject( htextPen );
1885 /******************************************************************************
1888 * This method is used to draw a single tab into the tab control.
1890 static void TAB_DrawItem(
1895 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1899 RECT r, fillRect, r1;
1902 COLORREF bkgnd, corner;
1906 * Get the rectangle for the item.
1908 isVisible = TAB_InternalGetItemRect(infoPtr,
1917 /* Clip UpDown control to not draw over it */
1918 if (infoPtr->needsScrolling)
1920 GetWindowRect(infoPtr->hwnd, &rC);
1921 GetWindowRect(infoPtr->hwndUpDown, &rUD);
1922 ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
1925 /* If you need to see what the control is doing,
1926 * then override these variables. They will change what
1927 * fill colors are used for filling the tabs, and the
1928 * corners when drawing the edge.
1930 bkgnd = comctl32_color.clrBtnFace;
1931 corner = comctl32_color.clrBtnFace;
1933 if (lStyle & TCS_BUTTONS)
1935 /* Get item rectangle */
1938 /* Separators between flat buttons */
1939 if (lStyle & TCS_FLATBUTTONS)
1942 r1.right += (FLAT_BTN_SPACINGX -2);
1943 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1946 if (iItem == infoPtr->iSelected)
1948 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1950 OffsetRect(&r, 1, 1);
1952 else /* ! selected */
1954 if (!(lStyle & TCS_FLATBUTTONS))
1955 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1958 else /* !TCS_BUTTONS */
1960 /* We draw a rectangle of different sizes depending on the selection
1962 if (iItem == infoPtr->iSelected) {
1964 GetClientRect (infoPtr->hwnd, &rect);
1965 clRight = rect.right;
1966 clBottom = rect.bottom;
1973 * Erase the background. (Delay it but setup rectangle.)
1974 * This is necessary when drawing the selected item since it is larger
1975 * than the others, it might overlap with stuff already drawn by the
1980 /* Draw themed tabs - but only if they are at the top.
1981 * Windows draws even side or bottom tabs themed, with wacky results.
1982 * However, since in Wine apps may get themed that did not opt in via
1983 * a manifest avoid theming when we know the result will be wrong */
1984 if ((theme = GetWindowTheme (infoPtr->hwnd))
1985 && ((lStyle & (TCS_VERTICAL | TCS_BOTTOM)) == 0))
1987 const static int partIds[8] = {
1990 TABP_TABITEMLEFTEDGE,
1991 TABP_TABITEMRIGHTEDGE,
1992 TABP_TABITEMBOTHEDGE,
1995 TABP_TOPTABITEMLEFTEDGE,
1996 TABP_TOPTABITEMRIGHTEDGE,
1997 TABP_TOPTABITEMBOTHEDGE,
2000 int stateId = TIS_NORMAL;
2002 /* selected and unselected tabs have different parts */
2003 if (iItem == infoPtr->iSelected)
2005 /* The part also differs on the position of a tab on a line.
2006 * "Visually" determining the position works well enough. */
2007 if(selectedRect.left == 0)
2009 if(selectedRect.right == clRight)
2012 if (iItem == infoPtr->iSelected)
2013 stateId = TIS_SELECTED;
2014 else if (iItem == infoPtr->iHotTracked)
2016 else if (iItem == infoPtr->uFocus)
2017 stateId = TIS_FOCUSED;
2019 /* Adjust rectangle for bottommost row */
2020 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2023 DrawThemeBackground (theme, hdc, partIds[partIndex], stateId, &r, NULL);
2024 GetThemeBackgroundContentRect (theme, hdc, partIds[partIndex], stateId, &r, &r);
2026 else if(lStyle & TCS_VERTICAL)
2028 /* These are for adjusting the drawing of a Selected tab */
2029 /* The initial values are for the normal case of non-Selected */
2030 int ZZ = 1; /* Do not strech if selected */
2031 if (iItem == infoPtr->iSelected) {
2034 /* if leftmost draw the line longer */
2035 if(selectedRect.top == 0)
2036 fillRect.top += CONTROL_BORDER_SIZEY;
2037 /* if rightmost draw the line longer */
2038 if(selectedRect.bottom == clBottom)
2039 fillRect.bottom -= CONTROL_BORDER_SIZEY;
2042 if (lStyle & TCS_BOTTOM)
2044 /* Adjust both rectangles to match native */
2047 TRACE("<right> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2049 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2050 r.left,r.top,r.right,r.bottom);
2052 /* Clear interior */
2053 SetBkColor(hdc, bkgnd);
2054 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2056 /* Draw rectangular edge around tab */
2057 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
2059 /* Now erase the top corner and draw diagonal edge */
2060 SetBkColor(hdc, corner);
2061 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2064 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2065 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2067 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2069 /* Now erase the bottom corner and draw diagonal edge */
2070 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2071 r1.bottom = r.bottom;
2073 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2074 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2076 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2078 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
2082 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
2088 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2090 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2091 r.left,r.top,r.right,r.bottom);
2093 /* Clear interior */
2094 SetBkColor(hdc, bkgnd);
2095 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2097 /* Draw rectangular edge around tab */
2098 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2100 /* Now erase the top corner and draw diagonal edge */
2101 SetBkColor(hdc, corner);
2104 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2105 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2106 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2108 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2110 /* Now erase the bottom corner and draw diagonal edge */
2112 r1.bottom = r.bottom;
2113 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2114 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2115 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2117 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2120 else /* ! TCS_VERTICAL */
2122 /* These are for adjusting the drawing of a Selected tab */
2123 /* The initial values are for the normal case of non-Selected */
2124 if (iItem == infoPtr->iSelected) {
2125 /* if leftmost draw the line longer */
2126 if(selectedRect.left == 0)
2127 fillRect.left += CONTROL_BORDER_SIZEX;
2128 /* if rightmost draw the line longer */
2129 if(selectedRect.right == clRight)
2130 fillRect.right -= CONTROL_BORDER_SIZEX;
2133 if (lStyle & TCS_BOTTOM)
2135 /* Adjust both rectangles for topmost row */
2136 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2142 TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2144 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2145 r.left,r.top,r.right,r.bottom);
2147 /* Clear interior */
2148 SetBkColor(hdc, bkgnd);
2149 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2151 /* Draw rectangular edge around tab */
2152 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2154 /* Now erase the righthand corner and draw diagonal edge */
2155 SetBkColor(hdc, corner);
2156 r1.left = r.right - ROUND_CORNER_SIZE;
2157 r1.bottom = r.bottom;
2159 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2160 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2162 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2164 /* Now erase the lefthand corner and draw diagonal edge */
2166 r1.bottom = r.bottom;
2167 r1.right = r1.left + ROUND_CORNER_SIZE;
2168 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2169 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2171 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2173 if (iItem == infoPtr->iSelected)
2177 if (selectedRect.left == 0)
2182 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2189 /* Adjust both rectangles for bottommost row */
2190 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2192 fillRect.bottom += 3;
2196 TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2198 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2199 r.left,r.top,r.right,r.bottom);
2201 /* Clear interior */
2202 SetBkColor(hdc, bkgnd);
2203 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2205 /* Draw rectangular edge around tab */
2206 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2208 /* Now erase the righthand corner and draw diagonal edge */
2209 SetBkColor(hdc, corner);
2210 r1.left = r.right - ROUND_CORNER_SIZE;
2213 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2214 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2216 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2218 /* Now erase the lefthand corner and draw diagonal edge */
2221 r1.right = r1.left + ROUND_CORNER_SIZE;
2222 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2223 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2225 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2230 TAB_DumpItemInternal(infoPtr, iItem);
2232 /* This modifies r to be the text rectangle. */
2233 TAB_DrawItemInterior(infoPtr, hdc, iItem, &r);
2237 /******************************************************************************
2240 * This method is used to draw the raised border around the tab control
2243 static void TAB_DrawBorder (TAB_INFO *infoPtr, HDC hdc)
2246 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2247 HTHEME theme = GetWindowTheme (infoPtr->hwnd);
2249 GetClientRect (infoPtr->hwnd, &rect);
2252 * Adjust for the style
2255 if (infoPtr->uNumItem)
2257 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2258 rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2259 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2260 rect.right -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2261 else if(lStyle & TCS_VERTICAL)
2262 rect.left += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2263 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2264 rect.top += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2267 TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2268 rect.left, rect.top, rect.right, rect.bottom);
2271 DrawThemeBackground (theme, hdc, TABP_PANE, 0, &rect, NULL);
2273 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2276 /******************************************************************************
2279 * This method repaints the tab control..
2281 static void TAB_Refresh (TAB_INFO *infoPtr, HDC hdc)
2286 if (!infoPtr->DoRedraw)
2289 hOldFont = SelectObject (hdc, infoPtr->hFont);
2291 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS)
2293 for (i = 0; i < infoPtr->uNumItem; i++)
2294 TAB_DrawItem (infoPtr, hdc, i);
2298 /* Draw all the non selected item first */
2299 for (i = 0; i < infoPtr->uNumItem; i++)
2301 if (i != infoPtr->iSelected)
2302 TAB_DrawItem (infoPtr, hdc, i);
2305 /* Now, draw the border, draw it before the selected item
2306 * since the selected item overwrites part of the border. */
2307 TAB_DrawBorder (infoPtr, hdc);
2309 /* Then, draw the selected item */
2310 TAB_DrawItem (infoPtr, hdc, infoPtr->iSelected);
2312 /* If we haven't set the current focus yet, set it now.
2313 * Only happens when we first paint the tab controls */
2314 if (infoPtr->uFocus == -1)
2315 TAB_SetCurFocus(infoPtr, infoPtr->iSelected);
2318 SelectObject (hdc, hOldFont);
2321 static inline DWORD TAB_GetRowCount (const TAB_INFO *infoPtr)
2323 return infoPtr->uNumRows;
2326 static inline LRESULT TAB_SetRedraw (TAB_INFO *infoPtr, BOOL doRedraw)
2328 infoPtr->DoRedraw = doRedraw;
2332 /******************************************************************************
2333 * TAB_EnsureSelectionVisible
2335 * This method will make sure that the current selection is completely
2336 * visible by scrolling until it is.
2338 static void TAB_EnsureSelectionVisible(
2341 INT iSelected = infoPtr->iSelected;
2342 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2343 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2345 /* set the items row to the bottommost row or topmost row depending on
2347 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2349 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2353 if(lStyle & TCS_VERTICAL)
2354 newselected = selected->rect.left;
2356 newselected = selected->rect.top;
2358 /* the target row is always (number of rows - 1)
2359 as row 0 is furthest from the clientRect */
2360 iTargetRow = infoPtr->uNumRows - 1;
2362 if (newselected != iTargetRow)
2365 if(lStyle & TCS_VERTICAL)
2367 for (i=0; i < infoPtr->uNumItem; i++)
2369 /* move everything in the row of the selected item to the iTargetRow */
2370 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2372 if (item->rect.left == newselected )
2373 item->rect.left = iTargetRow;
2376 if (item->rect.left > newselected)
2383 for (i=0; i < infoPtr->uNumItem; i++)
2385 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2387 if (item->rect.top == newselected )
2388 item->rect.top = iTargetRow;
2391 if (item->rect.top > newselected)
2396 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2401 * Do the trivial cases first.
2403 if ( (!infoPtr->needsScrolling) ||
2404 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2407 if (infoPtr->leftmostVisible >= iSelected)
2409 infoPtr->leftmostVisible = iSelected;
2413 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2418 /* Calculate the part of the client area that is visible */
2419 GetClientRect(infoPtr->hwnd, &r);
2422 GetClientRect(infoPtr->hwndUpDown, &r);
2425 if ((selected->rect.right -
2426 selected->rect.left) >= width )
2428 /* Special case: width of selected item is greater than visible
2431 infoPtr->leftmostVisible = iSelected;
2435 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2437 if ((selected->rect.right - TAB_GetItem(infoPtr, i)->rect.left) < width)
2440 infoPtr->leftmostVisible = i;
2444 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2445 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2447 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2448 MAKELONG(infoPtr->leftmostVisible, 0));
2451 /******************************************************************************
2452 * TAB_InvalidateTabArea
2454 * This method will invalidate the portion of the control that contains the
2455 * tabs. It is called when the state of the control changes and needs
2458 static void TAB_InvalidateTabArea(TAB_INFO* infoPtr)
2460 RECT clientRect, rInvalidate, rAdjClient;
2461 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2462 INT lastRow = infoPtr->uNumRows - 1;
2465 if (lastRow < 0) return;
2467 GetClientRect(infoPtr->hwnd, &clientRect);
2468 rInvalidate = clientRect;
2469 rAdjClient = clientRect;
2471 TAB_AdjustRect(infoPtr, 0, &rAdjClient);
2473 TAB_InternalGetItemRect(infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2474 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2476 rInvalidate.left = rAdjClient.right;
2477 if (infoPtr->uNumRows == 1)
2478 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2480 else if(lStyle & TCS_VERTICAL)
2482 rInvalidate.right = rAdjClient.left;
2483 if (infoPtr->uNumRows == 1)
2484 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2486 else if (lStyle & TCS_BOTTOM)
2488 rInvalidate.top = rAdjClient.bottom;
2489 if (infoPtr->uNumRows == 1)
2490 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2494 rInvalidate.bottom = rAdjClient.top;
2495 if (infoPtr->uNumRows == 1)
2496 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2499 /* Punch out the updown control */
2500 if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2502 GetClientRect(infoPtr->hwndUpDown, &r);
2503 if (rInvalidate.right > clientRect.right - r.left)
2504 rInvalidate.right = rInvalidate.right - (r.right - r.left);
2506 rInvalidate.right = clientRect.right - r.left;
2509 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2510 rInvalidate.left, rInvalidate.top,
2511 rInvalidate.right, rInvalidate.bottom);
2513 InvalidateRect(infoPtr->hwnd, &rInvalidate, TRUE);
2516 static inline LRESULT TAB_Paint (TAB_INFO *infoPtr, HDC hdcPaint)
2525 hdc = BeginPaint (infoPtr->hwnd, &ps);
2526 TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2528 ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2531 TAB_Refresh (infoPtr, hdc);
2534 EndPaint (infoPtr->hwnd, &ps);
2540 TAB_InsertItemT (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2547 GetClientRect (infoPtr->hwnd, &rect);
2548 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", infoPtr->hwnd,
2549 rect.top, rect.left, rect.bottom, rect.right);
2551 pti = (TCITEMW *)lParam;
2552 iItem = (INT)wParam;
2554 if (iItem < 0) return -1;
2555 if (iItem > infoPtr->uNumItem)
2556 iItem = infoPtr->uNumItem;
2558 TAB_DumpItemExternalT(pti, iItem, bUnicode);
2561 if (infoPtr->uNumItem == 0) {
2562 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2563 infoPtr->uNumItem++;
2564 infoPtr->iSelected = 0;
2567 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2569 infoPtr->uNumItem++;
2570 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2572 /* pre insert copy */
2574 memcpy (infoPtr->items, oldItems,
2575 iItem * TAB_ITEM_SIZE(infoPtr));
2578 /* post insert copy */
2579 if (iItem < infoPtr->uNumItem - 1) {
2580 memcpy (TAB_GetItem(infoPtr, iItem + 1),
2581 oldItems + iItem * TAB_ITEM_SIZE(infoPtr),
2582 (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2586 if (iItem <= infoPtr->iSelected)
2587 infoPtr->iSelected++;
2592 item = TAB_GetItem(infoPtr, iItem);
2594 item->mask = pti->mask;
2595 item->pszText = NULL;
2597 if (pti->mask & TCIF_TEXT)
2600 Str_SetPtrW (&item->pszText, pti->pszText);
2602 Str_SetPtrAtoW (&item->pszText, (LPSTR)pti->pszText);
2605 if (pti->mask & TCIF_IMAGE)
2606 item->iImage = pti->iImage;
2610 if (pti->mask & TCIF_PARAM)
2611 memcpy(item->extra, &pti->lParam, infoPtr->cbInfo);
2613 memset(item->extra, 0, infoPtr->cbInfo);
2615 TAB_SetItemBounds(infoPtr);
2616 if (infoPtr->uNumItem > 1)
2617 TAB_InvalidateTabArea(infoPtr);
2619 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2621 TRACE("[%p]: added item %d %s\n",
2622 infoPtr->hwnd, iItem, debugstr_w(item->pszText));
2628 TAB_SetItemSize (TAB_INFO *infoPtr, LPARAM lParam)
2630 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2632 BOOL bNeedPaint = FALSE;
2634 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2636 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2637 if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2639 infoPtr->tabWidth = max((INT)LOWORD(lParam), infoPtr->tabMinWidth);
2643 if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2645 if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2646 infoPtr->tabHeight = (INT)HIWORD(lParam);
2650 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2651 HIWORD(lResult), LOWORD(lResult),
2652 infoPtr->tabHeight, infoPtr->tabWidth);
2656 TAB_SetItemBounds(infoPtr);
2657 RedrawWindow(infoPtr->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2663 static inline LRESULT TAB_SetMinTabWidth (TAB_INFO *infoPtr, INT cx)
2667 TRACE("(%p,%d)\n", infoPtr, cx);
2670 oldcx = infoPtr->tabMinWidth;
2671 infoPtr->tabMinWidth = (cx==-1)?DEFAULT_TAB_WIDTH:cx;
2677 static inline LRESULT
2678 TAB_HighlightItem (TAB_INFO *infoPtr, INT iItem, BOOL fHighlight)
2682 TRACE("(%p,%d,%s)\n", infoPtr, iItem, fHighlight ? "true" : "false");
2684 if (!infoPtr || iItem < 0 || iItem >= infoPtr->uNumItem)
2687 lpState = &TAB_GetItem(infoPtr, iItem)->dwState;
2690 *lpState |= TCIS_HIGHLIGHTED;
2692 *lpState &= ~TCIS_HIGHLIGHTED;
2698 TAB_SetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2702 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2704 if (iItem < 0 || iItem >= infoPtr->uNumItem)
2707 TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2709 wineItem = TAB_GetItem(infoPtr, iItem);
2711 if (tabItem->mask & TCIF_IMAGE)
2712 wineItem->iImage = tabItem->iImage;
2714 if (tabItem->mask & TCIF_PARAM)
2715 memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2717 if (tabItem->mask & TCIF_RTLREADING)
2718 FIXME("TCIF_RTLREADING\n");
2720 if (tabItem->mask & TCIF_STATE)
2721 wineItem->dwState = tabItem->dwState;
2723 if (tabItem->mask & TCIF_TEXT)
2725 if (wineItem->pszText)
2727 Free(wineItem->pszText);
2728 wineItem->pszText = NULL;
2731 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2733 Str_SetPtrAtoW(&wineItem->pszText, (LPSTR)tabItem->pszText);
2736 /* Update and repaint tabs */
2737 TAB_SetItemBounds(infoPtr);
2738 TAB_InvalidateTabArea(infoPtr);
2743 static inline LRESULT TAB_GetItemCount (const TAB_INFO *infoPtr)
2745 return infoPtr->uNumItem;
2750 TAB_GetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2754 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2756 if (iItem < 0 || iItem >= infoPtr->uNumItem)
2759 wineItem = TAB_GetItem(infoPtr, iItem);
2761 if (tabItem->mask & TCIF_IMAGE)
2762 tabItem->iImage = wineItem->iImage;
2764 if (tabItem->mask & TCIF_PARAM)
2765 memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2767 if (tabItem->mask & TCIF_RTLREADING)
2768 FIXME("TCIF_RTLREADING\n");
2770 if (tabItem->mask & TCIF_STATE)
2771 tabItem->dwState = wineItem->dwState;
2773 if (tabItem->mask & TCIF_TEXT)
2776 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2778 Str_GetPtrWtoA (wineItem->pszText, (LPSTR)tabItem->pszText, tabItem->cchTextMax);
2781 TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2787 static LRESULT TAB_DeleteItem (TAB_INFO *infoPtr, INT iItem)
2789 BOOL bResult = FALSE;
2791 TRACE("(%p, %d)\n", infoPtr, iItem);
2793 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2795 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
2796 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2798 TAB_InvalidateTabArea(infoPtr);
2800 if ((item->mask & TCIF_TEXT) && item->pszText)
2801 Free(item->pszText);
2803 infoPtr->uNumItem--;
2805 if (!infoPtr->uNumItem)
2807 infoPtr->items = NULL;
2808 if (infoPtr->iHotTracked >= 0)
2810 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
2811 infoPtr->iHotTracked = -1;
2816 infoPtr->items = Alloc(TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2819 memcpy(infoPtr->items, oldItems, iItem * TAB_ITEM_SIZE(infoPtr));
2821 if (iItem < infoPtr->uNumItem)
2822 memcpy(TAB_GetItem(infoPtr, iItem),
2823 oldItems + (iItem + 1) * TAB_ITEM_SIZE(infoPtr),
2824 (infoPtr->uNumItem - iItem) * TAB_ITEM_SIZE(infoPtr));
2826 if (iItem <= infoPtr->iHotTracked)
2828 /* When tabs move left/up, the hot track item may change */
2829 FIXME("Recalc hot track");
2834 /* Readjust the selected index */
2835 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2836 infoPtr->iSelected--;
2838 if (iItem < infoPtr->iSelected)
2839 infoPtr->iSelected--;
2841 if (infoPtr->uNumItem == 0)
2842 infoPtr->iSelected = -1;
2844 /* Reposition and repaint tabs */
2845 TAB_SetItemBounds(infoPtr);
2853 static inline LRESULT TAB_DeleteAllItems (TAB_INFO *infoPtr)
2855 TRACE("(%p)\n", infoPtr);
2856 while (infoPtr->uNumItem)
2857 TAB_DeleteItem (infoPtr, 0);
2862 static inline LRESULT TAB_GetFont (const TAB_INFO *infoPtr)
2864 TRACE("(%p) returning %p\n", infoPtr, infoPtr->hFont);
2865 return (LRESULT)infoPtr->hFont;
2868 static inline LRESULT TAB_SetFont (TAB_INFO *infoPtr, HFONT hNewFont)
2870 TRACE("(%p,%p)\n", infoPtr, hNewFont);
2872 infoPtr->hFont = hNewFont;
2874 TAB_SetItemBounds(infoPtr);
2876 TAB_InvalidateTabArea(infoPtr);
2882 static inline LRESULT TAB_GetImageList (const TAB_INFO *infoPtr)
2885 return (LRESULT)infoPtr->himl;
2888 static inline LRESULT TAB_SetImageList (TAB_INFO *infoPtr, HIMAGELIST himlNew)
2890 HIMAGELIST himlPrev = infoPtr->himl;
2892 infoPtr->himl = himlNew;
2893 return (LRESULT)himlPrev;
2896 static inline LRESULT TAB_GetUnicodeFormat (const TAB_INFO *infoPtr)
2898 return infoPtr->bUnicode;
2901 static inline LRESULT TAB_SetUnicodeFormat (TAB_INFO *infoPtr, BOOL bUnicode)
2903 BOOL bTemp = infoPtr->bUnicode;
2905 infoPtr->bUnicode = bUnicode;
2910 static inline LRESULT TAB_Size (TAB_INFO *infoPtr)
2912 /* I'm not really sure what the following code was meant to do.
2913 This is what it is doing:
2914 When WM_SIZE is sent with SIZE_RESTORED, the control
2915 gets positioned in the top left corner.
2919 UINT uPosFlags,cx,cy;
2923 parent = GetParent (hwnd);
2924 GetClientRect(parent, &parent_rect);
2927 if (GetWindowLongW(hwnd, GWL_STYLE) & CCS_NORESIZE)
2928 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2930 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2931 cx, cy, uPosFlags | SWP_NOZORDER);
2933 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2936 /* Recompute the size/position of the tabs. */
2937 TAB_SetItemBounds (infoPtr);
2939 /* Force a repaint of the control. */
2940 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2946 static LRESULT TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2949 TEXTMETRICW fontMetrics;
2954 infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
2956 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
2958 infoPtr->hwnd = hwnd;
2959 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
2960 infoPtr->uNumItem = 0;
2961 infoPtr->uNumRows = 0;
2962 infoPtr->uHItemPadding = 6;
2963 infoPtr->uVItemPadding = 3;
2964 infoPtr->uHItemPadding_s = 6;
2965 infoPtr->uVItemPadding_s = 3;
2968 infoPtr->hcurArrow = LoadCursorW (0, (LPWSTR)IDC_ARROW);
2969 infoPtr->iSelected = -1;
2970 infoPtr->iHotTracked = -1;
2971 infoPtr->uFocus = -1;
2972 infoPtr->hwndToolTip = 0;
2973 infoPtr->DoRedraw = TRUE;
2974 infoPtr->needsScrolling = FALSE;
2975 infoPtr->hwndUpDown = 0;
2976 infoPtr->leftmostVisible = 0;
2977 infoPtr->fHeightSet = FALSE;
2978 infoPtr->bUnicode = IsWindowUnicode (hwnd);
2979 infoPtr->cbInfo = sizeof(LPARAM);
2981 TRACE("Created tab control, hwnd [%p]\n", hwnd);
2983 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2984 if you don't specify it in CreateWindow. This is necessary in
2985 order for paint to work correctly. This follows windows behaviour. */
2986 dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
2987 SetWindowLongW(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2989 if (dwStyle & TCS_TOOLTIPS) {
2990 /* Create tooltip control */
2991 infoPtr->hwndToolTip =
2992 CreateWindowExW (0, TOOLTIPS_CLASSW, NULL, 0,
2993 CW_USEDEFAULT, CW_USEDEFAULT,
2994 CW_USEDEFAULT, CW_USEDEFAULT,
2997 /* Send NM_TOOLTIPSCREATED notification */
2998 if (infoPtr->hwndToolTip) {
2999 NMTOOLTIPSCREATED nmttc;
3001 nmttc.hdr.hwndFrom = hwnd;
3002 nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
3003 nmttc.hdr.code = NM_TOOLTIPSCREATED;
3004 nmttc.hwndToolTips = infoPtr->hwndToolTip;
3006 SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
3007 (WPARAM)GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc);
3011 OpenThemeData (infoPtr->hwnd, themeClass);
3014 * We need to get text information so we need a DC and we need to select
3018 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3020 /* Use the system font to determine the initial height of a tab. */
3021 GetTextMetricsW(hdc, &fontMetrics);
3024 * Make sure there is enough space for the letters + growing the
3025 * selected item + extra space for the selected item.
3027 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3028 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
3029 infoPtr->uVItemPadding;
3031 /* Initialize the width of a tab. */
3032 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
3033 infoPtr->tabMinWidth = 0;
3035 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3037 SelectObject (hdc, hOldFont);
3038 ReleaseDC(hwnd, hdc);
3044 TAB_Destroy (TAB_INFO *infoPtr)
3051 SetWindowLongPtrW(infoPtr->hwnd, 0, 0);
3053 if (infoPtr->items) {
3054 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3055 if (TAB_GetItem(infoPtr, iItem)->pszText)
3056 Free (TAB_GetItem(infoPtr, iItem)->pszText);
3058 Free (infoPtr->items);
3061 if (infoPtr->hwndToolTip)
3062 DestroyWindow (infoPtr->hwndToolTip);
3064 if (infoPtr->hwndUpDown)
3065 DestroyWindow(infoPtr->hwndUpDown);
3067 if (infoPtr->iHotTracked >= 0)
3068 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
3070 CloseThemeData (GetWindowTheme (infoPtr->hwnd));
3076 /* update theme after a WM_THEMECHANGED message */
3077 static LRESULT theme_changed (TAB_INFO* infoPtr)
3079 HTHEME theme = GetWindowTheme (infoPtr->hwnd);
3080 CloseThemeData (theme);
3081 OpenThemeData (infoPtr->hwnd, themeClass);
3085 static LRESULT TAB_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
3089 return WVR_ALIGNTOP;
3092 static inline LRESULT
3093 TAB_SetItemExtra (TAB_INFO *infoPtr, INT cbInfo)
3095 if (!infoPtr || cbInfo <= 0)
3098 if (infoPtr->uNumItem)
3100 /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
3104 infoPtr->cbInfo = cbInfo;
3108 static LRESULT WINAPI
3109 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3111 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3113 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3114 if (!infoPtr && (uMsg != WM_CREATE))
3115 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
3119 case TCM_GETIMAGELIST:
3120 return TAB_GetImageList (infoPtr);
3122 case TCM_SETIMAGELIST:
3123 return TAB_SetImageList (infoPtr, (HIMAGELIST)lParam);
3125 case TCM_GETITEMCOUNT:
3126 return TAB_GetItemCount (infoPtr);
3130 return TAB_GetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_GETITEMW);
3134 return TAB_SetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_SETITEMW);
3136 case TCM_DELETEITEM:
3137 return TAB_DeleteItem (infoPtr, (INT)wParam);
3139 case TCM_DELETEALLITEMS:
3140 return TAB_DeleteAllItems (infoPtr);
3142 case TCM_GETITEMRECT:
3143 return TAB_GetItemRect (infoPtr, wParam, lParam);
3146 return TAB_GetCurSel (infoPtr);
3149 return TAB_HitTest (infoPtr, (LPTCHITTESTINFO)lParam);
3152 return TAB_SetCurSel (infoPtr, (INT)wParam);
3154 case TCM_INSERTITEMA:
3155 case TCM_INSERTITEMW:
3156 return TAB_InsertItemT (infoPtr, wParam, lParam, uMsg == TCM_INSERTITEMW);
3158 case TCM_SETITEMEXTRA:
3159 return TAB_SetItemExtra (infoPtr, (int)wParam);
3161 case TCM_ADJUSTRECT:
3162 return TAB_AdjustRect (infoPtr, (BOOL)wParam, (LPRECT)lParam);
3164 case TCM_SETITEMSIZE:
3165 return TAB_SetItemSize (infoPtr, lParam);
3167 case TCM_REMOVEIMAGE:
3168 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3171 case TCM_SETPADDING:
3172 return TAB_SetPadding (infoPtr, lParam);
3174 case TCM_GETROWCOUNT:
3175 return TAB_GetRowCount(infoPtr);
3177 case TCM_GETUNICODEFORMAT:
3178 return TAB_GetUnicodeFormat (infoPtr);
3180 case TCM_SETUNICODEFORMAT:
3181 return TAB_SetUnicodeFormat (infoPtr, (BOOL)wParam);
3183 case TCM_HIGHLIGHTITEM:
3184 return TAB_HighlightItem (infoPtr, (INT)wParam, (BOOL)LOWORD(lParam));
3186 case TCM_GETTOOLTIPS:
3187 return TAB_GetToolTips (infoPtr);
3189 case TCM_SETTOOLTIPS:
3190 return TAB_SetToolTips (infoPtr, (HWND)wParam);
3192 case TCM_GETCURFOCUS:
3193 return TAB_GetCurFocus (infoPtr);
3195 case TCM_SETCURFOCUS:
3196 return TAB_SetCurFocus (infoPtr, (INT)wParam);
3198 case TCM_SETMINTABWIDTH:
3199 return TAB_SetMinTabWidth(infoPtr, (INT)lParam);
3201 case TCM_DESELECTALL:
3202 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3205 case TCM_GETEXTENDEDSTYLE:
3206 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3209 case TCM_SETEXTENDEDSTYLE:
3210 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3214 return TAB_GetFont (infoPtr);
3217 return TAB_SetFont (infoPtr, (HFONT)wParam);
3220 return TAB_Create (hwnd, wParam, lParam);
3223 return TAB_Destroy (infoPtr);
3226 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3228 case WM_LBUTTONDOWN:
3229 return TAB_LButtonDown (infoPtr, wParam, lParam);
3232 return TAB_LButtonUp (infoPtr);
3235 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3237 case WM_RBUTTONDOWN:
3238 return TAB_RButtonDown (infoPtr);
3241 return TAB_MouseMove (infoPtr, wParam, lParam);
3244 return TAB_Paint (infoPtr, (HDC)wParam);
3247 return TAB_Size (infoPtr);
3250 return TAB_SetRedraw (infoPtr, (BOOL)wParam);
3253 return TAB_OnHScroll(infoPtr, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3255 case WM_STYLECHANGED:
3256 TAB_SetItemBounds (infoPtr);
3257 InvalidateRect(hwnd, NULL, TRUE);
3260 case WM_SYSCOLORCHANGE:
3261 COMCTL32_RefreshSysColors();
3264 case WM_THEMECHANGED:
3265 return theme_changed (infoPtr);
3269 TAB_FocusChanging(infoPtr);
3270 break; /* Don't disturb normal focus behavior */
3273 return TAB_KeyUp(infoPtr, wParam);
3275 return TAB_NCHitTest(infoPtr, lParam);
3278 return TAB_NCCalcSize(hwnd, wParam, lParam);
3281 if (uMsg >= WM_USER && uMsg < WM_APP)
3282 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3283 uMsg, wParam, lParam);
3286 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
3295 ZeroMemory (&wndClass, sizeof(WNDCLASSW));
3296 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3297 wndClass.lpfnWndProc = TAB_WindowProc;
3298 wndClass.cbClsExtra = 0;
3299 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3300 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3301 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
3302 wndClass.lpszClassName = WC_TABCONTROLW;
3304 RegisterClassW (&wndClass);
3309 TAB_Unregister (void)
3311 UnregisterClassW (WC_TABCONTROLW, NULL);