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) || (lStyle & TCS_BUTTONS))
1624 SetTextColor(hdc, (((lStyle & TCS_HOTTRACK) && (iItem == infoPtr->iHotTracked)
1625 && !(lStyle & TCS_FLATBUTTONS))
1626 | (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED)) ?
1627 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1630 * if owner draw, tell the owner to draw
1632 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(infoPtr->hwnd))
1638 drawRect->right -= 1;
1639 if ( iItem == infoPtr->iSelected )
1641 drawRect->right -= 1;
1642 drawRect->left += 1;
1646 * get the control id
1648 id = (UINT)GetWindowLongPtrW( infoPtr->hwnd, GWLP_ID );
1651 * put together the DRAWITEMSTRUCT
1653 dis.CtlType = ODT_TAB;
1656 dis.itemAction = ODA_DRAWENTIRE;
1658 if ( iItem == infoPtr->iSelected )
1659 dis.itemState |= ODS_SELECTED;
1660 if (infoPtr->uFocus == iItem)
1661 dis.itemState |= ODS_FOCUS;
1662 dis.hwndItem = infoPtr->hwnd;
1664 CopyRect(&dis.rcItem,drawRect);
1665 dis.itemData = (ULONG_PTR)TAB_GetItem(infoPtr, iItem)->extra;
1668 * send the draw message
1670 SendMessageW( infoPtr->hwndNotify, WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1674 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
1678 /* used to center the icon and text in the tab */
1680 INT center_offset_h, center_offset_v;
1682 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1683 rcImage = *drawRect;
1687 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1689 /* get the rectangle that the text fits in */
1692 DrawTextW(hdc, item->pszText, -1, &rcText, DT_CALCRECT);
1695 * If not owner draw, then do the drawing ourselves.
1699 if (infoPtr->himl && (item->mask & TCIF_IMAGE))
1704 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1706 if(lStyle & TCS_VERTICAL)
1708 center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1709 center_offset_v = ((drawRect->right - drawRect->left) - (cx + infoPtr->uVItemPadding)) / 2;
1713 center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1714 center_offset_v = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uVItemPadding)) / 2;
1717 if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1718 center_offset_h = infoPtr->uHItemPadding;
1720 if (center_offset_h < 2)
1721 center_offset_h = 2;
1723 if (center_offset_v < 0)
1724 center_offset_v = 0;
1726 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1727 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1728 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1729 (rcText.right-rcText.left));
1731 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1733 rcImage.top = drawRect->top + center_offset_h;
1734 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1735 /* right side of the tab, but the image still uses the left as its x position */
1736 /* this keeps the image always drawn off of the same side of the tab */
1737 rcImage.left = drawRect->right - cx - center_offset_v;
1738 drawRect->top += cy + infoPtr->uHItemPadding;
1740 else if(lStyle & TCS_VERTICAL)
1742 rcImage.top = drawRect->bottom - cy - center_offset_h;
1743 rcImage.left = drawRect->left + center_offset_v;
1744 drawRect->bottom -= cy + infoPtr->uHItemPadding;
1746 else /* normal style, whether TCS_BOTTOM or not */
1748 rcImage.left = drawRect->left + center_offset_h;
1749 rcImage.top = drawRect->top + center_offset_v;
1750 drawRect->left += cx + infoPtr->uHItemPadding;
1753 TRACE("drawing image=%d, left=%ld, top=%ld\n",
1754 item->iImage, rcImage.left, rcImage.top-1);
1766 /* Now position text */
1767 if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT)
1768 center_offset_h = infoPtr->uHItemPadding;
1770 if(lStyle & TCS_VERTICAL)
1771 center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1773 center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1775 if(lStyle & TCS_VERTICAL)
1777 if(lStyle & TCS_BOTTOM)
1778 drawRect->top+=center_offset_h;
1780 drawRect->bottom-=center_offset_h;
1782 center_offset_v = ((drawRect->right - drawRect->left) - (rcText.bottom - rcText.top)) / 2;
1786 drawRect->left += center_offset_h;
1787 center_offset_v = ((drawRect->bottom - drawRect->top) - (rcText.bottom - rcText.top)) / 2;
1790 /* if an item is selected, the text is shifted up instead of down */
1791 if (iItem == infoPtr->iSelected)
1792 center_offset_v -= infoPtr->uVItemPadding / 2;
1794 center_offset_v += infoPtr->uVItemPadding / 2;
1796 if (center_offset_v < 0)
1797 center_offset_v = 0;
1799 if(lStyle & TCS_VERTICAL)
1800 drawRect->left += center_offset_v;
1802 drawRect->top += center_offset_v;
1805 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1807 static const WCHAR ArialW[] = { 'A','r','i','a','l',0 };
1810 INT nEscapement = 900;
1811 INT nOrientation = 900;
1813 if(lStyle & TCS_BOTTOM)
1816 nOrientation = -900;
1819 /* to get a font with the escapement and orientation we are looking for, we need to */
1820 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1821 if (!GetObjectW((infoPtr->hFont) ?
1822 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1823 sizeof(LOGFONTW),&logfont))
1827 lstrcpyW(logfont.lfFaceName, ArialW);
1828 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1830 logfont.lfWeight = FW_NORMAL;
1831 logfont.lfItalic = 0;
1832 logfont.lfUnderline = 0;
1833 logfont.lfStrikeOut = 0;
1836 logfont.lfEscapement = nEscapement;
1837 logfont.lfOrientation = nOrientation;
1838 hFont = CreateFontIndirectW(&logfont);
1839 SelectObject(hdc, hFont);
1844 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1845 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1849 lstrlenW(item->pszText),
1853 DeleteObject(hFont);
1857 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1858 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1859 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1860 (rcText.right-rcText.left));
1867 lstrlenW(item->pszText),
1869 DT_LEFT | DT_SINGLELINE
1874 *drawRect = rcTemp; /* restore drawRect */
1880 SelectObject(hdc, hOldFont);
1881 SetBkMode(hdc, oldBkMode);
1882 SelectObject(hdc, holdPen);
1883 DeleteObject( htextPen );
1886 /******************************************************************************
1889 * This method is used to draw a single tab into the tab control.
1891 static void TAB_DrawItem(
1896 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1900 RECT r, fillRect, r1;
1903 COLORREF bkgnd, corner;
1907 * Get the rectangle for the item.
1909 isVisible = TAB_InternalGetItemRect(infoPtr,
1918 /* Clip UpDown control to not draw over it */
1919 if (infoPtr->needsScrolling)
1921 GetWindowRect(infoPtr->hwnd, &rC);
1922 GetWindowRect(infoPtr->hwndUpDown, &rUD);
1923 ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
1926 /* If you need to see what the control is doing,
1927 * then override these variables. They will change what
1928 * fill colors are used for filling the tabs, and the
1929 * corners when drawing the edge.
1931 bkgnd = comctl32_color.clrBtnFace;
1932 corner = comctl32_color.clrBtnFace;
1934 if (lStyle & TCS_BUTTONS)
1936 /* Get item rectangle */
1939 /* Separators between flat buttons */
1940 if (lStyle & TCS_FLATBUTTONS)
1943 r1.right += (FLAT_BTN_SPACINGX -2);
1944 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1947 if (iItem == infoPtr->iSelected)
1949 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1951 OffsetRect(&r, 1, 1);
1953 else /* ! selected */
1955 if (!(lStyle & TCS_FLATBUTTONS))
1956 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1959 else /* !TCS_BUTTONS */
1961 /* We draw a rectangle of different sizes depending on the selection
1963 if (iItem == infoPtr->iSelected) {
1965 GetClientRect (infoPtr->hwnd, &rect);
1966 clRight = rect.right;
1967 clBottom = rect.bottom;
1974 * Erase the background. (Delay it but setup rectangle.)
1975 * This is necessary when drawing the selected item since it is larger
1976 * than the others, it might overlap with stuff already drawn by the
1981 /* Draw themed tabs - but only if they are at the top.
1982 * Windows draws even side or bottom tabs themed, with wacky results.
1983 * However, since in Wine apps may get themed that did not opt in via
1984 * a manifest avoid theming when we know the result will be wrong */
1985 if ((theme = GetWindowTheme (infoPtr->hwnd))
1986 && ((lStyle & (TCS_VERTICAL | TCS_BOTTOM)) == 0))
1988 const static int partIds[8] = {
1991 TABP_TABITEMLEFTEDGE,
1992 TABP_TABITEMRIGHTEDGE,
1993 TABP_TABITEMBOTHEDGE,
1996 TABP_TOPTABITEMLEFTEDGE,
1997 TABP_TOPTABITEMRIGHTEDGE,
1998 TABP_TOPTABITEMBOTHEDGE,
2001 int stateId = TIS_NORMAL;
2003 /* selected and unselected tabs have different parts */
2004 if (iItem == infoPtr->iSelected)
2006 /* The part also differs on the position of a tab on a line.
2007 * "Visually" determining the position works well enough. */
2008 if(selectedRect.left == 0)
2010 if(selectedRect.right == clRight)
2013 if (iItem == infoPtr->iSelected)
2014 stateId = TIS_SELECTED;
2015 else if (iItem == infoPtr->iHotTracked)
2017 else if (iItem == infoPtr->uFocus)
2018 stateId = TIS_FOCUSED;
2020 /* Adjust rectangle for bottommost row */
2021 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2024 DrawThemeBackground (theme, hdc, partIds[partIndex], stateId, &r, NULL);
2025 GetThemeBackgroundContentRect (theme, hdc, partIds[partIndex], stateId, &r, &r);
2027 else if(lStyle & TCS_VERTICAL)
2029 /* These are for adjusting the drawing of a Selected tab */
2030 /* The initial values are for the normal case of non-Selected */
2031 int ZZ = 1; /* Do not strech if selected */
2032 if (iItem == infoPtr->iSelected) {
2035 /* if leftmost draw the line longer */
2036 if(selectedRect.top == 0)
2037 fillRect.top += CONTROL_BORDER_SIZEY;
2038 /* if rightmost draw the line longer */
2039 if(selectedRect.bottom == clBottom)
2040 fillRect.bottom -= CONTROL_BORDER_SIZEY;
2043 if (lStyle & TCS_BOTTOM)
2045 /* Adjust both rectangles to match native */
2048 TRACE("<right> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2050 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2051 r.left,r.top,r.right,r.bottom);
2053 /* Clear interior */
2054 SetBkColor(hdc, bkgnd);
2055 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2057 /* Draw rectangular edge around tab */
2058 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
2060 /* Now erase the top corner and draw diagonal edge */
2061 SetBkColor(hdc, corner);
2062 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2065 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2066 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2068 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2070 /* Now erase the bottom corner and draw diagonal edge */
2071 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2072 r1.bottom = r.bottom;
2074 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2075 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2077 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2079 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
2083 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
2089 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2091 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2092 r.left,r.top,r.right,r.bottom);
2094 /* Clear interior */
2095 SetBkColor(hdc, bkgnd);
2096 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2098 /* Draw rectangular edge around tab */
2099 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2101 /* Now erase the top corner and draw diagonal edge */
2102 SetBkColor(hdc, corner);
2105 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2106 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2107 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2109 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2111 /* Now erase the bottom corner and draw diagonal edge */
2113 r1.bottom = r.bottom;
2114 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2115 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2116 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2118 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2121 else /* ! TCS_VERTICAL */
2123 /* These are for adjusting the drawing of a Selected tab */
2124 /* The initial values are for the normal case of non-Selected */
2125 if (iItem == infoPtr->iSelected) {
2126 /* if leftmost draw the line longer */
2127 if(selectedRect.left == 0)
2128 fillRect.left += CONTROL_BORDER_SIZEX;
2129 /* if rightmost draw the line longer */
2130 if(selectedRect.right == clRight)
2131 fillRect.right -= CONTROL_BORDER_SIZEX;
2134 if (lStyle & TCS_BOTTOM)
2136 /* Adjust both rectangles for topmost row */
2137 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2143 TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2145 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2146 r.left,r.top,r.right,r.bottom);
2148 /* Clear interior */
2149 SetBkColor(hdc, bkgnd);
2150 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2152 /* Draw rectangular edge around tab */
2153 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2155 /* Now erase the righthand corner and draw diagonal edge */
2156 SetBkColor(hdc, corner);
2157 r1.left = r.right - ROUND_CORNER_SIZE;
2158 r1.bottom = r.bottom;
2160 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2161 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2163 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2165 /* Now erase the lefthand corner and draw diagonal edge */
2167 r1.bottom = r.bottom;
2168 r1.right = r1.left + ROUND_CORNER_SIZE;
2169 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2170 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2172 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2174 if (iItem == infoPtr->iSelected)
2178 if (selectedRect.left == 0)
2183 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2190 /* Adjust both rectangles for bottommost row */
2191 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2193 fillRect.bottom += 3;
2197 TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2199 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2200 r.left,r.top,r.right,r.bottom);
2202 /* Clear interior */
2203 SetBkColor(hdc, bkgnd);
2204 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2206 /* Draw rectangular edge around tab */
2207 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2209 /* Now erase the righthand corner and draw diagonal edge */
2210 SetBkColor(hdc, corner);
2211 r1.left = r.right - ROUND_CORNER_SIZE;
2214 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2215 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2217 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2219 /* Now erase the lefthand corner and draw diagonal edge */
2222 r1.right = r1.left + ROUND_CORNER_SIZE;
2223 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2224 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2226 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2231 TAB_DumpItemInternal(infoPtr, iItem);
2233 /* This modifies r to be the text rectangle. */
2234 TAB_DrawItemInterior(infoPtr, hdc, iItem, &r);
2238 /******************************************************************************
2241 * This method is used to draw the raised border around the tab control
2244 static void TAB_DrawBorder (TAB_INFO *infoPtr, HDC hdc)
2247 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2248 HTHEME theme = GetWindowTheme (infoPtr->hwnd);
2250 GetClientRect (infoPtr->hwnd, &rect);
2253 * Adjust for the style
2256 if (infoPtr->uNumItem)
2258 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2259 rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2260 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2261 rect.right -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2262 else if(lStyle & TCS_VERTICAL)
2263 rect.left += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2264 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2265 rect.top += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2268 TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2269 rect.left, rect.top, rect.right, rect.bottom);
2272 DrawThemeBackground (theme, hdc, TABP_PANE, 0, &rect, NULL);
2274 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2277 /******************************************************************************
2280 * This method repaints the tab control..
2282 static void TAB_Refresh (TAB_INFO *infoPtr, HDC hdc)
2287 if (!infoPtr->DoRedraw)
2290 hOldFont = SelectObject (hdc, infoPtr->hFont);
2292 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS)
2294 for (i = 0; i < infoPtr->uNumItem; i++)
2295 TAB_DrawItem (infoPtr, hdc, i);
2299 /* Draw all the non selected item first */
2300 for (i = 0; i < infoPtr->uNumItem; i++)
2302 if (i != infoPtr->iSelected)
2303 TAB_DrawItem (infoPtr, hdc, i);
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);
2310 /* Then, draw the selected item */
2311 TAB_DrawItem (infoPtr, hdc, infoPtr->iSelected);
2313 /* If we haven't set the current focus yet, set it now.
2314 * Only happens when we first paint the tab controls */
2315 if (infoPtr->uFocus == -1)
2316 TAB_SetCurFocus(infoPtr, infoPtr->iSelected);
2319 SelectObject (hdc, hOldFont);
2322 static inline DWORD TAB_GetRowCount (const TAB_INFO *infoPtr)
2324 return infoPtr->uNumRows;
2327 static inline LRESULT TAB_SetRedraw (TAB_INFO *infoPtr, BOOL doRedraw)
2329 infoPtr->DoRedraw = doRedraw;
2333 /******************************************************************************
2334 * TAB_EnsureSelectionVisible
2336 * This method will make sure that the current selection is completely
2337 * visible by scrolling until it is.
2339 static void TAB_EnsureSelectionVisible(
2342 INT iSelected = infoPtr->iSelected;
2343 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2344 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2346 /* set the items row to the bottommost row or topmost row depending on
2348 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2350 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2354 if(lStyle & TCS_VERTICAL)
2355 newselected = selected->rect.left;
2357 newselected = selected->rect.top;
2359 /* the target row is always (number of rows - 1)
2360 as row 0 is furthest from the clientRect */
2361 iTargetRow = infoPtr->uNumRows - 1;
2363 if (newselected != iTargetRow)
2366 if(lStyle & TCS_VERTICAL)
2368 for (i=0; i < infoPtr->uNumItem; i++)
2370 /* move everything in the row of the selected item to the iTargetRow */
2371 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2373 if (item->rect.left == newselected )
2374 item->rect.left = iTargetRow;
2377 if (item->rect.left > newselected)
2384 for (i=0; i < infoPtr->uNumItem; i++)
2386 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2388 if (item->rect.top == newselected )
2389 item->rect.top = iTargetRow;
2392 if (item->rect.top > newselected)
2397 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2402 * Do the trivial cases first.
2404 if ( (!infoPtr->needsScrolling) ||
2405 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2408 if (infoPtr->leftmostVisible >= iSelected)
2410 infoPtr->leftmostVisible = iSelected;
2414 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2419 /* Calculate the part of the client area that is visible */
2420 GetClientRect(infoPtr->hwnd, &r);
2423 GetClientRect(infoPtr->hwndUpDown, &r);
2426 if ((selected->rect.right -
2427 selected->rect.left) >= width )
2429 /* Special case: width of selected item is greater than visible
2432 infoPtr->leftmostVisible = iSelected;
2436 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2438 if ((selected->rect.right - TAB_GetItem(infoPtr, i)->rect.left) < width)
2441 infoPtr->leftmostVisible = i;
2445 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2446 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2448 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2449 MAKELONG(infoPtr->leftmostVisible, 0));
2452 /******************************************************************************
2453 * TAB_InvalidateTabArea
2455 * This method will invalidate the portion of the control that contains the
2456 * tabs. It is called when the state of the control changes and needs
2459 static void TAB_InvalidateTabArea(TAB_INFO* infoPtr)
2461 RECT clientRect, rInvalidate, rAdjClient;
2462 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2463 INT lastRow = infoPtr->uNumRows - 1;
2466 if (lastRow < 0) return;
2468 GetClientRect(infoPtr->hwnd, &clientRect);
2469 rInvalidate = clientRect;
2470 rAdjClient = clientRect;
2472 TAB_AdjustRect(infoPtr, 0, &rAdjClient);
2474 TAB_InternalGetItemRect(infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2475 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2477 rInvalidate.left = rAdjClient.right;
2478 if (infoPtr->uNumRows == 1)
2479 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2481 else if(lStyle & TCS_VERTICAL)
2483 rInvalidate.right = rAdjClient.left;
2484 if (infoPtr->uNumRows == 1)
2485 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2487 else if (lStyle & TCS_BOTTOM)
2489 rInvalidate.top = rAdjClient.bottom;
2490 if (infoPtr->uNumRows == 1)
2491 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2495 rInvalidate.bottom = rAdjClient.top;
2496 if (infoPtr->uNumRows == 1)
2497 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2500 /* Punch out the updown control */
2501 if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2503 GetClientRect(infoPtr->hwndUpDown, &r);
2504 if (rInvalidate.right > clientRect.right - r.left)
2505 rInvalidate.right = rInvalidate.right - (r.right - r.left);
2507 rInvalidate.right = clientRect.right - r.left;
2510 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2511 rInvalidate.left, rInvalidate.top,
2512 rInvalidate.right, rInvalidate.bottom);
2514 InvalidateRect(infoPtr->hwnd, &rInvalidate, TRUE);
2517 static inline LRESULT TAB_Paint (TAB_INFO *infoPtr, HDC hdcPaint)
2526 hdc = BeginPaint (infoPtr->hwnd, &ps);
2527 TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2529 ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2532 TAB_Refresh (infoPtr, hdc);
2535 EndPaint (infoPtr->hwnd, &ps);
2541 TAB_InsertItemT (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2548 GetClientRect (infoPtr->hwnd, &rect);
2549 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", infoPtr->hwnd,
2550 rect.top, rect.left, rect.bottom, rect.right);
2552 pti = (TCITEMW *)lParam;
2553 iItem = (INT)wParam;
2555 if (iItem < 0) return -1;
2556 if (iItem > infoPtr->uNumItem)
2557 iItem = infoPtr->uNumItem;
2559 TAB_DumpItemExternalT(pti, iItem, bUnicode);
2562 if (infoPtr->uNumItem == 0) {
2563 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2564 infoPtr->uNumItem++;
2565 infoPtr->iSelected = 0;
2568 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2570 infoPtr->uNumItem++;
2571 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2573 /* pre insert copy */
2575 memcpy (infoPtr->items, oldItems,
2576 iItem * TAB_ITEM_SIZE(infoPtr));
2579 /* post insert copy */
2580 if (iItem < infoPtr->uNumItem - 1) {
2581 memcpy (TAB_GetItem(infoPtr, iItem + 1),
2582 oldItems + iItem * TAB_ITEM_SIZE(infoPtr),
2583 (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2587 if (iItem <= infoPtr->iSelected)
2588 infoPtr->iSelected++;
2593 item = TAB_GetItem(infoPtr, iItem);
2595 item->mask = pti->mask;
2596 item->pszText = NULL;
2598 if (pti->mask & TCIF_TEXT)
2601 Str_SetPtrW (&item->pszText, pti->pszText);
2603 Str_SetPtrAtoW (&item->pszText, (LPSTR)pti->pszText);
2606 if (pti->mask & TCIF_IMAGE)
2607 item->iImage = pti->iImage;
2611 if (pti->mask & TCIF_PARAM)
2612 memcpy(item->extra, &pti->lParam, infoPtr->cbInfo);
2614 memset(item->extra, 0, infoPtr->cbInfo);
2616 TAB_SetItemBounds(infoPtr);
2617 if (infoPtr->uNumItem > 1)
2618 TAB_InvalidateTabArea(infoPtr);
2620 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2622 TRACE("[%p]: added item %d %s\n",
2623 infoPtr->hwnd, iItem, debugstr_w(item->pszText));
2629 TAB_SetItemSize (TAB_INFO *infoPtr, LPARAM lParam)
2631 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2633 BOOL bNeedPaint = FALSE;
2635 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2637 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2638 if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2640 infoPtr->tabWidth = max((INT)LOWORD(lParam), infoPtr->tabMinWidth);
2644 if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2646 if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2647 infoPtr->tabHeight = (INT)HIWORD(lParam);
2651 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2652 HIWORD(lResult), LOWORD(lResult),
2653 infoPtr->tabHeight, infoPtr->tabWidth);
2657 TAB_SetItemBounds(infoPtr);
2658 RedrawWindow(infoPtr->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2664 static inline LRESULT TAB_SetMinTabWidth (TAB_INFO *infoPtr, INT cx)
2668 TRACE("(%p,%d)\n", infoPtr, cx);
2671 oldcx = infoPtr->tabMinWidth;
2672 infoPtr->tabMinWidth = (cx==-1)?DEFAULT_TAB_WIDTH:cx;
2678 static inline LRESULT
2679 TAB_HighlightItem (TAB_INFO *infoPtr, INT iItem, BOOL fHighlight)
2683 TRACE("(%p,%d,%s)\n", infoPtr, iItem, fHighlight ? "true" : "false");
2685 if (!infoPtr || iItem < 0 || iItem >= infoPtr->uNumItem)
2688 lpState = &TAB_GetItem(infoPtr, iItem)->dwState;
2691 *lpState |= TCIS_HIGHLIGHTED;
2693 *lpState &= ~TCIS_HIGHLIGHTED;
2699 TAB_SetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2703 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2705 if (iItem < 0 || iItem >= infoPtr->uNumItem)
2708 TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2710 wineItem = TAB_GetItem(infoPtr, iItem);
2712 if (tabItem->mask & TCIF_IMAGE)
2713 wineItem->iImage = tabItem->iImage;
2715 if (tabItem->mask & TCIF_PARAM)
2716 memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2718 if (tabItem->mask & TCIF_RTLREADING)
2719 FIXME("TCIF_RTLREADING\n");
2721 if (tabItem->mask & TCIF_STATE)
2722 wineItem->dwState = tabItem->dwState;
2724 if (tabItem->mask & TCIF_TEXT)
2726 if (wineItem->pszText)
2728 Free(wineItem->pszText);
2729 wineItem->pszText = NULL;
2732 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2734 Str_SetPtrAtoW(&wineItem->pszText, (LPSTR)tabItem->pszText);
2737 /* Update and repaint tabs */
2738 TAB_SetItemBounds(infoPtr);
2739 TAB_InvalidateTabArea(infoPtr);
2744 static inline LRESULT TAB_GetItemCount (const TAB_INFO *infoPtr)
2746 return infoPtr->uNumItem;
2751 TAB_GetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2755 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2757 if (iItem < 0 || iItem >= infoPtr->uNumItem)
2760 wineItem = TAB_GetItem(infoPtr, iItem);
2762 if (tabItem->mask & TCIF_IMAGE)
2763 tabItem->iImage = wineItem->iImage;
2765 if (tabItem->mask & TCIF_PARAM)
2766 memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2768 if (tabItem->mask & TCIF_RTLREADING)
2769 FIXME("TCIF_RTLREADING\n");
2771 if (tabItem->mask & TCIF_STATE)
2772 tabItem->dwState = wineItem->dwState;
2774 if (tabItem->mask & TCIF_TEXT)
2777 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2779 Str_GetPtrWtoA (wineItem->pszText, (LPSTR)tabItem->pszText, tabItem->cchTextMax);
2782 TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2788 static LRESULT TAB_DeleteItem (TAB_INFO *infoPtr, INT iItem)
2790 BOOL bResult = FALSE;
2792 TRACE("(%p, %d)\n", infoPtr, iItem);
2794 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2796 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
2797 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2799 TAB_InvalidateTabArea(infoPtr);
2801 if ((item->mask & TCIF_TEXT) && item->pszText)
2802 Free(item->pszText);
2804 infoPtr->uNumItem--;
2806 if (!infoPtr->uNumItem)
2808 infoPtr->items = NULL;
2809 if (infoPtr->iHotTracked >= 0)
2811 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
2812 infoPtr->iHotTracked = -1;
2817 infoPtr->items = Alloc(TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2820 memcpy(infoPtr->items, oldItems, iItem * TAB_ITEM_SIZE(infoPtr));
2822 if (iItem < infoPtr->uNumItem)
2823 memcpy(TAB_GetItem(infoPtr, iItem),
2824 oldItems + (iItem + 1) * TAB_ITEM_SIZE(infoPtr),
2825 (infoPtr->uNumItem - iItem) * TAB_ITEM_SIZE(infoPtr));
2827 if (iItem <= infoPtr->iHotTracked)
2829 /* When tabs move left/up, the hot track item may change */
2830 FIXME("Recalc hot track");
2835 /* Readjust the selected index */
2836 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2837 infoPtr->iSelected--;
2839 if (iItem < infoPtr->iSelected)
2840 infoPtr->iSelected--;
2842 if (infoPtr->uNumItem == 0)
2843 infoPtr->iSelected = -1;
2845 /* Reposition and repaint tabs */
2846 TAB_SetItemBounds(infoPtr);
2854 static inline LRESULT TAB_DeleteAllItems (TAB_INFO *infoPtr)
2856 TRACE("(%p)\n", infoPtr);
2857 while (infoPtr->uNumItem)
2858 TAB_DeleteItem (infoPtr, 0);
2863 static inline LRESULT TAB_GetFont (const TAB_INFO *infoPtr)
2865 TRACE("(%p) returning %p\n", infoPtr, infoPtr->hFont);
2866 return (LRESULT)infoPtr->hFont;
2869 static inline LRESULT TAB_SetFont (TAB_INFO *infoPtr, HFONT hNewFont)
2871 TRACE("(%p,%p)\n", infoPtr, hNewFont);
2873 infoPtr->hFont = hNewFont;
2875 TAB_SetItemBounds(infoPtr);
2877 TAB_InvalidateTabArea(infoPtr);
2883 static inline LRESULT TAB_GetImageList (const TAB_INFO *infoPtr)
2886 return (LRESULT)infoPtr->himl;
2889 static inline LRESULT TAB_SetImageList (TAB_INFO *infoPtr, HIMAGELIST himlNew)
2891 HIMAGELIST himlPrev = infoPtr->himl;
2893 infoPtr->himl = himlNew;
2894 return (LRESULT)himlPrev;
2897 static inline LRESULT TAB_GetUnicodeFormat (const TAB_INFO *infoPtr)
2899 return infoPtr->bUnicode;
2902 static inline LRESULT TAB_SetUnicodeFormat (TAB_INFO *infoPtr, BOOL bUnicode)
2904 BOOL bTemp = infoPtr->bUnicode;
2906 infoPtr->bUnicode = bUnicode;
2911 static inline LRESULT TAB_Size (TAB_INFO *infoPtr)
2913 /* I'm not really sure what the following code was meant to do.
2914 This is what it is doing:
2915 When WM_SIZE is sent with SIZE_RESTORED, the control
2916 gets positioned in the top left corner.
2920 UINT uPosFlags,cx,cy;
2924 parent = GetParent (hwnd);
2925 GetClientRect(parent, &parent_rect);
2928 if (GetWindowLongW(hwnd, GWL_STYLE) & CCS_NORESIZE)
2929 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2931 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2932 cx, cy, uPosFlags | SWP_NOZORDER);
2934 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2937 /* Recompute the size/position of the tabs. */
2938 TAB_SetItemBounds (infoPtr);
2940 /* Force a repaint of the control. */
2941 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2947 static LRESULT TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2950 TEXTMETRICW fontMetrics;
2955 infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
2957 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
2959 infoPtr->hwnd = hwnd;
2960 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
2961 infoPtr->uNumItem = 0;
2962 infoPtr->uNumRows = 0;
2963 infoPtr->uHItemPadding = 6;
2964 infoPtr->uVItemPadding = 3;
2965 infoPtr->uHItemPadding_s = 6;
2966 infoPtr->uVItemPadding_s = 3;
2969 infoPtr->hcurArrow = LoadCursorW (0, (LPWSTR)IDC_ARROW);
2970 infoPtr->iSelected = -1;
2971 infoPtr->iHotTracked = -1;
2972 infoPtr->uFocus = -1;
2973 infoPtr->hwndToolTip = 0;
2974 infoPtr->DoRedraw = TRUE;
2975 infoPtr->needsScrolling = FALSE;
2976 infoPtr->hwndUpDown = 0;
2977 infoPtr->leftmostVisible = 0;
2978 infoPtr->fHeightSet = FALSE;
2979 infoPtr->bUnicode = IsWindowUnicode (hwnd);
2980 infoPtr->cbInfo = sizeof(LPARAM);
2982 TRACE("Created tab control, hwnd [%p]\n", hwnd);
2984 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2985 if you don't specify it in CreateWindow. This is necessary in
2986 order for paint to work correctly. This follows windows behaviour. */
2987 dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
2988 SetWindowLongW(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2990 if (dwStyle & TCS_TOOLTIPS) {
2991 /* Create tooltip control */
2992 infoPtr->hwndToolTip =
2993 CreateWindowExW (0, TOOLTIPS_CLASSW, NULL, 0,
2994 CW_USEDEFAULT, CW_USEDEFAULT,
2995 CW_USEDEFAULT, CW_USEDEFAULT,
2998 /* Send NM_TOOLTIPSCREATED notification */
2999 if (infoPtr->hwndToolTip) {
3000 NMTOOLTIPSCREATED nmttc;
3002 nmttc.hdr.hwndFrom = hwnd;
3003 nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
3004 nmttc.hdr.code = NM_TOOLTIPSCREATED;
3005 nmttc.hwndToolTips = infoPtr->hwndToolTip;
3007 SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
3008 (WPARAM)GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc);
3012 OpenThemeData (infoPtr->hwnd, themeClass);
3015 * We need to get text information so we need a DC and we need to select
3019 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3021 /* Use the system font to determine the initial height of a tab. */
3022 GetTextMetricsW(hdc, &fontMetrics);
3025 * Make sure there is enough space for the letters + growing the
3026 * selected item + extra space for the selected item.
3028 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3029 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
3030 infoPtr->uVItemPadding;
3032 /* Initialize the width of a tab. */
3033 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
3034 infoPtr->tabMinWidth = 0;
3036 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3038 SelectObject (hdc, hOldFont);
3039 ReleaseDC(hwnd, hdc);
3045 TAB_Destroy (TAB_INFO *infoPtr)
3052 SetWindowLongPtrW(infoPtr->hwnd, 0, 0);
3054 if (infoPtr->items) {
3055 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3056 if (TAB_GetItem(infoPtr, iItem)->pszText)
3057 Free (TAB_GetItem(infoPtr, iItem)->pszText);
3059 Free (infoPtr->items);
3062 if (infoPtr->hwndToolTip)
3063 DestroyWindow (infoPtr->hwndToolTip);
3065 if (infoPtr->hwndUpDown)
3066 DestroyWindow(infoPtr->hwndUpDown);
3068 if (infoPtr->iHotTracked >= 0)
3069 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
3071 CloseThemeData (GetWindowTheme (infoPtr->hwnd));
3077 /* update theme after a WM_THEMECHANGED message */
3078 static LRESULT theme_changed (TAB_INFO* infoPtr)
3080 HTHEME theme = GetWindowTheme (infoPtr->hwnd);
3081 CloseThemeData (theme);
3082 OpenThemeData (infoPtr->hwnd, themeClass);
3086 static LRESULT TAB_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
3090 return WVR_ALIGNTOP;
3093 static inline LRESULT
3094 TAB_SetItemExtra (TAB_INFO *infoPtr, INT cbInfo)
3096 if (!infoPtr || cbInfo <= 0)
3099 if (infoPtr->uNumItem)
3101 /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
3105 infoPtr->cbInfo = cbInfo;
3109 static LRESULT WINAPI
3110 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3112 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3114 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3115 if (!infoPtr && (uMsg != WM_CREATE))
3116 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
3120 case TCM_GETIMAGELIST:
3121 return TAB_GetImageList (infoPtr);
3123 case TCM_SETIMAGELIST:
3124 return TAB_SetImageList (infoPtr, (HIMAGELIST)lParam);
3126 case TCM_GETITEMCOUNT:
3127 return TAB_GetItemCount (infoPtr);
3131 return TAB_GetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_GETITEMW);
3135 return TAB_SetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_SETITEMW);
3137 case TCM_DELETEITEM:
3138 return TAB_DeleteItem (infoPtr, (INT)wParam);
3140 case TCM_DELETEALLITEMS:
3141 return TAB_DeleteAllItems (infoPtr);
3143 case TCM_GETITEMRECT:
3144 return TAB_GetItemRect (infoPtr, wParam, lParam);
3147 return TAB_GetCurSel (infoPtr);
3150 return TAB_HitTest (infoPtr, (LPTCHITTESTINFO)lParam);
3153 return TAB_SetCurSel (infoPtr, (INT)wParam);
3155 case TCM_INSERTITEMA:
3156 case TCM_INSERTITEMW:
3157 return TAB_InsertItemT (infoPtr, wParam, lParam, uMsg == TCM_INSERTITEMW);
3159 case TCM_SETITEMEXTRA:
3160 return TAB_SetItemExtra (infoPtr, (int)wParam);
3162 case TCM_ADJUSTRECT:
3163 return TAB_AdjustRect (infoPtr, (BOOL)wParam, (LPRECT)lParam);
3165 case TCM_SETITEMSIZE:
3166 return TAB_SetItemSize (infoPtr, lParam);
3168 case TCM_REMOVEIMAGE:
3169 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3172 case TCM_SETPADDING:
3173 return TAB_SetPadding (infoPtr, lParam);
3175 case TCM_GETROWCOUNT:
3176 return TAB_GetRowCount(infoPtr);
3178 case TCM_GETUNICODEFORMAT:
3179 return TAB_GetUnicodeFormat (infoPtr);
3181 case TCM_SETUNICODEFORMAT:
3182 return TAB_SetUnicodeFormat (infoPtr, (BOOL)wParam);
3184 case TCM_HIGHLIGHTITEM:
3185 return TAB_HighlightItem (infoPtr, (INT)wParam, (BOOL)LOWORD(lParam));
3187 case TCM_GETTOOLTIPS:
3188 return TAB_GetToolTips (infoPtr);
3190 case TCM_SETTOOLTIPS:
3191 return TAB_SetToolTips (infoPtr, (HWND)wParam);
3193 case TCM_GETCURFOCUS:
3194 return TAB_GetCurFocus (infoPtr);
3196 case TCM_SETCURFOCUS:
3197 return TAB_SetCurFocus (infoPtr, (INT)wParam);
3199 case TCM_SETMINTABWIDTH:
3200 return TAB_SetMinTabWidth(infoPtr, (INT)lParam);
3202 case TCM_DESELECTALL:
3203 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3206 case TCM_GETEXTENDEDSTYLE:
3207 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3210 case TCM_SETEXTENDEDSTYLE:
3211 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3215 return TAB_GetFont (infoPtr);
3218 return TAB_SetFont (infoPtr, (HFONT)wParam);
3221 return TAB_Create (hwnd, wParam, lParam);
3224 return TAB_Destroy (infoPtr);
3227 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3229 case WM_LBUTTONDOWN:
3230 return TAB_LButtonDown (infoPtr, wParam, lParam);
3233 return TAB_LButtonUp (infoPtr);
3236 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3238 case WM_RBUTTONDOWN:
3239 return TAB_RButtonDown (infoPtr);
3242 return TAB_MouseMove (infoPtr, wParam, lParam);
3245 return TAB_Paint (infoPtr, (HDC)wParam);
3248 return TAB_Size (infoPtr);
3251 return TAB_SetRedraw (infoPtr, (BOOL)wParam);
3254 return TAB_OnHScroll(infoPtr, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3256 case WM_STYLECHANGED:
3257 TAB_SetItemBounds (infoPtr);
3258 InvalidateRect(hwnd, NULL, TRUE);
3261 case WM_SYSCOLORCHANGE:
3262 COMCTL32_RefreshSysColors();
3265 case WM_THEMECHANGED:
3266 return theme_changed (infoPtr);
3270 TAB_FocusChanging(infoPtr);
3271 break; /* Don't disturb normal focus behavior */
3274 return TAB_KeyUp(infoPtr, wParam);
3276 return TAB_NCHitTest(infoPtr, lParam);
3279 return TAB_NCCalcSize(hwnd, wParam, lParam);
3282 if (uMsg >= WM_USER && uMsg < WM_APP)
3283 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3284 uMsg, wParam, lParam);
3287 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
3296 ZeroMemory (&wndClass, sizeof(WNDCLASSW));
3297 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3298 wndClass.lpfnWndProc = TAB_WindowProc;
3299 wndClass.cbClsExtra = 0;
3300 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3301 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3302 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
3303 wndClass.lpszClassName = WC_TABCONTROLW;
3305 RegisterClassW (&wndClass);
3310 TAB_Unregister (void)
3312 UnregisterClassW (WC_TABCONTROLW, NULL);