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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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);
88 RECT rect; /* bounding rectangle of the item relative to the
89 * leftmost item (the leftmost item, 0, would have a
90 * "left" member of 0 in this rectangle)
92 * additionally the top member holds the row number
93 * and bottom is unused and should be 0 */
94 BYTE extra[1]; /* Space for caller supplied info, variable size */
97 /* The size of a tab item depends on how much extra data is requested */
98 #define TAB_ITEM_SIZE(infoPtr) (FIELD_OFFSET(TAB_ITEM, extra[(infoPtr)->cbInfo]))
102 HWND hwnd; /* Tab control window */
103 HWND hwndNotify; /* notification window (parent) */
104 UINT uNumItem; /* number of tab items */
105 UINT uNumRows; /* number of tab rows */
106 INT tabHeight; /* height of the tab row */
107 INT tabWidth; /* width of tabs */
108 INT tabMinWidth; /* minimum width of items */
109 USHORT uHItemPadding; /* amount of horizontal padding, in pixels */
110 USHORT uVItemPadding; /* amount of vertical padding, in pixels */
111 USHORT uHItemPadding_s; /* Set amount of horizontal padding, in pixels */
112 USHORT uVItemPadding_s; /* Set amount of vertical padding, in pixels */
113 HFONT hFont; /* handle to the current font */
114 HCURSOR hcurArrow; /* handle to the current cursor */
115 HIMAGELIST himl; /* handle to an image list (may be 0) */
116 HWND hwndToolTip; /* handle to tab's tooltip */
117 INT leftmostVisible; /* Used for scrolling, this member contains
118 * the index of the first visible item */
119 INT iSelected; /* the currently selected item */
120 INT iHotTracked; /* the highlighted item under the mouse */
121 INT uFocus; /* item which has the focus */
122 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
123 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
124 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
125 * the size of the control */
126 BOOL fHeightSet; /* was the height of the tabs explicitly set? */
127 BOOL bUnicode; /* Unicode control? */
128 HWND hwndUpDown; /* Updown control used for scrolling */
129 INT cbInfo; /* Number of bytes of caller supplied info per tab */
132 /******************************************************************************
133 * Positioning constants
135 #define SELECTED_TAB_OFFSET 2
136 #define ROUND_CORNER_SIZE 2
137 #define DISPLAY_AREA_PADDINGX 2
138 #define DISPLAY_AREA_PADDINGY 2
139 #define CONTROL_BORDER_SIZEX 2
140 #define CONTROL_BORDER_SIZEY 2
141 #define BUTTON_SPACINGX 3
142 #define BUTTON_SPACINGY 3
143 #define FLAT_BTN_SPACINGX 8
144 #define DEFAULT_MIN_TAB_WIDTH 54
145 #define DEFAULT_TAB_WIDTH_FIXED 96
146 #define DEFAULT_PADDING_X 6
147 #define EXTRA_ICON_PADDING 3
149 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongPtrW(hwnd,0))
150 /* Since items are variable sized, cannot directly access them */
151 #define TAB_GetItem(info,i) \
152 ((TAB_ITEM*)((LPBYTE)info->items + (i) * TAB_ITEM_SIZE(info)))
154 #define GET_DEFAULT_MIN_TAB_WIDTH(infoPtr) (DEFAULT_MIN_TAB_WIDTH - (DEFAULT_PADDING_X - (infoPtr)->uHItemPadding) * 2)
156 /******************************************************************************
157 * Hot-tracking timer constants
159 #define TAB_HOTTRACK_TIMER 1
160 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
162 static const WCHAR themeClass[] = { 'T','a','b',0 };
164 /******************************************************************************
167 static void TAB_InvalidateTabArea(TAB_INFO *);
168 static void TAB_EnsureSelectionVisible(TAB_INFO *);
169 static void TAB_DrawItemInterior(TAB_INFO *, HDC, INT, RECT*);
172 TAB_SendSimpleNotify (const TAB_INFO *infoPtr, UINT code)
176 nmhdr.hwndFrom = infoPtr->hwnd;
177 nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID);
180 return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
181 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
185 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
186 WPARAM wParam, LPARAM lParam)
194 msg.time = GetMessageTime ();
195 msg.pt.x = (short)LOWORD(GetMessagePos ());
196 msg.pt.y = (short)HIWORD(GetMessagePos ());
198 SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
202 TAB_DumpItemExternalT(TCITEMW *pti, UINT iItem, BOOL isW)
205 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
206 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
207 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
208 iItem, pti->iImage, pti->lParam, isW ? debugstr_w(pti->pszText) : debugstr_a((LPSTR)pti->pszText));
213 TAB_DumpItemInternal(TAB_INFO *infoPtr, UINT iItem)
218 ti = TAB_GetItem(infoPtr, iItem);
219 TRACE("tab %d, dwState=0x%08x, pszText=%s, iImage=%d\n",
220 iItem, ti->dwState, debugstr_w(ti->pszText), ti->iImage);
221 TRACE("tab %d, rect.left=%d, rect.top(row)=%d\n",
222 iItem, ti->rect.left, ti->rect.top);
227 * the index of the selected tab, or -1 if no tab is selected. */
228 static inline LRESULT TAB_GetCurSel (const TAB_INFO *infoPtr)
230 return infoPtr->iSelected;
234 * the index of the tab item that has the focus. */
235 static inline LRESULT
236 TAB_GetCurFocus (const TAB_INFO *infoPtr)
238 return infoPtr->uFocus;
241 static inline LRESULT TAB_GetToolTips (const TAB_INFO *infoPtr)
243 if (infoPtr == NULL) return 0;
244 return (LRESULT)infoPtr->hwndToolTip;
247 static inline LRESULT TAB_SetCurSel (TAB_INFO *infoPtr, INT iItem)
249 INT prevItem = infoPtr->iSelected;
252 infoPtr->iSelected=-1;
253 else if (iItem >= infoPtr->uNumItem)
256 if (infoPtr->iSelected != iItem) {
257 infoPtr->iSelected=iItem;
258 TAB_EnsureSelectionVisible(infoPtr);
259 TAB_InvalidateTabArea(infoPtr);
265 static LRESULT TAB_SetCurFocus (TAB_INFO *infoPtr, INT iItem)
268 infoPtr->uFocus = -1;
269 else if (iItem < infoPtr->uNumItem) {
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);
292 static inline LRESULT
293 TAB_SetToolTips (TAB_INFO *infoPtr, HWND hwndToolTip)
296 infoPtr->hwndToolTip = hwndToolTip;
300 static inline LRESULT
301 TAB_SetPadding (TAB_INFO *infoPtr, LPARAM lParam)
305 infoPtr->uHItemPadding_s=LOWORD(lParam);
306 infoPtr->uVItemPadding_s=HIWORD(lParam);
311 /******************************************************************************
312 * TAB_InternalGetItemRect
314 * This method will calculate the rectangle representing a given tab item in
315 * client coordinates. This method takes scrolling into account.
317 * This method returns TRUE if the item is visible in the window and FALSE
318 * if it is completely outside the client area.
320 static BOOL TAB_InternalGetItemRect(
321 const TAB_INFO* infoPtr,
326 RECT tmpItemRect,clientRect;
327 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
329 /* Perform a sanity check and a trivial visibility check. */
330 if ( (infoPtr->uNumItem <= 0) ||
331 (itemIndex >= infoPtr->uNumItem) ||
332 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
334 TRACE("Not Visible\n");
335 /* need to initialize these to empty rects */
338 memset(itemRect,0,sizeof(RECT));
339 itemRect->bottom = infoPtr->tabHeight;
342 memset(selectedRect,0,sizeof(RECT));
347 * Avoid special cases in this procedure by assigning the "out"
348 * parameters if the caller didn't supply them
350 if (itemRect == NULL)
351 itemRect = &tmpItemRect;
353 /* Retrieve the unmodified item rect. */
354 *itemRect = TAB_GetItem(infoPtr,itemIndex)->rect;
356 /* calculate the times bottom and top based on the row */
357 GetClientRect(infoPtr->hwnd, &clientRect);
359 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
361 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * infoPtr->tabHeight -
362 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
363 itemRect->left = itemRect->right - infoPtr->tabHeight;
365 else if (lStyle & TCS_VERTICAL)
367 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * infoPtr->tabHeight +
368 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
369 itemRect->right = itemRect->left + infoPtr->tabHeight;
371 else if (lStyle & TCS_BOTTOM)
373 itemRect->bottom = clientRect.bottom - itemRect->top * infoPtr->tabHeight -
374 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
375 itemRect->top = itemRect->bottom - infoPtr->tabHeight;
377 else /* not TCS_BOTTOM and not TCS_VERTICAL */
379 itemRect->top = clientRect.top + itemRect->top * infoPtr->tabHeight +
380 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
381 itemRect->bottom = itemRect->top + infoPtr->tabHeight;
385 * "scroll" it to make sure the item at the very left of the
386 * tab control is the leftmost visible tab.
388 if(lStyle & TCS_VERTICAL)
392 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.top);
395 * Move the rectangle so the first item is slightly offset from
396 * the bottom of the tab control.
400 SELECTED_TAB_OFFSET);
405 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.left,
409 * Move the rectangle so the first item is slightly offset from
410 * the left of the tab control.
416 TRACE("item %d tab h=%d, rect=(%d,%d)-(%d,%d)\n",
417 itemIndex, infoPtr->tabHeight,
418 itemRect->left, itemRect->top, itemRect->right, itemRect->bottom);
420 /* Now, calculate the position of the item as if it were selected. */
421 if (selectedRect!=NULL)
423 CopyRect(selectedRect, itemRect);
425 /* The rectangle of a selected item is a bit wider. */
426 if(lStyle & TCS_VERTICAL)
427 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
429 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
431 /* If it also a bit higher. */
432 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
434 selectedRect->left -= 2; /* the border is thicker on the right */
435 selectedRect->right += SELECTED_TAB_OFFSET;
437 else if (lStyle & TCS_VERTICAL)
439 selectedRect->left -= SELECTED_TAB_OFFSET;
440 selectedRect->right += 1;
442 else if (lStyle & TCS_BOTTOM)
444 selectedRect->bottom += SELECTED_TAB_OFFSET;
446 else /* not TCS_BOTTOM and not TCS_VERTICAL */
448 selectedRect->top -= SELECTED_TAB_OFFSET;
449 selectedRect->bottom -= 1;
453 /* Check for visibility */
454 if (lStyle & TCS_VERTICAL)
455 return (itemRect->top < clientRect.bottom) && (itemRect->bottom > clientRect.top);
457 return (itemRect->left < clientRect.right) && (itemRect->right > clientRect.left);
461 TAB_GetItemRect(TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
463 return TAB_InternalGetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam, (LPRECT)NULL);
466 /******************************************************************************
469 * This method is called to handle keyboard input
471 static LRESULT TAB_KeyUp(TAB_INFO* infoPtr, WPARAM keyCode)
478 newItem = infoPtr->uFocus - 1;
481 newItem = infoPtr->uFocus + 1;
486 * If we changed to a valid item, change the selection
489 newItem < infoPtr->uNumItem &&
490 infoPtr->uFocus != newItem)
492 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
494 infoPtr->iSelected = newItem;
495 infoPtr->uFocus = newItem;
496 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
498 TAB_EnsureSelectionVisible(infoPtr);
499 TAB_InvalidateTabArea(infoPtr);
506 /******************************************************************************
509 * This method is called whenever the focus goes in or out of this control
510 * it is used to update the visual state of the control.
512 static void TAB_FocusChanging(const TAB_INFO *infoPtr)
518 * Get the rectangle for the item.
520 isVisible = TAB_InternalGetItemRect(infoPtr,
526 * If the rectangle is not completely invisible, invalidate that
527 * portion of the window.
531 TRACE("invalidate (%d,%d)-(%d,%d)\n",
532 selectedRect.left,selectedRect.top,
533 selectedRect.right,selectedRect.bottom);
534 InvalidateRect(infoPtr->hwnd, &selectedRect, TRUE);
538 static INT TAB_InternalHitTest (
547 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
549 TAB_InternalGetItemRect(infoPtr, iCount, &rect, NULL);
551 if (PtInRect(&rect, pt))
553 *flags = TCHT_ONITEM;
558 *flags = TCHT_NOWHERE;
562 static inline LRESULT
563 TAB_HitTest (TAB_INFO *infoPtr, LPTCHITTESTINFO lptest)
565 return TAB_InternalHitTest (infoPtr, lptest->pt, &lptest->flags);
568 /******************************************************************************
571 * Napster v2b5 has a tab control for its main navigation which has a client
572 * area that covers the whole area of the dialog pages.
573 * That's why it receives all msgs for that area and the underlying dialog ctrls
575 * So I decided that we should handle WM_NCHITTEST here and return
576 * HTTRANSPARENT if we don't hit the tab control buttons.
577 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
578 * doesn't do it that way. Maybe depends on tab control styles ?
580 static inline LRESULT
581 TAB_NCHitTest (TAB_INFO *infoPtr, LPARAM lParam)
586 pt.x = (short)LOWORD(lParam);
587 pt.y = (short)HIWORD(lParam);
588 ScreenToClient(infoPtr->hwnd, &pt);
590 if (TAB_InternalHitTest(infoPtr, pt, &dummyflag) == -1)
591 return HTTRANSPARENT;
597 TAB_LButtonDown (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
603 if (infoPtr->hwndToolTip)
604 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
605 WM_LBUTTONDOWN, wParam, lParam);
607 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
608 SetFocus (infoPtr->hwnd);
611 if (infoPtr->hwndToolTip)
612 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
613 WM_LBUTTONDOWN, wParam, lParam);
615 pt.x = (short)LOWORD(lParam);
616 pt.y = (short)HIWORD(lParam);
618 newItem = TAB_InternalHitTest (infoPtr, pt, &dummy);
620 TRACE("On Tab, item %d\n", newItem);
622 if (newItem != -1 && infoPtr->iSelected != newItem)
624 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
626 infoPtr->iSelected = newItem;
627 infoPtr->uFocus = newItem;
628 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
630 TAB_EnsureSelectionVisible(infoPtr);
632 TAB_InvalidateTabArea(infoPtr);
638 static inline LRESULT
639 TAB_LButtonUp (const TAB_INFO *infoPtr)
641 TAB_SendSimpleNotify(infoPtr, NM_CLICK);
646 static inline LRESULT
647 TAB_RButtonDown (const TAB_INFO *infoPtr)
649 TAB_SendSimpleNotify(infoPtr, NM_RCLICK);
653 /******************************************************************************
654 * TAB_DrawLoneItemInterior
656 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
657 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
658 * up the device context and font. This routine does the same setup but
659 * only calls TAB_DrawItemInterior for the single specified item.
662 TAB_DrawLoneItemInterior(TAB_INFO* infoPtr, int iItem)
664 HDC hdc = GetDC(infoPtr->hwnd);
667 /* Clip UpDown control to not draw over it */
668 if (infoPtr->needsScrolling)
670 GetWindowRect(infoPtr->hwnd, &rC);
671 GetWindowRect(infoPtr->hwndUpDown, &r);
672 ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top);
674 TAB_DrawItemInterior(infoPtr, hdc, iItem, NULL);
675 ReleaseDC(infoPtr->hwnd, hdc);
678 /* update a tab after hottracking - invalidate it or just redraw the interior,
679 * based on whether theming is used or not */
680 static inline void hottrack_refresh (TAB_INFO* infoPtr, int tabIndex)
682 if (tabIndex == -1) return;
684 if (GetWindowTheme (infoPtr->hwnd))
687 TAB_InternalGetItemRect(infoPtr, tabIndex, &rect, NULL);
688 InvalidateRect (infoPtr->hwnd, &rect, FALSE);
691 TAB_DrawLoneItemInterior(infoPtr, tabIndex);
694 /******************************************************************************
695 * TAB_HotTrackTimerProc
697 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
698 * timer is setup so we can check if the mouse is moved out of our window.
699 * (We don't get an event when the mouse leaves, the mouse-move events just
700 * stop being delivered to our window and just start being delivered to
701 * another window.) This function is called when the timer triggers so
702 * we can check if the mouse has left our window. If so, we un-highlight
703 * the hot-tracked tab.
706 TAB_HotTrackTimerProc
708 HWND hwnd, /* handle of window for timer messages */
709 UINT uMsg, /* WM_TIMER message */
710 UINT_PTR idEvent, /* timer identifier */
711 DWORD dwTime /* current system time */
714 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
716 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
721 ** If we can't get the cursor position, or if the cursor is outside our
722 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
723 ** "outside" even if it is within our bounding rect if another window
724 ** overlaps. Note also that the case where the cursor stayed within our
725 ** window but has moved off the hot-tracked tab will be handled by the
726 ** WM_MOUSEMOVE event.
728 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
730 /* Redraw iHotTracked to look normal */
731 INT iRedraw = infoPtr->iHotTracked;
732 infoPtr->iHotTracked = -1;
733 hottrack_refresh (infoPtr, iRedraw);
735 /* Kill this timer */
736 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
741 /******************************************************************************
744 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
745 * should be highlighted. This function determines which tab in a tab control,
746 * if any, is under the mouse and records that information. The caller may
747 * supply output parameters to receive the item number of the tab item which
748 * was highlighted but isn't any longer and of the tab item which is now
749 * highlighted but wasn't previously. The caller can use this information to
750 * selectively redraw those tab items.
752 * If the caller has a mouse position, it can supply it through the pos
753 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
754 * supplies NULL and this function determines the current mouse position
762 int* out_redrawLeave,
769 if (out_redrawLeave != NULL)
770 *out_redrawLeave = -1;
771 if (out_redrawEnter != NULL)
772 *out_redrawEnter = -1;
774 if ((GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_HOTTRACK)
775 || GetWindowTheme (infoPtr->hwnd))
783 ScreenToClient(infoPtr->hwnd, &pt);
787 pt.x = (short)LOWORD(*pos);
788 pt.y = (short)HIWORD(*pos);
791 item = TAB_InternalHitTest(infoPtr, pt, &flags);
794 if (item != infoPtr->iHotTracked)
796 if (infoPtr->iHotTracked >= 0)
798 /* Mark currently hot-tracked to be redrawn to look normal */
799 if (out_redrawLeave != NULL)
800 *out_redrawLeave = infoPtr->iHotTracked;
804 /* Kill timer which forces recheck of mouse pos */
805 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
810 /* Start timer so we recheck mouse pos */
811 UINT timerID = SetTimer
815 TAB_HOTTRACK_TIMER_INTERVAL,
816 TAB_HotTrackTimerProc
820 return; /* Hot tracking not available */
823 infoPtr->iHotTracked = item;
827 /* Mark new hot-tracked to be redrawn to look highlighted */
828 if (out_redrawEnter != NULL)
829 *out_redrawEnter = item;
834 /******************************************************************************
837 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
840 TAB_MouseMove (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
845 if (infoPtr->hwndToolTip)
846 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
847 WM_LBUTTONDOWN, wParam, lParam);
849 /* Determine which tab to highlight. Redraw tabs which change highlight
851 TAB_RecalcHotTrack(infoPtr, &lParam, &redrawLeave, &redrawEnter);
853 hottrack_refresh (infoPtr, redrawLeave);
854 hottrack_refresh (infoPtr, redrawEnter);
859 /******************************************************************************
862 * Calculates the tab control's display area given the window rectangle or
863 * the window rectangle given the requested display rectangle.
865 static LRESULT TAB_AdjustRect(
870 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
871 LONG *iRightBottom, *iLeftTop;
873 TRACE ("hwnd=%p fLarger=%d (%d,%d)-(%d,%d)\n", infoPtr->hwnd, fLarger, prc->left, prc->top, prc->right, prc->bottom);
875 if(lStyle & TCS_VERTICAL)
877 iRightBottom = &(prc->right);
878 iLeftTop = &(prc->left);
882 iRightBottom = &(prc->bottom);
883 iLeftTop = &(prc->top);
886 if (fLarger) /* Go from display rectangle */
888 /* Add the height of the tabs. */
889 if (lStyle & TCS_BOTTOM)
890 *iRightBottom += infoPtr->tabHeight * infoPtr->uNumRows;
892 *iLeftTop -= infoPtr->tabHeight * infoPtr->uNumRows +
893 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
895 /* Inflate the rectangle for the padding */
896 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
898 /* Inflate for the border */
899 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEY);
901 else /* Go from window rectangle. */
903 /* Deflate the rectangle for the border */
904 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEY);
906 /* Deflate the rectangle for the padding */
907 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
909 /* Remove the height of the tabs. */
910 if (lStyle & TCS_BOTTOM)
911 *iRightBottom -= infoPtr->tabHeight * infoPtr->uNumRows;
913 *iLeftTop += (infoPtr->tabHeight) * infoPtr->uNumRows +
914 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
920 /******************************************************************************
923 * This method will handle the notification from the scroll control and
924 * perform the scrolling operation on the tab control.
926 static LRESULT TAB_OnHScroll(
932 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
934 if(nPos < infoPtr->leftmostVisible)
935 infoPtr->leftmostVisible--;
937 infoPtr->leftmostVisible++;
939 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
940 TAB_InvalidateTabArea(infoPtr);
941 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
942 MAKELONG(infoPtr->leftmostVisible, 0));
948 /******************************************************************************
951 * This method will check the current scrolling state and make sure the
952 * scrolling control is displayed (or not).
954 static void TAB_SetupScrolling(
957 const RECT* clientRect)
959 static const WCHAR msctls_updown32W[] = { 'm','s','c','t','l','s','_','u','p','d','o','w','n','3','2',0 };
960 static const WCHAR emptyW[] = { 0 };
962 DWORD lStyle = GetWindowLongW(hwnd, GWL_STYLE);
964 if (infoPtr->needsScrolling)
970 * Calculate the position of the scroll control.
972 if(lStyle & TCS_VERTICAL)
974 controlPos.right = clientRect->right;
975 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
977 if (lStyle & TCS_BOTTOM)
979 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
980 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
984 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
985 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
990 controlPos.right = clientRect->right;
991 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
993 if (lStyle & TCS_BOTTOM)
995 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
996 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
1000 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
1001 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
1006 * If we don't have a scroll control yet, we want to create one.
1007 * If we have one, we want to make sure it's positioned properly.
1009 if (infoPtr->hwndUpDown==0)
1011 infoPtr->hwndUpDown = CreateWindowW(msctls_updown32W, emptyW,
1012 WS_VISIBLE | WS_CHILD | UDS_HORZ,
1013 controlPos.left, controlPos.top,
1014 controlPos.right - controlPos.left,
1015 controlPos.bottom - controlPos.top,
1016 hwnd, NULL, NULL, NULL);
1020 SetWindowPos(infoPtr->hwndUpDown,
1022 controlPos.left, controlPos.top,
1023 controlPos.right - controlPos.left,
1024 controlPos.bottom - controlPos.top,
1025 SWP_SHOWWINDOW | SWP_NOZORDER);
1028 /* Now calculate upper limit of the updown control range.
1029 * We do this by calculating how many tabs will be offscreen when the
1030 * last tab is visible.
1032 if(infoPtr->uNumItem)
1034 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
1035 maxRange = infoPtr->uNumItem;
1036 tabwidth = TAB_GetItem(infoPtr, infoPtr->uNumItem - 1)->rect.right;
1038 for(; maxRange > 0; maxRange--)
1040 if(tabwidth - TAB_GetItem(infoPtr,maxRange - 1)->rect.left > vsize)
1044 if(maxRange == infoPtr->uNumItem)
1050 /* If we once had a scroll control... hide it */
1051 if (infoPtr->hwndUpDown!=0)
1052 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1054 if (infoPtr->hwndUpDown)
1055 SendMessageW(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1058 /******************************************************************************
1061 * This method will calculate the position rectangles of all the items in the
1062 * control. The rectangle calculated starts at 0 for the first item in the
1063 * list and ignores scrolling and selection.
1064 * It also uses the current font to determine the height of the tab row and
1065 * it checks if all the tabs fit in the client area of the window. If they
1066 * don't, a scrolling control is added.
1068 static void TAB_SetItemBounds (TAB_INFO *infoPtr)
1070 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1071 TEXTMETRICW fontMetrics;
1074 INT curItemRowCount;
1075 HFONT hFont, hOldFont;
1084 * We need to get text information so we need a DC and we need to select
1087 hdc = GetDC(infoPtr->hwnd);
1089 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1090 hOldFont = SelectObject (hdc, hFont);
1093 * We will base the rectangle calculations on the client rectangle
1096 GetClientRect(infoPtr->hwnd, &clientRect);
1098 /* if TCS_VERTICAL then swap the height and width so this code places the
1099 tabs along the top of the rectangle and we can just rotate them after
1100 rather than duplicate all of the below code */
1101 if(lStyle & TCS_VERTICAL)
1103 iTemp = clientRect.bottom;
1104 clientRect.bottom = clientRect.right;
1105 clientRect.right = iTemp;
1108 /* Now use hPadding and vPadding */
1109 infoPtr->uHItemPadding = infoPtr->uHItemPadding_s;
1110 infoPtr->uVItemPadding = infoPtr->uVItemPadding_s;
1112 /* The leftmost item will be "0" aligned */
1114 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1116 if (!(infoPtr->fHeightSet))
1119 int icon_height = 0;
1121 /* Use the current font to determine the height of a tab. */
1122 GetTextMetricsW(hdc, &fontMetrics);
1124 /* Get the icon height */
1126 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1128 /* Take the highest between font or icon */
1129 if (fontMetrics.tmHeight > icon_height)
1130 item_height = fontMetrics.tmHeight + 2;
1132 item_height = icon_height;
1135 * Make sure there is enough space for the letters + icon + growing the
1136 * selected item + extra space for the selected item.
1138 infoPtr->tabHeight = item_height +
1139 ((lStyle & TCS_BUTTONS) ? 2 : 1) *
1140 infoPtr->uVItemPadding;
1142 TRACE("tabH=%d, tmH=%d, iconh=%d\n",
1143 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1146 TRACE("client right=%d\n", clientRect.right);
1148 /* Get the icon width */
1151 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1153 if (lStyle & TCS_FIXEDWIDTH)
1156 /* Add padding if icon is present */
1157 icon_width += infoPtr->uHItemPadding;
1160 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1162 TAB_ITEM *curr = TAB_GetItem(infoPtr, curItem);
1164 /* Set the leftmost position of the tab. */
1165 curr->rect.left = curItemLeftPos;
1167 if (lStyle & TCS_FIXEDWIDTH)
1169 curr->rect.right = curr->rect.left +
1170 max(infoPtr->tabWidth, icon_width);
1172 else if (!curr->pszText)
1174 /* If no text use minimum tab width including padding. */
1175 if (infoPtr->tabMinWidth < 0)
1176 curr->rect.right = curr->rect.left + GET_DEFAULT_MIN_TAB_WIDTH(infoPtr);
1179 curr->rect.right = curr->rect.left + infoPtr->tabMinWidth;
1181 /* Add extra padding if icon is present */
1182 if (infoPtr->himl && infoPtr->tabMinWidth > 0 && infoPtr->tabMinWidth < DEFAULT_MIN_TAB_WIDTH
1183 && infoPtr->uHItemPadding > 1)
1184 curr->rect.right += EXTRA_ICON_PADDING * (infoPtr->uHItemPadding-1);
1191 /* Calculate how wide the tab is depending on the text it contains */
1192 GetTextExtentPoint32W(hdc, curr->pszText,
1193 lstrlenW(curr->pszText), &size);
1195 tabwidth = size.cx + icon_width + 2 * infoPtr->uHItemPadding;
1197 if (infoPtr->tabMinWidth < 0)
1198 tabwidth = max(tabwidth, GET_DEFAULT_MIN_TAB_WIDTH(infoPtr));
1200 tabwidth = max(tabwidth, infoPtr->tabMinWidth);
1202 curr->rect.right = curr->rect.left + tabwidth;
1203 TRACE("for <%s>, l,r=%d,%d\n",
1204 debugstr_w(curr->pszText), curr->rect.left, curr->rect.right);
1208 * Check if this is a multiline tab control and if so
1209 * check to see if we should wrap the tabs
1211 * Wrap all these tabs. We will arrange them evenly later.
1215 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1217 (clientRect.right - CONTROL_BORDER_SIZEX - DISPLAY_AREA_PADDINGX)))
1219 curr->rect.right -= curr->rect.left;
1221 curr->rect.left = 0;
1223 TRACE("wrapping <%s>, l,r=%d,%d\n", debugstr_w(curr->pszText),
1224 curr->rect.left, curr->rect.right);
1227 curr->rect.bottom = 0;
1228 curr->rect.top = curItemRowCount - 1;
1230 TRACE("Rect: T %i, L %i, B %i, R %i\n", curr->rect.top,
1231 curr->rect.left, curr->rect.bottom, curr->rect.right);
1234 * The leftmost position of the next item is the rightmost position
1237 if (lStyle & TCS_BUTTONS)
1239 curItemLeftPos = curr->rect.right + BUTTON_SPACINGX;
1240 if (lStyle & TCS_FLATBUTTONS)
1241 curItemLeftPos += FLAT_BTN_SPACINGX;
1244 curItemLeftPos = curr->rect.right;
1247 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1250 * Check if we need a scrolling control.
1252 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1255 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1256 if(!infoPtr->needsScrolling)
1257 infoPtr->leftmostVisible = 0;
1262 * No scrolling in Multiline or Vertical styles.
1264 infoPtr->needsScrolling = FALSE;
1265 infoPtr->leftmostVisible = 0;
1267 TAB_SetupScrolling(infoPtr->hwnd, infoPtr, &clientRect);
1269 /* Set the number of rows */
1270 infoPtr->uNumRows = curItemRowCount;
1272 /* Arrange all tabs evenly if style says so */
1273 if (!(lStyle & TCS_RAGGEDRIGHT) &&
1274 ((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1275 (infoPtr->uNumItem > 0) &&
1276 (infoPtr->uNumRows > 1))
1278 INT tabPerRow,remTab,iRow;
1283 * Ok windows tries to even out the rows. place the same
1284 * number of tabs in each row. So lets give that a shot
1287 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1288 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1290 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1291 iItm<infoPtr->uNumItem;
1294 /* normalize the current rect */
1295 TAB_ITEM *curr = TAB_GetItem(infoPtr, iItm);
1297 /* shift the item to the left side of the clientRect */
1298 curr->rect.right -= curr->rect.left;
1299 curr->rect.left = 0;
1301 TRACE("r=%d, cl=%d, cl.r=%d, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1302 curr->rect.right, curItemLeftPos, clientRect.right,
1303 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1305 /* if we have reached the maximum number of tabs on this row */
1306 /* move to the next row, reset our current item left position and */
1307 /* the count of items on this row */
1309 if (lStyle & TCS_VERTICAL) {
1310 /* Vert: Add the remaining tabs in the *last* remainder rows */
1311 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1317 /* Horz: Add the remaining tabs in the *first* remainder rows */
1318 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1325 /* shift the item to the right to place it as the next item in this row */
1326 curr->rect.left += curItemLeftPos;
1327 curr->rect.right += curItemLeftPos;
1328 curr->rect.top = iRow;
1329 if (lStyle & TCS_BUTTONS)
1331 curItemLeftPos = curr->rect.right + 1;
1332 if (lStyle & TCS_FLATBUTTONS)
1333 curItemLeftPos += FLAT_BTN_SPACINGX;
1336 curItemLeftPos = curr->rect.right;
1338 TRACE("arranging <%s>, l,r=%d,%d, row=%d\n",
1339 debugstr_w(curr->pszText), curr->rect.left,
1340 curr->rect.right, curr->rect.top);
1347 INT widthDiff, iIndexStart=0, iIndexEnd=0;
1351 while(iIndexStart < infoPtr->uNumItem)
1353 TAB_ITEM *start = TAB_GetItem(infoPtr, iIndexStart);
1356 * find the index of the row
1358 /* find the first item on the next row */
1359 for (iIndexEnd=iIndexStart;
1360 (iIndexEnd < infoPtr->uNumItem) &&
1361 (TAB_GetItem(infoPtr, iIndexEnd)->rect.top ==
1364 /* intentionally blank */;
1367 * we need to justify these tabs so they fill the whole given
1371 /* find the amount of space remaining on this row */
1372 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1373 TAB_GetItem(infoPtr, iIndexEnd - 1)->rect.right;
1375 /* iCount is the number of tab items on this row */
1376 iCount = iIndexEnd - iIndexStart;
1380 remainder = widthDiff % iCount;
1381 widthDiff = widthDiff / iCount;
1382 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1383 for (iIndex=iIndexStart, iCount=0; iIndex < iIndexEnd; iIndex++, iCount++)
1385 TAB_ITEM *item = TAB_GetItem(infoPtr, iIndex);
1387 item->rect.left += iCount * widthDiff;
1388 item->rect.right += (iCount + 1) * widthDiff;
1390 TRACE("adjusting 1 <%s>, l,r=%d,%d\n",
1391 debugstr_w(item->pszText),
1392 item->rect.left, item->rect.right);
1395 TAB_GetItem(infoPtr, iIndex - 1)->rect.right += remainder;
1397 else /* we have only one item on this row, make it take up the entire row */
1399 start->rect.left = clientRect.left;
1400 start->rect.right = clientRect.right - 4;
1402 TRACE("adjusting 2 <%s>, l,r=%d,%d\n",
1403 debugstr_w(start->pszText),
1404 start->rect.left, start->rect.right);
1409 iIndexStart = iIndexEnd;
1414 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1415 if(lStyle & TCS_VERTICAL)
1418 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1420 rcItem = &TAB_GetItem(infoPtr, iIndex)->rect;
1422 rcOriginal = *rcItem;
1424 /* this is rotating the items by 90 degrees clockwise around the center of the control */
1425 rcItem->top = (rcOriginal.left - clientRect.left);
1426 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1427 rcItem->left = rcOriginal.top;
1428 rcItem->right = rcOriginal.bottom;
1432 TAB_EnsureSelectionVisible(infoPtr);
1433 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
1436 SelectObject (hdc, hOldFont);
1437 ReleaseDC (infoPtr->hwnd, hdc);
1442 TAB_EraseTabInterior
1450 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1451 HBRUSH hbr = CreateSolidBrush (comctl32_color.clrBtnFace);
1452 BOOL deleteBrush = TRUE;
1453 RECT rTemp = *drawRect;
1455 InflateRect(&rTemp, -2, -2);
1456 if (lStyle & TCS_BUTTONS)
1458 if (iItem == infoPtr->iSelected)
1460 /* Background color */
1461 if (!(lStyle & TCS_OWNERDRAWFIXED))
1464 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1466 SetTextColor(hdc, comctl32_color.clr3dFace);
1467 SetBkColor(hdc, comctl32_color.clr3dHilight);
1469 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1470 * we better use 0x55aa bitmap brush to make scrollbar's background
1471 * look different from the window background.
1473 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1474 hbr = COMCTL32_hPattern55AABrush;
1476 deleteBrush = FALSE;
1478 FillRect(hdc, &rTemp, hbr);
1480 else /* ! selected */
1482 if (lStyle & TCS_FLATBUTTONS)
1484 FillRect(hdc, drawRect, hbr);
1485 if (iItem == infoPtr->iHotTracked)
1486 DrawEdge(hdc, drawRect, EDGE_RAISED, BF_SOFT|BF_RECT);
1489 FillRect(hdc, &rTemp, hbr);
1493 else /* !TCS_BUTTONS */
1495 if (!GetWindowTheme (infoPtr->hwnd))
1496 FillRect(hdc, &rTemp, hbr);
1500 if (deleteBrush) DeleteObject(hbr);
1503 /******************************************************************************
1504 * TAB_DrawItemInterior
1506 * This method is used to draw the interior (text and icon) of a single tab
1507 * into the tab control.
1510 TAB_DrawItemInterior
1518 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1527 /* if (drawRect == NULL) */
1534 * Get the rectangle for the item.
1536 isVisible = TAB_InternalGetItemRect(infoPtr, iItem, &itemRect, &selectedRect);
1541 * Make sure drawRect points to something valid; simplifies code.
1543 drawRect = &localRect;
1546 * This logic copied from the part of TAB_DrawItem which draws
1547 * the tab background. It's important to keep it in sync. I
1548 * would have liked to avoid code duplication, but couldn't figure
1549 * out how without making spaghetti of TAB_DrawItem.
1551 if (iItem == infoPtr->iSelected)
1552 *drawRect = selectedRect;
1554 *drawRect = itemRect;
1556 if (lStyle & TCS_BUTTONS)
1558 if (iItem == infoPtr->iSelected)
1560 drawRect->left += 4;
1562 drawRect->right -= 4;
1563 drawRect->bottom -= 1;
1567 drawRect->left += 2;
1569 drawRect->right -= 2;
1570 drawRect->bottom -= 2;
1575 if ((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1577 if (iItem != infoPtr->iSelected)
1579 drawRect->left += 2;
1581 drawRect->bottom -= 2;
1584 else if (lStyle & TCS_VERTICAL)
1586 if (iItem == infoPtr->iSelected)
1588 drawRect->right += 1;
1593 drawRect->right -= 2;
1594 drawRect->bottom -= 2;
1597 else if (lStyle & TCS_BOTTOM)
1599 if (iItem == infoPtr->iSelected)
1605 InflateRect(drawRect, -2, -2);
1606 drawRect->bottom += 2;
1611 if (iItem == infoPtr->iSelected)
1613 drawRect->bottom += 3;
1617 drawRect->bottom -= 2;
1618 InflateRect(drawRect, -2, 0);
1623 TRACE("drawRect=(%d,%d)-(%d,%d)\n",
1624 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom);
1626 /* Clear interior */
1627 TAB_EraseTabInterior (infoPtr, hdc, iItem, drawRect);
1629 /* Draw the focus rectangle */
1630 if (!(lStyle & TCS_FOCUSNEVER) &&
1631 (GetFocus() == infoPtr->hwnd) &&
1632 (iItem == infoPtr->uFocus) )
1634 RECT rFocus = *drawRect;
1635 InflateRect(&rFocus, -3, -3);
1636 if (lStyle & TCS_BOTTOM && !(lStyle & TCS_VERTICAL))
1638 if (lStyle & TCS_BUTTONS)
1644 DrawFocusRect(hdc, &rFocus);
1650 htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1651 holdPen = SelectObject(hdc, htextPen);
1652 hOldFont = SelectObject(hdc, infoPtr->hFont);
1655 * Setup for text output
1657 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1658 if (!GetWindowTheme (infoPtr->hwnd) || (lStyle & TCS_BUTTONS))
1659 SetTextColor(hdc, (((lStyle & TCS_HOTTRACK) && (iItem == infoPtr->iHotTracked)
1660 && !(lStyle & TCS_FLATBUTTONS))
1661 | (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED)) ?
1662 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1665 * if owner draw, tell the owner to draw
1667 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(infoPtr->hwnd))
1673 drawRect->right -= 1;
1674 if ( iItem == infoPtr->iSelected )
1676 drawRect->right -= 1;
1677 drawRect->left += 1;
1681 * get the control id
1683 id = (UINT)GetWindowLongPtrW( infoPtr->hwnd, GWLP_ID );
1686 * put together the DRAWITEMSTRUCT
1688 dis.CtlType = ODT_TAB;
1691 dis.itemAction = ODA_DRAWENTIRE;
1693 if ( iItem == infoPtr->iSelected )
1694 dis.itemState |= ODS_SELECTED;
1695 if (infoPtr->uFocus == iItem)
1696 dis.itemState |= ODS_FOCUS;
1697 dis.hwndItem = infoPtr->hwnd;
1699 CopyRect(&dis.rcItem,drawRect);
1700 dis.itemData = (ULONG_PTR)TAB_GetItem(infoPtr, iItem)->extra;
1703 * send the draw message
1705 SendMessageW( infoPtr->hwndNotify, WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1709 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
1713 /* used to center the icon and text in the tab */
1715 INT center_offset_h, center_offset_v;
1717 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1718 rcImage = *drawRect;
1722 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1724 /* get the rectangle that the text fits in */
1727 DrawTextW(hdc, item->pszText, -1, &rcText, DT_CALCRECT);
1730 * If not owner draw, then do the drawing ourselves.
1734 if (infoPtr->himl && item->iImage != -1)
1739 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1741 if(lStyle & TCS_VERTICAL)
1743 center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1744 center_offset_v = (drawRect->left + (drawRect->right - drawRect->left) - cx) / 2;
1748 center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1749 center_offset_v = (drawRect->top + (drawRect->bottom - drawRect->top) - cy) / 2;
1752 /* if an item is selected, the icon is shifted up instead of down */
1753 if (iItem == infoPtr->iSelected)
1754 center_offset_v -= infoPtr->uVItemPadding / 2;
1756 center_offset_v += infoPtr->uVItemPadding / 2;
1758 if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1759 center_offset_h = infoPtr->uHItemPadding;
1761 if (center_offset_h < 2)
1762 center_offset_h = 2;
1764 if (center_offset_v < 0)
1765 center_offset_v = 0;
1767 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%d,%d)-(%d,%d), textlen=%d\n",
1768 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1769 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1770 (rcText.right-rcText.left));
1772 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1774 rcImage.top = drawRect->top + center_offset_h;
1775 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1776 /* right side of the tab, but the image still uses the left as its x position */
1777 /* this keeps the image always drawn off of the same side of the tab */
1778 rcImage.left = drawRect->right - cx - center_offset_v;
1779 drawRect->top += cy + infoPtr->uHItemPadding;
1781 else if(lStyle & TCS_VERTICAL)
1783 rcImage.top = drawRect->bottom - cy - center_offset_h;
1784 rcImage.left = drawRect->left + center_offset_v;
1785 drawRect->bottom -= cy + infoPtr->uHItemPadding;
1787 else /* normal style, whether TCS_BOTTOM or not */
1789 rcImage.left = drawRect->left + center_offset_h;
1790 rcImage.top = drawRect->top + center_offset_v;
1791 drawRect->left += cx + infoPtr->uHItemPadding;
1794 TRACE("drawing image=%d, left=%d, top=%d\n",
1795 item->iImage, rcImage.left, rcImage.top-1);
1807 /* Now position text */
1808 if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT)
1809 center_offset_h = infoPtr->uHItemPadding;
1811 if(lStyle & TCS_VERTICAL)
1812 center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1814 center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1816 if(lStyle & TCS_VERTICAL)
1818 if(lStyle & TCS_BOTTOM)
1819 drawRect->top+=center_offset_h;
1821 drawRect->bottom-=center_offset_h;
1823 center_offset_v = ((drawRect->right - drawRect->left) - (rcText.bottom - rcText.top)) / 2;
1827 drawRect->left += center_offset_h;
1828 center_offset_v = ((drawRect->bottom - drawRect->top) - (rcText.bottom - rcText.top)) / 2;
1831 /* if an item is selected, the text is shifted up instead of down */
1832 if (iItem == infoPtr->iSelected)
1833 center_offset_v -= infoPtr->uVItemPadding / 2;
1835 center_offset_v += infoPtr->uVItemPadding / 2;
1837 if (center_offset_v < 0)
1838 center_offset_v = 0;
1840 if(lStyle & TCS_VERTICAL)
1841 drawRect->left += center_offset_v;
1843 drawRect->top += center_offset_v;
1846 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1848 static const WCHAR ArialW[] = { 'A','r','i','a','l',0 };
1851 INT nEscapement = 900;
1852 INT nOrientation = 900;
1854 if(lStyle & TCS_BOTTOM)
1857 nOrientation = -900;
1860 /* to get a font with the escapement and orientation we are looking for, we need to */
1861 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1862 if (!GetObjectW((infoPtr->hFont) ?
1863 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1864 sizeof(LOGFONTW),&logfont))
1868 lstrcpyW(logfont.lfFaceName, ArialW);
1869 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1871 logfont.lfWeight = FW_NORMAL;
1872 logfont.lfItalic = 0;
1873 logfont.lfUnderline = 0;
1874 logfont.lfStrikeOut = 0;
1877 logfont.lfEscapement = nEscapement;
1878 logfont.lfOrientation = nOrientation;
1879 hFont = CreateFontIndirectW(&logfont);
1880 SelectObject(hdc, hFont);
1885 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1886 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1890 lstrlenW(item->pszText),
1894 DeleteObject(hFont);
1898 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%d,%d)-(%d,%d), textlen=%d\n",
1899 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1900 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1901 (rcText.right-rcText.left));
1908 lstrlenW(item->pszText),
1910 DT_LEFT | DT_SINGLELINE
1915 *drawRect = rcTemp; /* restore drawRect */
1921 SelectObject(hdc, hOldFont);
1922 SetBkMode(hdc, oldBkMode);
1923 SelectObject(hdc, holdPen);
1924 DeleteObject( htextPen );
1927 /******************************************************************************
1930 * This method is used to draw a single tab into the tab control.
1932 static void TAB_DrawItem(
1937 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1941 RECT r, fillRect, r1;
1944 COLORREF bkgnd, corner;
1948 * Get the rectangle for the item.
1950 isVisible = TAB_InternalGetItemRect(infoPtr,
1959 /* Clip UpDown control to not draw over it */
1960 if (infoPtr->needsScrolling)
1962 GetWindowRect(infoPtr->hwnd, &rC);
1963 GetWindowRect(infoPtr->hwndUpDown, &rUD);
1964 ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
1967 /* If you need to see what the control is doing,
1968 * then override these variables. They will change what
1969 * fill colors are used for filling the tabs, and the
1970 * corners when drawing the edge.
1972 bkgnd = comctl32_color.clrBtnFace;
1973 corner = comctl32_color.clrBtnFace;
1975 if (lStyle & TCS_BUTTONS)
1977 /* Get item rectangle */
1980 /* Separators between flat buttons */
1981 if (lStyle & TCS_FLATBUTTONS)
1984 r1.right += (FLAT_BTN_SPACINGX -2);
1985 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1988 if (iItem == infoPtr->iSelected)
1990 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1992 OffsetRect(&r, 1, 1);
1994 else /* ! selected */
1996 if (!(lStyle & TCS_FLATBUTTONS))
1997 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
2000 else /* !TCS_BUTTONS */
2002 /* We draw a rectangle of different sizes depending on the selection
2004 if (iItem == infoPtr->iSelected) {
2006 GetClientRect (infoPtr->hwnd, &rect);
2007 clRight = rect.right;
2008 clBottom = rect.bottom;
2015 * Erase the background. (Delay it but setup rectangle.)
2016 * This is necessary when drawing the selected item since it is larger
2017 * than the others, it might overlap with stuff already drawn by the
2022 /* Draw themed tabs - but only if they are at the top.
2023 * Windows draws even side or bottom tabs themed, with wacky results.
2024 * However, since in Wine apps may get themed that did not opt in via
2025 * a manifest avoid theming when we know the result will be wrong */
2026 if ((theme = GetWindowTheme (infoPtr->hwnd))
2027 && ((lStyle & (TCS_VERTICAL | TCS_BOTTOM)) == 0))
2029 static const int partIds[8] = {
2032 TABP_TABITEMLEFTEDGE,
2033 TABP_TABITEMRIGHTEDGE,
2034 TABP_TABITEMBOTHEDGE,
2037 TABP_TOPTABITEMLEFTEDGE,
2038 TABP_TOPTABITEMRIGHTEDGE,
2039 TABP_TOPTABITEMBOTHEDGE,
2042 int stateId = TIS_NORMAL;
2044 /* selected and unselected tabs have different parts */
2045 if (iItem == infoPtr->iSelected)
2047 /* The part also differs on the position of a tab on a line.
2048 * "Visually" determining the position works well enough. */
2049 if(selectedRect.left == 0)
2051 if(selectedRect.right == clRight)
2054 if (iItem == infoPtr->iSelected)
2055 stateId = TIS_SELECTED;
2056 else if (iItem == infoPtr->iHotTracked)
2058 else if (iItem == infoPtr->uFocus)
2059 stateId = TIS_FOCUSED;
2061 /* Adjust rectangle for bottommost row */
2062 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2065 DrawThemeBackground (theme, hdc, partIds[partIndex], stateId, &r, NULL);
2066 GetThemeBackgroundContentRect (theme, hdc, partIds[partIndex], stateId, &r, &r);
2068 else if(lStyle & TCS_VERTICAL)
2070 /* These are for adjusting the drawing of a Selected tab */
2071 /* The initial values are for the normal case of non-Selected */
2072 int ZZ = 1; /* Do not strech if selected */
2073 if (iItem == infoPtr->iSelected) {
2076 /* if leftmost draw the line longer */
2077 if(selectedRect.top == 0)
2078 fillRect.top += CONTROL_BORDER_SIZEY;
2079 /* if rightmost draw the line longer */
2080 if(selectedRect.bottom == clBottom)
2081 fillRect.bottom -= CONTROL_BORDER_SIZEY;
2084 if (lStyle & TCS_BOTTOM)
2086 /* Adjust both rectangles to match native */
2089 TRACE("<right> item=%d, fill=(%d,%d)-(%d,%d), edge=(%d,%d)-(%d,%d)\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_RIGHT|BF_TOP|BF_BOTTOM);
2101 /* Now erase the top corner and draw diagonal edge */
2102 SetBkColor(hdc, corner);
2103 r1.left = r.right - 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_ENDTOPLEFT);
2111 /* Now erase the bottom corner and draw diagonal edge */
2112 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2113 r1.bottom = r.bottom;
2115 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2116 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2118 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2120 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
2124 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
2130 TRACE("<left> item=%d, fill=(%d,%d)-(%d,%d), edge=(%d,%d)-(%d,%d)\n",
2132 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2133 r.left,r.top,r.right,r.bottom);
2135 /* Clear interior */
2136 SetBkColor(hdc, bkgnd);
2137 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2139 /* Draw rectangular edge around tab */
2140 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2142 /* Now erase the top corner and draw diagonal edge */
2143 SetBkColor(hdc, corner);
2146 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2147 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2148 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2150 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2152 /* Now erase the bottom corner and draw diagonal edge */
2154 r1.bottom = r.bottom;
2155 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2156 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2157 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2159 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2162 else /* ! TCS_VERTICAL */
2164 /* These are for adjusting the drawing of a Selected tab */
2165 /* The initial values are for the normal case of non-Selected */
2166 if (iItem == infoPtr->iSelected) {
2167 /* if leftmost draw the line longer */
2168 if(selectedRect.left == 0)
2169 fillRect.left += CONTROL_BORDER_SIZEX;
2170 /* if rightmost draw the line longer */
2171 if(selectedRect.right == clRight)
2172 fillRect.right -= CONTROL_BORDER_SIZEX;
2175 if (lStyle & TCS_BOTTOM)
2177 /* Adjust both rectangles for topmost row */
2178 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2184 TRACE("<bottom> item=%d, fill=(%d,%d)-(%d,%d), edge=(%d,%d)-(%d,%d)\n",
2186 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2187 r.left,r.top,r.right,r.bottom);
2189 /* Clear interior */
2190 SetBkColor(hdc, bkgnd);
2191 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2193 /* Draw rectangular edge around tab */
2194 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2196 /* Now erase the righthand corner and draw diagonal edge */
2197 SetBkColor(hdc, corner);
2198 r1.left = r.right - ROUND_CORNER_SIZE;
2199 r1.bottom = r.bottom;
2201 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2202 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2204 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2206 /* Now erase the lefthand corner and draw diagonal edge */
2208 r1.bottom = r.bottom;
2209 r1.right = r1.left + ROUND_CORNER_SIZE;
2210 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2211 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2213 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2215 if (iItem == infoPtr->iSelected)
2219 if (selectedRect.left == 0)
2224 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2231 /* Adjust both rectangles for bottommost row */
2232 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2234 fillRect.bottom += 3;
2238 TRACE("<top> item=%d, fill=(%d,%d)-(%d,%d), edge=(%d,%d)-(%d,%d)\n",
2240 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2241 r.left,r.top,r.right,r.bottom);
2243 /* Clear interior */
2244 SetBkColor(hdc, bkgnd);
2245 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2247 /* Draw rectangular edge around tab */
2248 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2250 /* Now erase the righthand corner and draw diagonal edge */
2251 SetBkColor(hdc, corner);
2252 r1.left = r.right - ROUND_CORNER_SIZE;
2255 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2256 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2258 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2260 /* Now erase the lefthand corner and draw diagonal edge */
2263 r1.right = r1.left + ROUND_CORNER_SIZE;
2264 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2265 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2267 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2272 TAB_DumpItemInternal(infoPtr, iItem);
2274 /* This modifies r to be the text rectangle. */
2275 TAB_DrawItemInterior(infoPtr, hdc, iItem, &r);
2279 /******************************************************************************
2282 * This method is used to draw the raised border around the tab control
2285 static void TAB_DrawBorder (TAB_INFO *infoPtr, HDC hdc)
2288 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2289 HTHEME theme = GetWindowTheme (infoPtr->hwnd);
2291 GetClientRect (infoPtr->hwnd, &rect);
2294 * Adjust for the style
2297 if (infoPtr->uNumItem)
2299 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2300 rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2301 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2302 rect.right -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2303 else if(lStyle & TCS_VERTICAL)
2304 rect.left += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2305 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2306 rect.top += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2309 TRACE("border=(%d,%d)-(%d,%d)\n",
2310 rect.left, rect.top, rect.right, rect.bottom);
2313 DrawThemeBackground (theme, hdc, TABP_PANE, 0, &rect, NULL);
2315 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2318 /******************************************************************************
2321 * This method repaints the tab control..
2323 static void TAB_Refresh (TAB_INFO *infoPtr, HDC hdc)
2328 if (!infoPtr->DoRedraw)
2331 hOldFont = SelectObject (hdc, infoPtr->hFont);
2333 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS)
2335 for (i = 0; i < infoPtr->uNumItem; i++)
2336 TAB_DrawItem (infoPtr, hdc, i);
2340 /* Draw all the non selected item first */
2341 for (i = 0; i < infoPtr->uNumItem; i++)
2343 if (i != infoPtr->iSelected)
2344 TAB_DrawItem (infoPtr, hdc, i);
2347 /* Now, draw the border, draw it before the selected item
2348 * since the selected item overwrites part of the border. */
2349 TAB_DrawBorder (infoPtr, hdc);
2351 /* Then, draw the selected item */
2352 TAB_DrawItem (infoPtr, hdc, infoPtr->iSelected);
2354 /* If we haven't set the current focus yet, set it now.
2355 * Only happens when we first paint the tab controls */
2356 if (infoPtr->uFocus == -1)
2357 TAB_SetCurFocus(infoPtr, infoPtr->iSelected);
2360 SelectObject (hdc, hOldFont);
2363 static inline DWORD TAB_GetRowCount (const TAB_INFO *infoPtr)
2365 return infoPtr->uNumRows;
2368 static inline LRESULT TAB_SetRedraw (TAB_INFO *infoPtr, BOOL doRedraw)
2370 infoPtr->DoRedraw = doRedraw;
2374 /******************************************************************************
2375 * TAB_EnsureSelectionVisible
2377 * This method will make sure that the current selection is completely
2378 * visible by scrolling until it is.
2380 static void TAB_EnsureSelectionVisible(
2383 INT iSelected = infoPtr->iSelected;
2384 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2385 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2387 /* set the items row to the bottommost row or topmost row depending on
2389 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2391 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2395 if(lStyle & TCS_VERTICAL)
2396 newselected = selected->rect.left;
2398 newselected = selected->rect.top;
2400 /* the target row is always (number of rows - 1)
2401 as row 0 is furthest from the clientRect */
2402 iTargetRow = infoPtr->uNumRows - 1;
2404 if (newselected != iTargetRow)
2407 if(lStyle & TCS_VERTICAL)
2409 for (i=0; i < infoPtr->uNumItem; i++)
2411 /* move everything in the row of the selected item to the iTargetRow */
2412 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2414 if (item->rect.left == newselected )
2415 item->rect.left = iTargetRow;
2418 if (item->rect.left > newselected)
2425 for (i=0; i < infoPtr->uNumItem; i++)
2427 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2429 if (item->rect.top == newselected )
2430 item->rect.top = iTargetRow;
2433 if (item->rect.top > newselected)
2438 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2443 * Do the trivial cases first.
2445 if ( (!infoPtr->needsScrolling) ||
2446 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2449 if (infoPtr->leftmostVisible >= iSelected)
2451 infoPtr->leftmostVisible = iSelected;
2455 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2460 /* Calculate the part of the client area that is visible */
2461 GetClientRect(infoPtr->hwnd, &r);
2464 GetClientRect(infoPtr->hwndUpDown, &r);
2467 if ((selected->rect.right -
2468 selected->rect.left) >= width )
2470 /* Special case: width of selected item is greater than visible
2473 infoPtr->leftmostVisible = iSelected;
2477 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2479 if ((selected->rect.right - TAB_GetItem(infoPtr, i)->rect.left) < width)
2482 infoPtr->leftmostVisible = i;
2486 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2487 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2489 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2490 MAKELONG(infoPtr->leftmostVisible, 0));
2493 /******************************************************************************
2494 * TAB_InvalidateTabArea
2496 * This method will invalidate the portion of the control that contains the
2497 * tabs. It is called when the state of the control changes and needs
2500 static void TAB_InvalidateTabArea(TAB_INFO* infoPtr)
2502 RECT clientRect, rInvalidate, rAdjClient;
2503 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2504 INT lastRow = infoPtr->uNumRows - 1;
2507 if (lastRow < 0) return;
2509 GetClientRect(infoPtr->hwnd, &clientRect);
2510 rInvalidate = clientRect;
2511 rAdjClient = clientRect;
2513 TAB_AdjustRect(infoPtr, 0, &rAdjClient);
2515 TAB_InternalGetItemRect(infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2516 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2518 rInvalidate.left = rAdjClient.right;
2519 if (infoPtr->uNumRows == 1)
2520 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2522 else if(lStyle & TCS_VERTICAL)
2524 rInvalidate.right = rAdjClient.left;
2525 if (infoPtr->uNumRows == 1)
2526 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2528 else if (lStyle & TCS_BOTTOM)
2530 rInvalidate.top = rAdjClient.bottom;
2531 if (infoPtr->uNumRows == 1)
2532 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2536 rInvalidate.bottom = rAdjClient.top;
2537 if (infoPtr->uNumRows == 1)
2538 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2541 /* Punch out the updown control */
2542 if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2544 GetClientRect(infoPtr->hwndUpDown, &r);
2545 if (rInvalidate.right > clientRect.right - r.left)
2546 rInvalidate.right = rInvalidate.right - (r.right - r.left);
2548 rInvalidate.right = clientRect.right - r.left;
2551 TRACE("invalidate (%d,%d)-(%d,%d)\n",
2552 rInvalidate.left, rInvalidate.top,
2553 rInvalidate.right, rInvalidate.bottom);
2555 InvalidateRect(infoPtr->hwnd, &rInvalidate, TRUE);
2558 static inline LRESULT TAB_Paint (TAB_INFO *infoPtr, HDC hdcPaint)
2567 hdc = BeginPaint (infoPtr->hwnd, &ps);
2568 TRACE("erase %d, rect=(%d,%d)-(%d,%d)\n",
2570 ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2573 TAB_Refresh (infoPtr, hdc);
2576 EndPaint (infoPtr->hwnd, &ps);
2582 TAB_InsertItemT (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2589 GetClientRect (infoPtr->hwnd, &rect);
2590 TRACE("Rect: %p T %i, L %i, B %i, R %i\n", infoPtr->hwnd,
2591 rect.top, rect.left, rect.bottom, rect.right);
2593 pti = (TCITEMW *)lParam;
2594 iItem = (INT)wParam;
2596 if (iItem < 0) return -1;
2597 if (iItem > infoPtr->uNumItem)
2598 iItem = infoPtr->uNumItem;
2600 TAB_DumpItemExternalT(pti, iItem, bUnicode);
2603 if (infoPtr->uNumItem == 0) {
2604 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2605 infoPtr->uNumItem++;
2606 infoPtr->iSelected = 0;
2609 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2611 infoPtr->uNumItem++;
2612 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2614 /* pre insert copy */
2616 memcpy (infoPtr->items, oldItems,
2617 iItem * TAB_ITEM_SIZE(infoPtr));
2620 /* post insert copy */
2621 if (iItem < infoPtr->uNumItem - 1) {
2622 memcpy (TAB_GetItem(infoPtr, iItem + 1),
2623 oldItems + iItem * TAB_ITEM_SIZE(infoPtr),
2624 (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2628 if (iItem <= infoPtr->iSelected)
2629 infoPtr->iSelected++;
2634 item = TAB_GetItem(infoPtr, iItem);
2636 item->pszText = NULL;
2638 if (pti->mask & TCIF_TEXT)
2641 Str_SetPtrW (&item->pszText, pti->pszText);
2643 Str_SetPtrAtoW (&item->pszText, (LPSTR)pti->pszText);
2646 if (pti->mask & TCIF_IMAGE)
2647 item->iImage = pti->iImage;
2651 if (pti->mask & TCIF_PARAM)
2652 memcpy(item->extra, &pti->lParam, infoPtr->cbInfo);
2654 memset(item->extra, 0, infoPtr->cbInfo);
2656 TAB_SetItemBounds(infoPtr);
2657 if (infoPtr->uNumItem > 1)
2658 TAB_InvalidateTabArea(infoPtr);
2660 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2662 TRACE("[%p]: added item %d %s\n",
2663 infoPtr->hwnd, iItem, debugstr_w(item->pszText));
2669 TAB_SetItemSize (TAB_INFO *infoPtr, LPARAM lParam)
2671 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2673 BOOL bNeedPaint = FALSE;
2675 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2677 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2678 if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2680 infoPtr->tabWidth = (INT)LOWORD(lParam);
2684 if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2686 if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2687 infoPtr->tabHeight = (INT)HIWORD(lParam);
2691 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2692 HIWORD(lResult), LOWORD(lResult),
2693 infoPtr->tabHeight, infoPtr->tabWidth);
2697 TAB_SetItemBounds(infoPtr);
2698 RedrawWindow(infoPtr->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2704 static inline LRESULT TAB_SetMinTabWidth (TAB_INFO *infoPtr, INT cx)
2708 TRACE("(%p,%d)\n", infoPtr, cx);
2711 oldcx = infoPtr->tabMinWidth;
2712 infoPtr->tabMinWidth = cx;
2714 TAB_SetItemBounds(infoPtr);
2719 static inline LRESULT
2720 TAB_HighlightItem (TAB_INFO *infoPtr, INT iItem, BOOL fHighlight)
2724 TRACE("(%p,%d,%s)\n", infoPtr, iItem, fHighlight ? "true" : "false");
2726 if (!infoPtr || iItem < 0 || iItem >= infoPtr->uNumItem)
2729 lpState = &TAB_GetItem(infoPtr, iItem)->dwState;
2732 *lpState |= TCIS_HIGHLIGHTED;
2734 *lpState &= ~TCIS_HIGHLIGHTED;
2740 TAB_SetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2744 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2746 if (iItem < 0 || iItem >= infoPtr->uNumItem)
2749 TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2751 wineItem = TAB_GetItem(infoPtr, iItem);
2753 if (tabItem->mask & TCIF_IMAGE)
2754 wineItem->iImage = tabItem->iImage;
2756 if (tabItem->mask & TCIF_PARAM)
2757 memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2759 if (tabItem->mask & TCIF_RTLREADING)
2760 FIXME("TCIF_RTLREADING\n");
2762 if (tabItem->mask & TCIF_STATE)
2763 wineItem->dwState = tabItem->dwState;
2765 if (tabItem->mask & TCIF_TEXT)
2767 Free(wineItem->pszText);
2768 wineItem->pszText = NULL;
2770 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2772 Str_SetPtrAtoW(&wineItem->pszText, (LPSTR)tabItem->pszText);
2775 /* Update and repaint tabs */
2776 TAB_SetItemBounds(infoPtr);
2777 TAB_InvalidateTabArea(infoPtr);
2782 static inline LRESULT TAB_GetItemCount (const TAB_INFO *infoPtr)
2784 return infoPtr->uNumItem;
2789 TAB_GetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2793 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2795 if (iItem < 0 || iItem >= infoPtr->uNumItem)
2798 wineItem = TAB_GetItem(infoPtr, iItem);
2800 if (tabItem->mask & TCIF_IMAGE)
2801 tabItem->iImage = wineItem->iImage;
2803 if (tabItem->mask & TCIF_PARAM)
2804 memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2806 if (tabItem->mask & TCIF_RTLREADING)
2807 FIXME("TCIF_RTLREADING\n");
2809 if (tabItem->mask & TCIF_STATE)
2810 tabItem->dwState = wineItem->dwState;
2812 if (tabItem->mask & TCIF_TEXT)
2815 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2817 Str_GetPtrWtoA (wineItem->pszText, (LPSTR)tabItem->pszText, tabItem->cchTextMax);
2820 TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2826 static LRESULT TAB_DeleteItem (TAB_INFO *infoPtr, INT iItem)
2828 BOOL bResult = FALSE;
2830 TRACE("(%p, %d)\n", infoPtr, iItem);
2832 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2834 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
2835 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2837 TAB_InvalidateTabArea(infoPtr);
2838 Free(item->pszText);
2839 infoPtr->uNumItem--;
2841 if (!infoPtr->uNumItem)
2843 infoPtr->items = NULL;
2844 if (infoPtr->iHotTracked >= 0)
2846 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
2847 infoPtr->iHotTracked = -1;
2852 infoPtr->items = Alloc(TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2855 memcpy(infoPtr->items, oldItems, iItem * TAB_ITEM_SIZE(infoPtr));
2857 if (iItem < infoPtr->uNumItem)
2858 memcpy(TAB_GetItem(infoPtr, iItem),
2859 oldItems + (iItem + 1) * TAB_ITEM_SIZE(infoPtr),
2860 (infoPtr->uNumItem - iItem) * TAB_ITEM_SIZE(infoPtr));
2862 if (iItem <= infoPtr->iHotTracked)
2864 /* When tabs move left/up, the hot track item may change */
2865 FIXME("Recalc hot track\n");
2870 /* Readjust the selected index */
2871 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2872 infoPtr->iSelected--;
2874 if (iItem < infoPtr->iSelected)
2875 infoPtr->iSelected--;
2877 if (infoPtr->uNumItem == 0)
2878 infoPtr->iSelected = -1;
2880 /* Reposition and repaint tabs */
2881 TAB_SetItemBounds(infoPtr);
2889 static inline LRESULT TAB_DeleteAllItems (TAB_INFO *infoPtr)
2891 TRACE("(%p)\n", infoPtr);
2892 while (infoPtr->uNumItem)
2893 TAB_DeleteItem (infoPtr, 0);
2898 static inline LRESULT TAB_GetFont (const TAB_INFO *infoPtr)
2900 TRACE("(%p) returning %p\n", infoPtr, infoPtr->hFont);
2901 return (LRESULT)infoPtr->hFont;
2904 static inline LRESULT TAB_SetFont (TAB_INFO *infoPtr, HFONT hNewFont)
2906 TRACE("(%p,%p)\n", infoPtr, hNewFont);
2908 infoPtr->hFont = hNewFont;
2910 TAB_SetItemBounds(infoPtr);
2912 TAB_InvalidateTabArea(infoPtr);
2918 static inline LRESULT TAB_GetImageList (const TAB_INFO *infoPtr)
2921 return (LRESULT)infoPtr->himl;
2924 static inline LRESULT TAB_SetImageList (TAB_INFO *infoPtr, HIMAGELIST himlNew)
2926 HIMAGELIST himlPrev = infoPtr->himl;
2928 infoPtr->himl = himlNew;
2929 TAB_SetItemBounds(infoPtr);
2930 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2931 return (LRESULT)himlPrev;
2934 static inline LRESULT TAB_GetUnicodeFormat (const TAB_INFO *infoPtr)
2936 return infoPtr->bUnicode;
2939 static inline LRESULT TAB_SetUnicodeFormat (TAB_INFO *infoPtr, BOOL bUnicode)
2941 BOOL bTemp = infoPtr->bUnicode;
2943 infoPtr->bUnicode = bUnicode;
2948 static inline LRESULT TAB_Size (TAB_INFO *infoPtr)
2950 /* I'm not really sure what the following code was meant to do.
2951 This is what it is doing:
2952 When WM_SIZE is sent with SIZE_RESTORED, the control
2953 gets positioned in the top left corner.
2957 UINT uPosFlags,cx,cy;
2961 parent = GetParent (hwnd);
2962 GetClientRect(parent, &parent_rect);
2965 if (GetWindowLongW(hwnd, GWL_STYLE) & CCS_NORESIZE)
2966 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2968 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2969 cx, cy, uPosFlags | SWP_NOZORDER);
2971 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2974 /* Recompute the size/position of the tabs. */
2975 TAB_SetItemBounds (infoPtr);
2977 /* Force a repaint of the control. */
2978 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2984 static LRESULT TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2987 TEXTMETRICW fontMetrics;
2992 infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
2994 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
2996 infoPtr->hwnd = hwnd;
2997 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
2998 infoPtr->uNumItem = 0;
2999 infoPtr->uNumRows = 0;
3000 infoPtr->uHItemPadding = 6;
3001 infoPtr->uVItemPadding = 3;
3002 infoPtr->uHItemPadding_s = 6;
3003 infoPtr->uVItemPadding_s = 3;
3006 infoPtr->hcurArrow = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3007 infoPtr->iSelected = -1;
3008 infoPtr->iHotTracked = -1;
3009 infoPtr->uFocus = -1;
3010 infoPtr->hwndToolTip = 0;
3011 infoPtr->DoRedraw = TRUE;
3012 infoPtr->needsScrolling = FALSE;
3013 infoPtr->hwndUpDown = 0;
3014 infoPtr->leftmostVisible = 0;
3015 infoPtr->fHeightSet = FALSE;
3016 infoPtr->bUnicode = IsWindowUnicode (hwnd);
3017 infoPtr->cbInfo = sizeof(LPARAM);
3019 TRACE("Created tab control, hwnd [%p]\n", hwnd);
3021 /* The tab control always has the WS_CLIPSIBLINGS style. Even
3022 if you don't specify it in CreateWindow. This is necessary in
3023 order for paint to work correctly. This follows windows behaviour. */
3024 dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
3025 SetWindowLongW(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
3027 if (dwStyle & TCS_TOOLTIPS) {
3028 /* Create tooltip control */
3029 infoPtr->hwndToolTip =
3030 CreateWindowExW (0, TOOLTIPS_CLASSW, NULL, WS_POPUP,
3031 CW_USEDEFAULT, CW_USEDEFAULT,
3032 CW_USEDEFAULT, CW_USEDEFAULT,
3035 /* Send NM_TOOLTIPSCREATED notification */
3036 if (infoPtr->hwndToolTip) {
3037 NMTOOLTIPSCREATED nmttc;
3039 nmttc.hdr.hwndFrom = hwnd;
3040 nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
3041 nmttc.hdr.code = NM_TOOLTIPSCREATED;
3042 nmttc.hwndToolTips = infoPtr->hwndToolTip;
3044 SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
3045 (WPARAM)GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc);
3049 OpenThemeData (infoPtr->hwnd, themeClass);
3052 * We need to get text information so we need a DC and we need to select
3056 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3058 /* Use the system font to determine the initial height of a tab. */
3059 GetTextMetricsW(hdc, &fontMetrics);
3062 * Make sure there is enough space for the letters + growing the
3063 * selected item + extra space for the selected item.
3065 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3066 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
3067 infoPtr->uVItemPadding;
3069 /* Initialize the width of a tab. */
3070 if (dwStyle & TCS_FIXEDWIDTH)
3071 infoPtr->tabWidth = DEFAULT_TAB_WIDTH_FIXED;
3073 infoPtr->tabMinWidth = -1;
3075 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3077 SelectObject (hdc, hOldFont);
3078 ReleaseDC(hwnd, hdc);
3084 TAB_Destroy (TAB_INFO *infoPtr)
3091 SetWindowLongPtrW(infoPtr->hwnd, 0, 0);
3093 if (infoPtr->items) {
3094 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3095 if (TAB_GetItem(infoPtr, iItem)->pszText)
3096 Free (TAB_GetItem(infoPtr, iItem)->pszText);
3098 Free (infoPtr->items);
3101 if (infoPtr->hwndToolTip)
3102 DestroyWindow (infoPtr->hwndToolTip);
3104 if (infoPtr->hwndUpDown)
3105 DestroyWindow(infoPtr->hwndUpDown);
3107 if (infoPtr->iHotTracked >= 0)
3108 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
3110 CloseThemeData (GetWindowTheme (infoPtr->hwnd));
3116 /* update theme after a WM_THEMECHANGED message */
3117 static LRESULT theme_changed (TAB_INFO* infoPtr)
3119 HTHEME theme = GetWindowTheme (infoPtr->hwnd);
3120 CloseThemeData (theme);
3121 OpenThemeData (infoPtr->hwnd, themeClass);
3125 static LRESULT TAB_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
3129 return WVR_ALIGNTOP;
3132 static inline LRESULT
3133 TAB_SetItemExtra (TAB_INFO *infoPtr, INT cbInfo)
3135 if (!infoPtr || cbInfo <= 0)
3138 if (infoPtr->uNumItem)
3140 /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
3144 infoPtr->cbInfo = cbInfo;
3148 static LRESULT WINAPI
3149 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3151 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3153 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3154 if (!infoPtr && (uMsg != WM_CREATE))
3155 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
3159 case TCM_GETIMAGELIST:
3160 return TAB_GetImageList (infoPtr);
3162 case TCM_SETIMAGELIST:
3163 return TAB_SetImageList (infoPtr, (HIMAGELIST)lParam);
3165 case TCM_GETITEMCOUNT:
3166 return TAB_GetItemCount (infoPtr);
3170 return TAB_GetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_GETITEMW);
3174 return TAB_SetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_SETITEMW);
3176 case TCM_DELETEITEM:
3177 return TAB_DeleteItem (infoPtr, (INT)wParam);
3179 case TCM_DELETEALLITEMS:
3180 return TAB_DeleteAllItems (infoPtr);
3182 case TCM_GETITEMRECT:
3183 return TAB_GetItemRect (infoPtr, wParam, lParam);
3186 return TAB_GetCurSel (infoPtr);
3189 return TAB_HitTest (infoPtr, (LPTCHITTESTINFO)lParam);
3192 return TAB_SetCurSel (infoPtr, (INT)wParam);
3194 case TCM_INSERTITEMA:
3195 case TCM_INSERTITEMW:
3196 return TAB_InsertItemT (infoPtr, wParam, lParam, uMsg == TCM_INSERTITEMW);
3198 case TCM_SETITEMEXTRA:
3199 return TAB_SetItemExtra (infoPtr, (int)wParam);
3201 case TCM_ADJUSTRECT:
3202 return TAB_AdjustRect (infoPtr, (BOOL)wParam, (LPRECT)lParam);
3204 case TCM_SETITEMSIZE:
3205 return TAB_SetItemSize (infoPtr, lParam);
3207 case TCM_REMOVEIMAGE:
3208 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3211 case TCM_SETPADDING:
3212 return TAB_SetPadding (infoPtr, lParam);
3214 case TCM_GETROWCOUNT:
3215 return TAB_GetRowCount(infoPtr);
3217 case TCM_GETUNICODEFORMAT:
3218 return TAB_GetUnicodeFormat (infoPtr);
3220 case TCM_SETUNICODEFORMAT:
3221 return TAB_SetUnicodeFormat (infoPtr, (BOOL)wParam);
3223 case TCM_HIGHLIGHTITEM:
3224 return TAB_HighlightItem (infoPtr, (INT)wParam, (BOOL)LOWORD(lParam));
3226 case TCM_GETTOOLTIPS:
3227 return TAB_GetToolTips (infoPtr);
3229 case TCM_SETTOOLTIPS:
3230 return TAB_SetToolTips (infoPtr, (HWND)wParam);
3232 case TCM_GETCURFOCUS:
3233 return TAB_GetCurFocus (infoPtr);
3235 case TCM_SETCURFOCUS:
3236 return TAB_SetCurFocus (infoPtr, (INT)wParam);
3238 case TCM_SETMINTABWIDTH:
3239 return TAB_SetMinTabWidth(infoPtr, (INT)lParam);
3241 case TCM_DESELECTALL:
3242 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3245 case TCM_GETEXTENDEDSTYLE:
3246 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3249 case TCM_SETEXTENDEDSTYLE:
3250 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3254 return TAB_GetFont (infoPtr);
3257 return TAB_SetFont (infoPtr, (HFONT)wParam);
3260 return TAB_Create (hwnd, wParam, lParam);
3263 return TAB_Destroy (infoPtr);
3266 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3268 case WM_LBUTTONDOWN:
3269 return TAB_LButtonDown (infoPtr, wParam, lParam);
3272 return TAB_LButtonUp (infoPtr);
3275 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3277 case WM_RBUTTONDOWN:
3278 return TAB_RButtonDown (infoPtr);
3281 return TAB_MouseMove (infoPtr, wParam, lParam);
3283 case WM_PRINTCLIENT:
3285 return TAB_Paint (infoPtr, (HDC)wParam);
3288 return TAB_Size (infoPtr);
3291 return TAB_SetRedraw (infoPtr, (BOOL)wParam);
3294 return TAB_OnHScroll(infoPtr, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3296 case WM_STYLECHANGED:
3297 TAB_SetItemBounds (infoPtr);
3298 InvalidateRect(hwnd, NULL, TRUE);
3301 case WM_SYSCOLORCHANGE:
3302 COMCTL32_RefreshSysColors();
3305 case WM_THEMECHANGED:
3306 return theme_changed (infoPtr);
3310 TAB_FocusChanging(infoPtr);
3311 break; /* Don't disturb normal focus behavior */
3314 return TAB_KeyUp(infoPtr, wParam);
3316 return TAB_NCHitTest(infoPtr, lParam);
3319 return TAB_NCCalcSize(hwnd, wParam, lParam);
3322 if (uMsg >= WM_USER && uMsg < WM_APP)
3323 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3324 uMsg, wParam, lParam);
3327 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
3336 ZeroMemory (&wndClass, sizeof(WNDCLASSW));
3337 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3338 wndClass.lpfnWndProc = TAB_WindowProc;
3339 wndClass.cbClsExtra = 0;
3340 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3341 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3342 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
3343 wndClass.lpszClassName = WC_TABCONTROLW;
3345 RegisterClassW (&wndClass);
3350 TAB_Unregister (void)
3352 UnregisterClassW (WC_TABCONTROLW, NULL);