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
76 #include "wine/debug.h"
79 WINE_DEFAULT_DEBUG_CHANNEL(tab);
87 RECT rect; /* bounding rectangle of the item relative to the
88 * leftmost item (the leftmost item, 0, would have a
89 * "left" member of 0 in this rectangle)
91 * additionally the top member holds the row number
92 * and bottom is unused and should be 0 */
93 BYTE extra[1]; /* Space for caller supplied info, variable size */
96 /* The size of a tab item depends on how much extra data is requested */
97 #define TAB_ITEM_SIZE(infoPtr) (sizeof(TAB_ITEM) - sizeof(BYTE) + infoPtr->cbInfo)
101 HWND hwnd; /* Tab control window */
102 HWND hwndNotify; /* notification window (parent) */
103 UINT uNumItem; /* number of tab items */
104 UINT uNumRows; /* number of tab rows */
105 INT tabHeight; /* height of the tab row */
106 INT tabWidth; /* width of tabs */
107 INT tabMinWidth; /* minimum width of items */
108 USHORT uHItemPadding; /* amount of horizontal padding, in pixels */
109 USHORT uVItemPadding; /* amount of vertical padding, in pixels */
110 USHORT uHItemPadding_s; /* Set amount of horizontal padding, in pixels */
111 USHORT uVItemPadding_s; /* Set amount of vertical padding, in pixels */
112 HFONT hFont; /* handle to the current font */
113 HCURSOR hcurArrow; /* handle to the current cursor */
114 HIMAGELIST himl; /* handle to an image list (may be 0) */
115 HWND hwndToolTip; /* handle to tab's tooltip */
116 INT leftmostVisible; /* Used for scrolling, this member contains
117 * the index of the first visible item */
118 INT iSelected; /* the currently selected item */
119 INT iHotTracked; /* the highlighted item under the mouse */
120 INT uFocus; /* item which has the focus */
121 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
122 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
123 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
124 * the size of the control */
125 BOOL fHeightSet; /* was the height of the tabs explicitly set? */
126 BOOL bUnicode; /* Unicode control? */
127 HWND hwndUpDown; /* Updown control used for scrolling */
128 INT cbInfo; /* Number of bytes of caller supplied info per tab */
131 /******************************************************************************
132 * Positioning constants
134 #define SELECTED_TAB_OFFSET 2
135 #define ROUND_CORNER_SIZE 2
136 #define DISPLAY_AREA_PADDINGX 2
137 #define DISPLAY_AREA_PADDINGY 2
138 #define CONTROL_BORDER_SIZEX 2
139 #define CONTROL_BORDER_SIZEY 2
140 #define BUTTON_SPACINGX 3
141 #define BUTTON_SPACINGY 3
142 #define FLAT_BTN_SPACINGX 8
143 #define DEFAULT_TAB_WIDTH 96
145 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongPtrW(hwnd,0))
146 /* Since items are variable sized, cannot directly access them */
147 #define TAB_GetItem(info,i) \
148 ((TAB_ITEM*)((LPBYTE)info->items + (i) * TAB_ITEM_SIZE(info)))
150 /******************************************************************************
151 * Hot-tracking timer constants
153 #define TAB_HOTTRACK_TIMER 1
154 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
156 /******************************************************************************
159 static void TAB_InvalidateTabArea(TAB_INFO *);
160 static void TAB_EnsureSelectionVisible(TAB_INFO *);
161 static void TAB_DrawItemInterior(TAB_INFO *, HDC, INT, RECT*);
164 TAB_SendSimpleNotify (const TAB_INFO *infoPtr, UINT code)
168 nmhdr.hwndFrom = infoPtr->hwnd;
169 nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID);
172 return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
173 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
177 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
178 WPARAM wParam, LPARAM lParam)
186 msg.time = GetMessageTime ();
187 msg.pt.x = LOWORD(GetMessagePos ());
188 msg.pt.y = HIWORD(GetMessagePos ());
190 SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
194 TAB_DumpItemExternalT(TCITEMW *pti, UINT iItem, BOOL isW)
197 TRACE("external tab %d, mask=0x%08x, dwState=0x%08lx, dwStateMask=0x%08lx, cchTextMax=0x%08x\n",
198 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
199 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
200 iItem, pti->iImage, pti->lParam, isW ? debugstr_w(pti->pszText) : debugstr_a((LPSTR)pti->pszText));
205 TAB_DumpItemInternal(TAB_INFO *infoPtr, UINT iItem)
210 ti = TAB_GetItem(infoPtr, iItem);
211 TRACE("tab %d, mask=0x%08x, dwState=0x%08lx, pszText=%s, iImage=%d\n",
212 iItem, ti->mask, ti->dwState, debugstr_w(ti->pszText),
214 TRACE("tab %d, rect.left=%ld, rect.top(row)=%ld\n",
215 iItem, ti->rect.left, ti->rect.top);
220 * the index of the selected tab, or -1 if no tab is selected. */
221 static inline LRESULT TAB_GetCurSel (const TAB_INFO *infoPtr)
223 return infoPtr->iSelected;
227 * the index of the tab item that has the focus
229 * we have not to return negative value
231 * test for windows */
232 static inline LRESULT
233 TAB_GetCurFocus (const TAB_INFO *infoPtr)
235 if (infoPtr->uFocus<0)
237 FIXME("we have not to return negative value");
240 return infoPtr->uFocus;
243 static inline LRESULT TAB_GetToolTips (const TAB_INFO *infoPtr)
245 if (infoPtr == NULL) return 0;
246 return (LRESULT)infoPtr->hwndToolTip;
249 static inline LRESULT TAB_SetCurSel (TAB_INFO *infoPtr, INT iItem)
253 if (iItem >= 0 && iItem < infoPtr->uNumItem) {
254 prevItem=infoPtr->iSelected;
255 infoPtr->iSelected=iItem;
256 TAB_EnsureSelectionVisible(infoPtr);
257 TAB_InvalidateTabArea(infoPtr);
262 static LRESULT TAB_SetCurFocus (TAB_INFO *infoPtr, INT iItem)
264 if (iItem < 0 || iItem >= infoPtr->uNumItem) return 0;
266 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS) {
267 FIXME("Should set input focus\n");
269 int oldFocus = infoPtr->uFocus;
270 if (infoPtr->iSelected != iItem || oldFocus == -1 ) {
271 infoPtr->uFocus = iItem;
272 if (oldFocus != -1) {
273 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING)) {
274 infoPtr->iSelected = iItem;
275 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
278 infoPtr->iSelected = iItem;
279 TAB_EnsureSelectionVisible(infoPtr);
280 TAB_InvalidateTabArea(infoPtr);
287 static inline LRESULT
288 TAB_SetToolTips (TAB_INFO *infoPtr, HWND hwndToolTip)
291 infoPtr->hwndToolTip = hwndToolTip;
295 static inline LRESULT
296 TAB_SetPadding (TAB_INFO *infoPtr, LPARAM lParam)
300 infoPtr->uHItemPadding_s=LOWORD(lParam);
301 infoPtr->uVItemPadding_s=HIWORD(lParam);
306 /******************************************************************************
307 * TAB_InternalGetItemRect
309 * This method will calculate the rectangle representing a given tab item in
310 * client coordinates. This method takes scrolling into account.
312 * This method returns TRUE if the item is visible in the window and FALSE
313 * if it is completely outside the client area.
315 static BOOL TAB_InternalGetItemRect(
316 const TAB_INFO* infoPtr,
321 RECT tmpItemRect,clientRect;
322 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
324 /* Perform a sanity check and a trivial visibility check. */
325 if ( (infoPtr->uNumItem <= 0) ||
326 (itemIndex >= infoPtr->uNumItem) ||
327 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
331 * Avoid special cases in this procedure by assigning the "out"
332 * parameters if the caller didn't supply them
334 if (itemRect == NULL)
335 itemRect = &tmpItemRect;
337 /* Retrieve the unmodified item rect. */
338 *itemRect = TAB_GetItem(infoPtr,itemIndex)->rect;
340 /* calculate the times bottom and top based on the row */
341 GetClientRect(infoPtr->hwnd, &clientRect);
343 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
345 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * infoPtr->tabHeight -
346 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
347 itemRect->left = itemRect->right - infoPtr->tabHeight;
349 else if (lStyle & TCS_VERTICAL)
351 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * infoPtr->tabHeight +
352 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
353 itemRect->right = itemRect->left + infoPtr->tabHeight;
355 else if (lStyle & TCS_BOTTOM)
357 itemRect->bottom = clientRect.bottom - itemRect->top * infoPtr->tabHeight -
358 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
359 itemRect->top = itemRect->bottom - infoPtr->tabHeight;
361 else /* not TCS_BOTTOM and not TCS_VERTICAL */
363 itemRect->top = clientRect.top + itemRect->top * infoPtr->tabHeight +
364 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
365 itemRect->bottom = itemRect->top + infoPtr->tabHeight;
369 * "scroll" it to make sure the item at the very left of the
370 * tab control is the leftmost visible tab.
372 if(lStyle & TCS_VERTICAL)
376 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.top);
379 * Move the rectangle so the first item is slightly offset from
380 * the bottom of the tab control.
384 SELECTED_TAB_OFFSET);
389 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.left,
393 * Move the rectangle so the first item is slightly offset from
394 * the left of the tab control.
400 TRACE("item %d tab h=%d, rect=(%ld,%ld)-(%ld,%ld)\n",
401 itemIndex, infoPtr->tabHeight,
402 itemRect->left, itemRect->top, itemRect->right, itemRect->bottom);
404 /* Now, calculate the position of the item as if it were selected. */
405 if (selectedRect!=NULL)
407 CopyRect(selectedRect, itemRect);
409 /* The rectangle of a selected item is a bit wider. */
410 if(lStyle & TCS_VERTICAL)
411 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
413 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
415 /* If it also a bit higher. */
416 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
418 selectedRect->left -= 2; /* the border is thicker on the right */
419 selectedRect->right += SELECTED_TAB_OFFSET;
421 else if (lStyle & TCS_VERTICAL)
423 selectedRect->left -= SELECTED_TAB_OFFSET;
424 selectedRect->right += 1;
426 else if (lStyle & TCS_BOTTOM)
428 selectedRect->bottom += SELECTED_TAB_OFFSET;
430 else /* not TCS_BOTTOM and not TCS_VERTICAL */
432 selectedRect->top -= SELECTED_TAB_OFFSET;
433 selectedRect->bottom -= 1;
437 /* Check for visibility */
438 if (lStyle & TCS_VERTICAL)
439 return (itemRect->top < clientRect.bottom) && (itemRect->bottom > clientRect.top);
441 return (itemRect->left < clientRect.right) && (itemRect->right > clientRect.left);
445 TAB_GetItemRect(TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
447 return TAB_InternalGetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam, (LPRECT)NULL);
450 /******************************************************************************
453 * This method is called to handle keyboard input
455 static LRESULT TAB_KeyUp(TAB_INFO* infoPtr, WPARAM keyCode)
462 newItem = infoPtr->uFocus - 1;
465 newItem = infoPtr->uFocus + 1;
470 * If we changed to a valid item, change the selection
473 newItem < infoPtr->uNumItem &&
474 infoPtr->uFocus != newItem)
476 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
478 infoPtr->iSelected = newItem;
479 infoPtr->uFocus = newItem;
480 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
482 TAB_EnsureSelectionVisible(infoPtr);
483 TAB_InvalidateTabArea(infoPtr);
490 /******************************************************************************
493 * This method is called whenever the focus goes in or out of this control
494 * it is used to update the visual state of the control.
496 static void TAB_FocusChanging(const TAB_INFO *infoPtr)
502 * Get the rectangle for the item.
504 isVisible = TAB_InternalGetItemRect(infoPtr,
510 * If the rectangle is not completely invisible, invalidate that
511 * portion of the window.
515 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
516 selectedRect.left,selectedRect.top,
517 selectedRect.right,selectedRect.bottom);
518 InvalidateRect(infoPtr->hwnd, &selectedRect, TRUE);
522 static INT TAB_InternalHitTest (
531 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
533 TAB_InternalGetItemRect(infoPtr, iCount, &rect, NULL);
535 if (PtInRect(&rect, pt))
537 *flags = TCHT_ONITEM;
542 *flags = TCHT_NOWHERE;
546 static inline LRESULT
547 TAB_HitTest (TAB_INFO *infoPtr, LPTCHITTESTINFO lptest)
549 return TAB_InternalHitTest (infoPtr, lptest->pt, &lptest->flags);
552 /******************************************************************************
555 * Napster v2b5 has a tab control for its main navigation which has a client
556 * area that covers the whole area of the dialog pages.
557 * That's why it receives all msgs for that area and the underlying dialog ctrls
559 * So I decided that we should handle WM_NCHITTEST here and return
560 * HTTRANSPARENT if we don't hit the tab control buttons.
561 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
562 * doesn't do it that way. Maybe depends on tab control styles ?
564 static inline LRESULT
565 TAB_NCHitTest (TAB_INFO *infoPtr, LPARAM lParam)
570 pt.x = LOWORD(lParam);
571 pt.y = HIWORD(lParam);
572 ScreenToClient(infoPtr->hwnd, &pt);
574 if (TAB_InternalHitTest(infoPtr, pt, &dummyflag) == -1)
575 return HTTRANSPARENT;
581 TAB_LButtonDown (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
586 if (infoPtr->hwndToolTip)
587 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
588 WM_LBUTTONDOWN, wParam, lParam);
590 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
591 SetFocus (infoPtr->hwnd);
594 if (infoPtr->hwndToolTip)
595 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
596 WM_LBUTTONDOWN, wParam, lParam);
598 pt.x = (INT)LOWORD(lParam);
599 pt.y = (INT)HIWORD(lParam);
601 newItem = TAB_InternalHitTest (infoPtr, pt, &dummy);
603 TRACE("On Tab, item %d\n", newItem);
605 if (newItem != -1 && infoPtr->iSelected != newItem)
607 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
609 infoPtr->iSelected = newItem;
610 infoPtr->uFocus = newItem;
611 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
613 TAB_EnsureSelectionVisible(infoPtr);
615 TAB_InvalidateTabArea(infoPtr);
621 static inline LRESULT
622 TAB_LButtonUp (const TAB_INFO *infoPtr)
624 TAB_SendSimpleNotify(infoPtr, NM_CLICK);
629 static inline LRESULT
630 TAB_RButtonDown (const TAB_INFO *infoPtr)
632 TAB_SendSimpleNotify(infoPtr, NM_RCLICK);
636 /******************************************************************************
637 * TAB_DrawLoneItemInterior
639 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
640 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
641 * up the device context and font. This routine does the same setup but
642 * only calls TAB_DrawItemInterior for the single specified item.
645 TAB_DrawLoneItemInterior(TAB_INFO* infoPtr, int iItem)
647 HDC hdc = GetDC(infoPtr->hwnd);
650 /* Clip UpDown control to not draw over it */
651 if (infoPtr->needsScrolling)
653 GetWindowRect(infoPtr->hwnd, &rC);
654 GetWindowRect(infoPtr->hwndUpDown, &r);
655 ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top);
657 TAB_DrawItemInterior(infoPtr, hdc, iItem, NULL);
658 ReleaseDC(infoPtr->hwnd, hdc);
661 /******************************************************************************
662 * TAB_HotTrackTimerProc
664 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
665 * timer is setup so we can check if the mouse is moved out of our window.
666 * (We don't get an event when the mouse leaves, the mouse-move events just
667 * stop being delivered to our window and just start being delivered to
668 * another window.) This function is called when the timer triggers so
669 * we can check if the mouse has left our window. If so, we un-highlight
670 * the hot-tracked tab.
673 TAB_HotTrackTimerProc
675 HWND hwnd, /* handle of window for timer messages */
676 UINT uMsg, /* WM_TIMER message */
677 UINT idEvent, /* timer identifier */
678 DWORD dwTime /* current system time */
681 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
683 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
688 ** If we can't get the cursor position, or if the cursor is outside our
689 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
690 ** "outside" even if it is within our bounding rect if another window
691 ** overlaps. Note also that the case where the cursor stayed within our
692 ** window but has moved off the hot-tracked tab will be handled by the
693 ** WM_MOUSEMOVE event.
695 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
697 /* Redraw iHotTracked to look normal */
698 INT iRedraw = infoPtr->iHotTracked;
699 infoPtr->iHotTracked = -1;
700 TAB_DrawLoneItemInterior(infoPtr, iRedraw);
702 /* Kill this timer */
703 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
708 /******************************************************************************
711 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
712 * should be highlighted. This function determines which tab in a tab control,
713 * if any, is under the mouse and records that information. The caller may
714 * supply output parameters to receive the item number of the tab item which
715 * was highlighted but isn't any longer and of the tab item which is now
716 * highlighted but wasn't previously. The caller can use this information to
717 * selectively redraw those tab items.
719 * If the caller has a mouse position, it can supply it through the pos
720 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
721 * supplies NULL and this function determines the current mouse position
729 int* out_redrawLeave,
736 if (out_redrawLeave != NULL)
737 *out_redrawLeave = -1;
738 if (out_redrawEnter != NULL)
739 *out_redrawEnter = -1;
741 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_HOTTRACK)
749 ScreenToClient(infoPtr->hwnd, &pt);
757 item = TAB_InternalHitTest(infoPtr, pt, &flags);
760 if (item != infoPtr->iHotTracked)
762 if (infoPtr->iHotTracked >= 0)
764 /* Mark currently hot-tracked to be redrawn to look normal */
765 if (out_redrawLeave != NULL)
766 *out_redrawLeave = infoPtr->iHotTracked;
770 /* Kill timer which forces recheck of mouse pos */
771 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
776 /* Start timer so we recheck mouse pos */
777 UINT timerID = SetTimer
781 TAB_HOTTRACK_TIMER_INTERVAL,
782 TAB_HotTrackTimerProc
786 return; /* Hot tracking not available */
789 infoPtr->iHotTracked = item;
793 /* Mark new hot-tracked to be redrawn to look highlighted */
794 if (out_redrawEnter != NULL)
795 *out_redrawEnter = item;
800 /******************************************************************************
803 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
806 TAB_MouseMove (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
811 if (infoPtr->hwndToolTip)
812 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
813 WM_LBUTTONDOWN, wParam, lParam);
815 /* Determine which tab to highlight. Redraw tabs which change highlight
817 TAB_RecalcHotTrack(infoPtr, &lParam, &redrawLeave, &redrawEnter);
819 if (redrawLeave != -1)
820 TAB_DrawLoneItemInterior(infoPtr, redrawLeave);
821 if (redrawEnter != -1)
822 TAB_DrawLoneItemInterior(infoPtr, redrawEnter);
827 /******************************************************************************
830 * Calculates the tab control's display area given the window rectangle or
831 * the window rectangle given the requested display rectangle.
833 static LRESULT TAB_AdjustRect(
838 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
839 LONG *iRightBottom, *iLeftTop;
841 TRACE ("hwnd=%p fLarger=%d (%ld,%ld)-(%ld,%ld)\n", infoPtr->hwnd, fLarger, prc->left, prc->top, prc->right, prc->bottom);
843 if(lStyle & TCS_VERTICAL)
845 iRightBottom = &(prc->right);
846 iLeftTop = &(prc->left);
850 iRightBottom = &(prc->bottom);
851 iLeftTop = &(prc->top);
854 if (fLarger) /* Go from display rectangle */
856 /* Add the height of the tabs. */
857 if (lStyle & TCS_BOTTOM)
858 *iRightBottom += infoPtr->tabHeight * infoPtr->uNumRows;
860 *iLeftTop -= infoPtr->tabHeight * infoPtr->uNumRows +
861 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
863 /* Inflate the rectangle for the padding */
864 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
866 /* Inflate for the border */
867 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEY);
869 else /* Go from window rectangle. */
871 /* Deflate the rectangle for the border */
872 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEY);
874 /* Deflate the rectangle for the padding */
875 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
877 /* Remove the height of the tabs. */
878 if (lStyle & TCS_BOTTOM)
879 *iRightBottom -= infoPtr->tabHeight * infoPtr->uNumRows;
881 *iLeftTop += (infoPtr->tabHeight) * infoPtr->uNumRows +
882 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
888 /******************************************************************************
891 * This method will handle the notification from the scroll control and
892 * perform the scrolling operation on the tab control.
894 static LRESULT TAB_OnHScroll(
900 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
902 if(nPos < infoPtr->leftmostVisible)
903 infoPtr->leftmostVisible--;
905 infoPtr->leftmostVisible++;
907 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
908 TAB_InvalidateTabArea(infoPtr);
909 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
910 MAKELONG(infoPtr->leftmostVisible, 0));
916 /******************************************************************************
919 * This method will check the current scrolling state and make sure the
920 * scrolling control is displayed (or not).
922 static void TAB_SetupScrolling(
925 const RECT* clientRect)
927 static const WCHAR msctls_updown32W[] = { 'm','s','c','t','l','s','_','u','p','d','o','w','n','3','2',0 };
928 static const WCHAR emptyW[] = { 0 };
930 DWORD lStyle = GetWindowLongW(hwnd, GWL_STYLE);
932 if (infoPtr->needsScrolling)
938 * Calculate the position of the scroll control.
940 if(lStyle & TCS_VERTICAL)
942 controlPos.right = clientRect->right;
943 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
945 if (lStyle & TCS_BOTTOM)
947 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
948 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
952 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
953 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
958 controlPos.right = clientRect->right;
959 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
961 if (lStyle & TCS_BOTTOM)
963 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
964 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
968 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
969 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
974 * If we don't have a scroll control yet, we want to create one.
975 * If we have one, we want to make sure it's positioned properly.
977 if (infoPtr->hwndUpDown==0)
979 infoPtr->hwndUpDown = CreateWindowW(msctls_updown32W, emptyW,
980 WS_VISIBLE | WS_CHILD | UDS_HORZ,
981 controlPos.left, controlPos.top,
982 controlPos.right - controlPos.left,
983 controlPos.bottom - controlPos.top,
984 hwnd, NULL, NULL, NULL);
988 SetWindowPos(infoPtr->hwndUpDown,
990 controlPos.left, controlPos.top,
991 controlPos.right - controlPos.left,
992 controlPos.bottom - controlPos.top,
993 SWP_SHOWWINDOW | SWP_NOZORDER);
996 /* Now calculate upper limit of the updown control range.
997 * We do this by calculating how many tabs will be offscreen when the
998 * last tab is visible.
1000 if(infoPtr->uNumItem)
1002 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
1003 maxRange = infoPtr->uNumItem;
1004 tabwidth = TAB_GetItem(infoPtr, infoPtr->uNumItem - 1)->rect.right;
1006 for(; maxRange > 0; maxRange--)
1008 if(tabwidth - TAB_GetItem(infoPtr,maxRange - 1)->rect.left > vsize)
1012 if(maxRange == infoPtr->uNumItem)
1018 /* If we once had a scroll control... hide it */
1019 if (infoPtr->hwndUpDown!=0)
1020 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1022 if (infoPtr->hwndUpDown)
1023 SendMessageW(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1026 /******************************************************************************
1029 * This method will calculate the position rectangles of all the items in the
1030 * control. The rectangle calculated starts at 0 for the first item in the
1031 * list and ignores scrolling and selection.
1032 * It also uses the current font to determine the height of the tab row and
1033 * it checks if all the tabs fit in the client area of the window. If they
1034 * don't, a scrolling control is added.
1036 static void TAB_SetItemBounds (TAB_INFO *infoPtr)
1038 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1039 TEXTMETRICW fontMetrics;
1042 INT curItemRowCount;
1043 HFONT hFont, hOldFont;
1053 * We need to get text information so we need a DC and we need to select
1056 hdc = GetDC(infoPtr->hwnd);
1058 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1059 hOldFont = SelectObject (hdc, hFont);
1062 * We will base the rectangle calculations on the client rectangle
1065 GetClientRect(infoPtr->hwnd, &clientRect);
1067 /* if TCS_VERTICAL then swap the height and width so this code places the
1068 tabs along the top of the rectangle and we can just rotate them after
1069 rather than duplicate all of the below code */
1070 if(lStyle & TCS_VERTICAL)
1072 iTemp = clientRect.bottom;
1073 clientRect.bottom = clientRect.right;
1074 clientRect.right = iTemp;
1077 /* Now use hPadding and vPadding */
1078 infoPtr->uHItemPadding = infoPtr->uHItemPadding_s;
1079 infoPtr->uVItemPadding = infoPtr->uVItemPadding_s;
1081 /* The leftmost item will be "0" aligned */
1083 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1085 if (!(infoPtr->fHeightSet))
1088 int icon_height = 0;
1090 /* Use the current font to determine the height of a tab. */
1091 GetTextMetricsW(hdc, &fontMetrics);
1093 /* Get the icon height */
1095 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1097 /* Take the highest between font or icon */
1098 if (fontMetrics.tmHeight > icon_height)
1099 item_height = fontMetrics.tmHeight + 2;
1101 item_height = icon_height;
1104 * Make sure there is enough space for the letters + icon + growing the
1105 * selected item + extra space for the selected item.
1107 infoPtr->tabHeight = item_height +
1108 ((lStyle & TCS_BUTTONS) ? 2 : 1) *
1109 infoPtr->uVItemPadding;
1111 TRACE("tabH=%d, tmH=%ld, iconh=%d\n",
1112 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1115 TRACE("client right=%ld\n", clientRect.right);
1117 /* Get the icon width */
1120 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1122 if (lStyle & TCS_FIXEDWIDTH)
1125 /* Add padding if icon is present */
1126 icon_width += infoPtr->uHItemPadding;
1129 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1131 TAB_ITEM *curr = TAB_GetItem(infoPtr, curItem);
1133 /* Set the leftmost position of the tab. */
1134 curr->rect.left = curItemLeftPos;
1136 if ((lStyle & TCS_FIXEDWIDTH) || !curr->pszText)
1138 curr->rect.right = curr->rect.left +
1139 max(infoPtr->tabWidth, icon_width);
1145 /* Calculate how wide the tab is depending on the text it contains */
1146 GetTextExtentPoint32W(hdc, curr->pszText,
1147 lstrlenW(curr->pszText), &size);
1149 curr->rect.right = curr->rect.left + size.cx + icon_width +
1150 num * infoPtr->uHItemPadding;
1151 TRACE("for <%s>, l,r=%ld,%ld, num=%d\n",
1152 debugstr_w(curr->pszText), curr->rect.left, curr->rect.right, num);
1156 * Check if this is a multiline tab control and if so
1157 * check to see if we should wrap the tabs
1159 * Wrap all these tabs. We will arrange them evenly later.
1163 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1165 (clientRect.right - CONTROL_BORDER_SIZEX - DISPLAY_AREA_PADDINGX)))
1167 curr->rect.right -= curr->rect.left;
1169 curr->rect.left = 0;
1171 TRACE("wrapping <%s>, l,r=%ld,%ld\n", debugstr_w(curr->pszText),
1172 curr->rect.left, curr->rect.right);
1175 curr->rect.bottom = 0;
1176 curr->rect.top = curItemRowCount - 1;
1178 TRACE("TextSize: %li\n", size.cx);
1179 TRACE("Rect: T %li, L %li, B %li, R %li\n", curr->rect.top,
1180 curr->rect.left, curr->rect.bottom, curr->rect.right);
1183 * The leftmost position of the next item is the rightmost position
1186 if (lStyle & TCS_BUTTONS)
1188 curItemLeftPos = curr->rect.right + BUTTON_SPACINGX;
1189 if (lStyle & TCS_FLATBUTTONS)
1190 curItemLeftPos += FLAT_BTN_SPACINGX;
1193 curItemLeftPos = curr->rect.right;
1196 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1199 * Check if we need a scrolling control.
1201 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1204 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1205 if(!infoPtr->needsScrolling)
1206 infoPtr->leftmostVisible = 0;
1211 * No scrolling in Multiline or Vertical styles.
1213 infoPtr->needsScrolling = FALSE;
1214 infoPtr->leftmostVisible = 0;
1216 TAB_SetupScrolling(infoPtr->hwnd, infoPtr, &clientRect);
1218 /* Set the number of rows */
1219 infoPtr->uNumRows = curItemRowCount;
1221 /* Arrange all tabs evenly if style says so */
1222 if (!(lStyle & TCS_RAGGEDRIGHT) && ((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->uNumItem > 0))
1224 INT tabPerRow,remTab,iRow;
1229 * Ok windows tries to even out the rows. place the same
1230 * number of tabs in each row. So lets give that a shot
1233 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1234 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1236 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1237 iItm<infoPtr->uNumItem;
1240 /* normalize the current rect */
1241 TAB_ITEM *curr = TAB_GetItem(infoPtr, iItm);
1243 /* shift the item to the left side of the clientRect */
1244 curr->rect.right -= curr->rect.left;
1245 curr->rect.left = 0;
1247 TRACE("r=%ld, cl=%d, cl.r=%ld, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1248 curr->rect.right, curItemLeftPos, clientRect.right,
1249 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1251 /* if we have reached the maximum number of tabs on this row */
1252 /* move to the next row, reset our current item left position and */
1253 /* the count of items on this row */
1255 if (lStyle & TCS_VERTICAL) {
1256 /* Vert: Add the remaining tabs in the *last* remainder rows */
1257 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1263 /* Horz: Add the remaining tabs in the *first* remainder rows */
1264 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1271 /* shift the item to the right to place it as the next item in this row */
1272 curr->rect.left += curItemLeftPos;
1273 curr->rect.right += curItemLeftPos;
1274 curr->rect.top = iRow;
1275 if (lStyle & TCS_BUTTONS)
1277 curItemLeftPos = curr->rect.right + 1;
1278 if (lStyle & TCS_FLATBUTTONS)
1279 curItemLeftPos += FLAT_BTN_SPACINGX;
1282 curItemLeftPos = curr->rect.right;
1284 TRACE("arranging <%s>, l,r=%ld,%ld, row=%ld\n",
1285 debugstr_w(curr->pszText), curr->rect.left,
1286 curr->rect.right, curr->rect.top);
1293 INT widthDiff, iIndexStart=0, iIndexEnd=0;
1297 while(iIndexStart < infoPtr->uNumItem)
1299 TAB_ITEM *start = TAB_GetItem(infoPtr, iIndexStart);
1302 * find the index of the row
1304 /* find the first item on the next row */
1305 for (iIndexEnd=iIndexStart;
1306 (iIndexEnd < infoPtr->uNumItem) &&
1307 (TAB_GetItem(infoPtr, iIndexEnd)->rect.top ==
1310 /* intentionally blank */;
1313 * we need to justify these tabs so they fill the whole given
1317 /* find the amount of space remaining on this row */
1318 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1319 TAB_GetItem(infoPtr, iIndexEnd - 1)->rect.right;
1321 /* iCount is the number of tab items on this row */
1322 iCount = iIndexEnd - iIndexStart;
1326 remainder = widthDiff % iCount;
1327 widthDiff = widthDiff / iCount;
1328 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1329 for (iIndex=iIndexStart, iCount=0; iIndex < iIndexEnd; iIndex++, iCount++)
1331 TAB_ITEM *item = TAB_GetItem(infoPtr, iIndex);
1333 item->rect.left += iCount * widthDiff;
1334 item->rect.right += (iCount + 1) * widthDiff;
1336 TRACE("adjusting 1 <%s>, l,r=%ld,%ld\n",
1337 debugstr_w(item->pszText),
1338 item->rect.left, item->rect.right);
1341 TAB_GetItem(infoPtr, iIndex - 1)->rect.right += remainder;
1343 else /* we have only one item on this row, make it take up the entire row */
1345 start->rect.left = clientRect.left;
1346 start->rect.right = clientRect.right - 4;
1348 TRACE("adjusting 2 <%s>, l,r=%ld,%ld\n",
1349 debugstr_w(start->pszText),
1350 start->rect.left, start->rect.right);
1355 iIndexStart = iIndexEnd;
1360 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1361 if(lStyle & TCS_VERTICAL)
1364 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1366 rcItem = &TAB_GetItem(infoPtr, iIndex)->rect;
1368 rcOriginal = *rcItem;
1370 /* this is rotating the items by 90 degrees clockwise around the center of the control */
1371 rcItem->top = (rcOriginal.left - clientRect.left);
1372 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1373 rcItem->left = rcOriginal.top;
1374 rcItem->right = rcOriginal.bottom;
1378 TAB_EnsureSelectionVisible(infoPtr);
1379 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
1382 SelectObject (hdc, hOldFont);
1383 ReleaseDC (infoPtr->hwnd, hdc);
1388 TAB_EraseTabInterior
1396 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1397 HBRUSH hbr = CreateSolidBrush (comctl32_color.clrBtnFace);
1398 BOOL deleteBrush = TRUE;
1399 RECT rTemp = *drawRect;
1401 InflateRect(&rTemp, -2, -2);
1402 if (lStyle & TCS_BUTTONS)
1404 if (iItem == infoPtr->iSelected)
1406 /* Background color */
1407 if (!(lStyle & TCS_OWNERDRAWFIXED))
1410 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1412 SetTextColor(hdc, comctl32_color.clr3dFace);
1413 SetBkColor(hdc, comctl32_color.clr3dHilight);
1415 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1416 * we better use 0x55aa bitmap brush to make scrollbar's background
1417 * look different from the window background.
1419 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1420 hbr = COMCTL32_hPattern55AABrush;
1422 deleteBrush = FALSE;
1424 FillRect(hdc, &rTemp, hbr);
1426 else /* ! selected */
1428 if (lStyle & TCS_FLATBUTTONS)
1430 FillRect(hdc, drawRect, hbr);
1431 if (iItem == infoPtr->iHotTracked)
1432 DrawEdge(hdc, drawRect, EDGE_RAISED, BF_SOFT|BF_RECT);
1435 FillRect(hdc, &rTemp, hbr);
1439 else /* !TCS_BUTTONS */
1441 FillRect(hdc, &rTemp, hbr);
1445 if (deleteBrush) DeleteObject(hbr);
1448 /******************************************************************************
1449 * TAB_DrawItemInterior
1451 * This method is used to draw the interior (text and icon) of a single tab
1452 * into the tab control.
1455 TAB_DrawItemInterior
1463 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1472 /* if (drawRect == NULL) */
1479 * Get the rectangle for the item.
1481 isVisible = TAB_InternalGetItemRect(infoPtr, iItem, &itemRect, &selectedRect);
1486 * Make sure drawRect points to something valid; simplifies code.
1488 drawRect = &localRect;
1491 * This logic copied from the part of TAB_DrawItem which draws
1492 * the tab background. It's important to keep it in sync. I
1493 * would have liked to avoid code duplication, but couldn't figure
1494 * out how without making spaghetti of TAB_DrawItem.
1496 if (iItem == infoPtr->iSelected)
1497 *drawRect = selectedRect;
1499 *drawRect = itemRect;
1501 if (lStyle & TCS_BUTTONS)
1503 if (iItem == infoPtr->iSelected)
1505 drawRect->left += 4;
1507 drawRect->right -= 4;
1508 drawRect->bottom -= 1;
1512 drawRect->left += 2;
1514 drawRect->right -= 2;
1515 drawRect->bottom -= 2;
1520 if ((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1522 if (iItem != infoPtr->iSelected)
1524 drawRect->left += 2;
1526 drawRect->bottom -= 2;
1529 else if (lStyle & TCS_VERTICAL)
1531 if (iItem == infoPtr->iSelected)
1533 drawRect->right += 1;
1538 drawRect->right -= 2;
1539 drawRect->bottom -= 2;
1542 else if (lStyle & TCS_BOTTOM)
1544 if (iItem == infoPtr->iSelected)
1550 InflateRect(drawRect, -2, -2);
1551 drawRect->bottom += 2;
1556 if (iItem == infoPtr->iSelected)
1558 drawRect->bottom += 3;
1562 drawRect->bottom -= 2;
1563 InflateRect(drawRect, -2, 0);
1568 TRACE("drawRect=(%ld,%ld)-(%ld,%ld)\n",
1569 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom);
1571 /* Clear interior */
1572 TAB_EraseTabInterior (infoPtr, hdc, iItem, drawRect);
1574 /* Draw the focus rectangle */
1575 if (!(lStyle & TCS_FOCUSNEVER) &&
1576 (GetFocus() == infoPtr->hwnd) &&
1577 (iItem == infoPtr->uFocus) )
1579 RECT rFocus = *drawRect;
1580 InflateRect(&rFocus, -3, -3);
1581 if (lStyle & TCS_BOTTOM && !(lStyle & TCS_VERTICAL))
1583 if (lStyle & TCS_BUTTONS)
1589 DrawFocusRect(hdc, &rFocus);
1595 htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1596 holdPen = SelectObject(hdc, htextPen);
1597 hOldFont = SelectObject(hdc, infoPtr->hFont);
1600 * Setup for text output
1602 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1603 SetTextColor(hdc, (((iItem == infoPtr->iHotTracked) && !(lStyle & TCS_FLATBUTTONS)) |
1604 (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED)) ?
1605 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1608 * if owner draw, tell the owner to draw
1610 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(infoPtr->hwnd))
1616 drawRect->right -= 1;
1617 if ( iItem == infoPtr->iSelected )
1619 drawRect->right -= 1;
1620 drawRect->left += 1;
1624 * get the control id
1626 id = (UINT)GetWindowLongPtrW( infoPtr->hwnd, GWLP_ID );
1629 * put together the DRAWITEMSTRUCT
1631 dis.CtlType = ODT_TAB;
1634 dis.itemAction = ODA_DRAWENTIRE;
1636 if ( iItem == infoPtr->iSelected )
1637 dis.itemState |= ODS_SELECTED;
1638 if (infoPtr->uFocus == iItem)
1639 dis.itemState |= ODS_FOCUS;
1640 dis.hwndItem = infoPtr->hwnd;
1642 CopyRect(&dis.rcItem,drawRect);
1643 dis.itemData = (ULONG_PTR)TAB_GetItem(infoPtr, iItem)->extra;
1646 * send the draw message
1648 SendMessageW( infoPtr->hwndNotify, WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1652 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
1656 /* used to center the icon and text in the tab */
1658 INT center_offset_h, center_offset_v;
1660 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1661 rcImage = *drawRect;
1665 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1667 /* get the rectangle that the text fits in */
1670 DrawTextW(hdc, item->pszText, -1, &rcText, DT_CALCRECT);
1673 * If not owner draw, then do the drawing ourselves.
1677 if (infoPtr->himl && (item->mask & TCIF_IMAGE))
1682 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1684 if(lStyle & TCS_VERTICAL)
1686 center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1687 center_offset_v = ((drawRect->right - drawRect->left) - (cx + infoPtr->uVItemPadding)) / 2;
1691 center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1692 center_offset_v = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uVItemPadding)) / 2;
1695 if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1696 center_offset_h = infoPtr->uHItemPadding;
1698 if (center_offset_h < 2)
1699 center_offset_h = 2;
1701 if (center_offset_v < 0)
1702 center_offset_v = 0;
1704 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1705 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1706 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1707 (rcText.right-rcText.left));
1709 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1711 rcImage.top = drawRect->top + center_offset_h;
1712 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1713 /* right side of the tab, but the image still uses the left as its x position */
1714 /* this keeps the image always drawn off of the same side of the tab */
1715 rcImage.left = drawRect->right - cx - center_offset_v;
1716 drawRect->top += cy + infoPtr->uHItemPadding;
1718 else if(lStyle & TCS_VERTICAL)
1720 rcImage.top = drawRect->bottom - cy - center_offset_h;
1721 rcImage.left = drawRect->left + center_offset_v;
1722 drawRect->bottom -= cy + infoPtr->uHItemPadding;
1724 else /* normal style, whether TCS_BOTTOM or not */
1726 rcImage.left = drawRect->left + center_offset_h;
1727 rcImage.top = drawRect->top + center_offset_v;
1728 drawRect->left += cx + infoPtr->uHItemPadding;
1731 TRACE("drawing image=%d, left=%ld, top=%ld\n",
1732 item->iImage, rcImage.left, rcImage.top-1);
1744 /* Now position text */
1745 if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT)
1746 center_offset_h = infoPtr->uHItemPadding;
1748 if(lStyle & TCS_VERTICAL)
1749 center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1751 center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1753 if(lStyle & TCS_VERTICAL)
1755 if(lStyle & TCS_BOTTOM)
1756 drawRect->top+=center_offset_h;
1758 drawRect->bottom-=center_offset_h;
1760 center_offset_v = ((drawRect->right - drawRect->left) - (rcText.bottom - rcText.top)) / 2;
1764 drawRect->left += center_offset_h;
1765 center_offset_v = ((drawRect->bottom - drawRect->top) - (rcText.bottom - rcText.top)) / 2;
1768 /* if an item is selected, the text is shifted up instead of down */
1769 if (iItem == infoPtr->iSelected)
1770 center_offset_v -= infoPtr->uVItemPadding / 2;
1772 center_offset_v += infoPtr->uVItemPadding / 2;
1774 if (center_offset_v < 0)
1775 center_offset_v = 0;
1777 if(lStyle & TCS_VERTICAL)
1778 drawRect->left += center_offset_v;
1780 drawRect->top += center_offset_v;
1783 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1785 static const WCHAR ArialW[] = { 'A','r','i','a','l',0 };
1788 INT nEscapement = 900;
1789 INT nOrientation = 900;
1791 if(lStyle & TCS_BOTTOM)
1794 nOrientation = -900;
1797 /* to get a font with the escapement and orientation we are looking for, we need to */
1798 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1799 if (!GetObjectW((infoPtr->hFont) ?
1800 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1801 sizeof(LOGFONTW),&logfont))
1805 lstrcpyW(logfont.lfFaceName, ArialW);
1806 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1808 logfont.lfWeight = FW_NORMAL;
1809 logfont.lfItalic = 0;
1810 logfont.lfUnderline = 0;
1811 logfont.lfStrikeOut = 0;
1814 logfont.lfEscapement = nEscapement;
1815 logfont.lfOrientation = nOrientation;
1816 hFont = CreateFontIndirectW(&logfont);
1817 SelectObject(hdc, hFont);
1822 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1823 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1827 lstrlenW(item->pszText),
1831 DeleteObject(hFont);
1835 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1836 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1837 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1838 (rcText.right-rcText.left));
1845 lstrlenW(item->pszText),
1847 DT_LEFT | DT_SINGLELINE
1852 *drawRect = rcTemp; /* restore drawRect */
1858 SelectObject(hdc, hOldFont);
1859 SetBkMode(hdc, oldBkMode);
1860 SelectObject(hdc, holdPen);
1861 DeleteObject( htextPen );
1864 /******************************************************************************
1867 * This method is used to draw a single tab into the tab control.
1869 static void TAB_DrawItem(
1874 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1878 RECT r, fillRect, r1;
1881 COLORREF bkgnd, corner;
1884 * Get the rectangle for the item.
1886 isVisible = TAB_InternalGetItemRect(infoPtr,
1895 /* Clip UpDown control to not draw over it */
1896 if (infoPtr->needsScrolling)
1898 GetWindowRect(infoPtr->hwnd, &rC);
1899 GetWindowRect(infoPtr->hwndUpDown, &rUD);
1900 ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
1903 /* If you need to see what the control is doing,
1904 * then override these variables. They will change what
1905 * fill colors are used for filling the tabs, and the
1906 * corners when drawing the edge.
1908 bkgnd = comctl32_color.clrBtnFace;
1909 corner = comctl32_color.clrBtnFace;
1911 if (lStyle & TCS_BUTTONS)
1913 /* Get item rectangle */
1916 /* Separators between flat buttons */
1917 if (lStyle & TCS_FLATBUTTONS)
1920 r1.right += (FLAT_BTN_SPACINGX -2);
1921 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1924 if (iItem == infoPtr->iSelected)
1926 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1928 OffsetRect(&r, 1, 1);
1930 else /* ! selected */
1932 if (!(lStyle & TCS_FLATBUTTONS))
1933 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1936 else /* !TCS_BUTTONS */
1938 /* We draw a rectangle of different sizes depending on the selection
1940 if (iItem == infoPtr->iSelected) {
1942 GetClientRect (infoPtr->hwnd, &rect);
1943 clRight = rect.right;
1944 clBottom = rect.bottom;
1951 * Erase the background. (Delay it but setup rectangle.)
1952 * This is necessary when drawing the selected item since it is larger
1953 * than the others, it might overlap with stuff already drawn by the
1958 if(lStyle & TCS_VERTICAL)
1960 /* These are for adjusting the drawing of a Selected tab */
1961 /* The initial values are for the normal case of non-Selected */
1962 int ZZ = 1; /* Do not strech if selected */
1963 if (iItem == infoPtr->iSelected) {
1966 /* if leftmost draw the line longer */
1967 if(selectedRect.top == 0)
1968 fillRect.top += CONTROL_BORDER_SIZEY;
1969 /* if rightmost draw the line longer */
1970 if(selectedRect.bottom == clBottom)
1971 fillRect.bottom -= CONTROL_BORDER_SIZEY;
1974 if (lStyle & TCS_BOTTOM)
1976 /* Adjust both rectangles to match native */
1979 TRACE("<right> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1981 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1982 r.left,r.top,r.right,r.bottom);
1984 /* Clear interior */
1985 SetBkColor(hdc, bkgnd);
1986 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1988 /* Draw rectangular edge around tab */
1989 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
1991 /* Now erase the top corner and draw diagonal edge */
1992 SetBkColor(hdc, corner);
1993 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1996 r1.bottom = r1.top + ROUND_CORNER_SIZE;
1997 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1999 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2001 /* Now erase the bottom corner and draw diagonal edge */
2002 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2003 r1.bottom = r.bottom;
2005 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2006 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2008 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2010 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
2014 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
2020 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2022 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2023 r.left,r.top,r.right,r.bottom);
2025 /* Clear interior */
2026 SetBkColor(hdc, bkgnd);
2027 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2029 /* Draw rectangular edge around tab */
2030 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2032 /* Now erase the top corner and draw diagonal edge */
2033 SetBkColor(hdc, corner);
2036 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2037 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2038 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2040 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2042 /* Now erase the bottom corner and draw diagonal edge */
2044 r1.bottom = r.bottom;
2045 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2046 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2047 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2049 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2052 else /* ! TCS_VERTICAL */
2054 /* These are for adjusting the drawing of a Selected tab */
2055 /* The initial values are for the normal case of non-Selected */
2056 if (iItem == infoPtr->iSelected) {
2057 /* if leftmost draw the line longer */
2058 if(selectedRect.left == 0)
2059 fillRect.left += CONTROL_BORDER_SIZEX;
2060 /* if rightmost draw the line longer */
2061 if(selectedRect.right == clRight)
2062 fillRect.right -= CONTROL_BORDER_SIZEX;
2065 if (lStyle & TCS_BOTTOM)
2067 /* Adjust both rectangles for topmost row */
2068 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2074 TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2076 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2077 r.left,r.top,r.right,r.bottom);
2079 /* Clear interior */
2080 SetBkColor(hdc, bkgnd);
2081 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2083 /* Draw rectangular edge around tab */
2084 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2086 /* Now erase the righthand corner and draw diagonal edge */
2087 SetBkColor(hdc, corner);
2088 r1.left = r.right - ROUND_CORNER_SIZE;
2089 r1.bottom = r.bottom;
2091 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2092 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2094 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2096 /* Now erase the lefthand corner and draw diagonal edge */
2098 r1.bottom = r.bottom;
2099 r1.right = r1.left + ROUND_CORNER_SIZE;
2100 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2101 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2103 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2105 if (iItem == infoPtr->iSelected)
2109 if (selectedRect.left == 0)
2114 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2121 /* Adjust both rectangles for bottommost row */
2122 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2124 fillRect.bottom += 3;
2128 TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2130 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2131 r.left,r.top,r.right,r.bottom);
2133 /* Clear interior */
2134 SetBkColor(hdc, bkgnd);
2135 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2137 /* Draw rectangular edge around tab */
2138 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2140 /* Now erase the righthand corner and draw diagonal edge */
2141 SetBkColor(hdc, corner);
2142 r1.left = r.right - ROUND_CORNER_SIZE;
2145 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2146 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2148 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2150 /* Now erase the lefthand corner and draw diagonal edge */
2153 r1.right = r1.left + ROUND_CORNER_SIZE;
2154 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2155 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2157 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2162 TAB_DumpItemInternal(infoPtr, iItem);
2164 /* This modifies r to be the text rectangle. */
2165 TAB_DrawItemInterior(infoPtr, hdc, iItem, &r);
2169 /******************************************************************************
2172 * This method is used to draw the raised border around the tab control
2175 static void TAB_DrawBorder (TAB_INFO *infoPtr, HDC hdc)
2178 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2180 GetClientRect (infoPtr->hwnd, &rect);
2183 * Adjust for the style
2186 if (infoPtr->uNumItem)
2188 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2189 rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2190 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2191 rect.right -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2192 else if(lStyle & TCS_VERTICAL)
2193 rect.left += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2194 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2195 rect.top += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2198 TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2199 rect.left, rect.top, rect.right, rect.bottom);
2201 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2204 /******************************************************************************
2207 * This method repaints the tab control..
2209 static void TAB_Refresh (TAB_INFO *infoPtr, HDC hdc)
2214 if (!infoPtr->DoRedraw)
2217 hOldFont = SelectObject (hdc, infoPtr->hFont);
2219 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS)
2221 for (i = 0; i < infoPtr->uNumItem; i++)
2222 TAB_DrawItem (infoPtr, hdc, i);
2226 /* Draw all the non selected item first */
2227 for (i = 0; i < infoPtr->uNumItem; i++)
2229 if (i != infoPtr->iSelected)
2230 TAB_DrawItem (infoPtr, hdc, i);
2233 /* Now, draw the border, draw it before the selected item
2234 * since the selected item overwrites part of the border. */
2235 TAB_DrawBorder (infoPtr, hdc);
2237 /* Then, draw the selected item */
2238 TAB_DrawItem (infoPtr, hdc, infoPtr->iSelected);
2240 /* If we haven't set the current focus yet, set it now.
2241 * Only happens when we first paint the tab controls */
2242 if (infoPtr->uFocus == -1)
2243 TAB_SetCurFocus(infoPtr, infoPtr->iSelected);
2246 SelectObject (hdc, hOldFont);
2249 static inline DWORD TAB_GetRowCount (const TAB_INFO *infoPtr)
2251 return infoPtr->uNumRows;
2254 static inline LRESULT TAB_SetRedraw (TAB_INFO *infoPtr, BOOL doRedraw)
2256 infoPtr->DoRedraw = doRedraw;
2260 /******************************************************************************
2261 * TAB_EnsureSelectionVisible
2263 * This method will make sure that the current selection is completely
2264 * visible by scrolling until it is.
2266 static void TAB_EnsureSelectionVisible(
2269 INT iSelected = infoPtr->iSelected;
2270 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2271 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2273 /* set the items row to the bottommost row or topmost row depending on
2275 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2277 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2281 if(lStyle & TCS_VERTICAL)
2282 newselected = selected->rect.left;
2284 newselected = selected->rect.top;
2286 /* the target row is always (number of rows - 1)
2287 as row 0 is furthest from the clientRect */
2288 iTargetRow = infoPtr->uNumRows - 1;
2290 if (newselected != iTargetRow)
2293 if(lStyle & TCS_VERTICAL)
2295 for (i=0; i < infoPtr->uNumItem; i++)
2297 /* move everything in the row of the selected item to the iTargetRow */
2298 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2300 if (item->rect.left == newselected )
2301 item->rect.left = iTargetRow;
2304 if (item->rect.left > newselected)
2311 for (i=0; i < infoPtr->uNumItem; i++)
2313 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2315 if (item->rect.top == newselected )
2316 item->rect.top = iTargetRow;
2319 if (item->rect.top > newselected)
2324 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2329 * Do the trivial cases first.
2331 if ( (!infoPtr->needsScrolling) ||
2332 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2335 if (infoPtr->leftmostVisible >= iSelected)
2337 infoPtr->leftmostVisible = iSelected;
2341 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2346 /* Calculate the part of the client area that is visible */
2347 GetClientRect(infoPtr->hwnd, &r);
2350 GetClientRect(infoPtr->hwndUpDown, &r);
2353 if ((selected->rect.right -
2354 selected->rect.left) >= width )
2356 /* Special case: width of selected item is greater than visible
2359 infoPtr->leftmostVisible = iSelected;
2363 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2365 if ((selected->rect.right - TAB_GetItem(infoPtr, i)->rect.left) < width)
2368 infoPtr->leftmostVisible = i;
2372 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2373 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2375 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2376 MAKELONG(infoPtr->leftmostVisible, 0));
2379 /******************************************************************************
2380 * TAB_InvalidateTabArea
2382 * This method will invalidate the portion of the control that contains the
2383 * tabs. It is called when the state of the control changes and needs
2386 static void TAB_InvalidateTabArea(TAB_INFO* infoPtr)
2388 RECT clientRect, rInvalidate, rAdjClient;
2389 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2390 INT lastRow = infoPtr->uNumRows - 1;
2393 if (lastRow < 0) return;
2395 GetClientRect(infoPtr->hwnd, &clientRect);
2396 rInvalidate = clientRect;
2397 rAdjClient = clientRect;
2399 TAB_AdjustRect(infoPtr, 0, &rAdjClient);
2401 TAB_InternalGetItemRect(infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2402 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2404 rInvalidate.left = rAdjClient.right;
2405 if (infoPtr->uNumRows == 1)
2406 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2408 else if(lStyle & TCS_VERTICAL)
2410 rInvalidate.right = rAdjClient.left;
2411 if (infoPtr->uNumRows == 1)
2412 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2414 else if (lStyle & TCS_BOTTOM)
2416 rInvalidate.top = rAdjClient.bottom;
2417 if (infoPtr->uNumRows == 1)
2418 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2422 rInvalidate.bottom = rAdjClient.top;
2423 if (infoPtr->uNumRows == 1)
2424 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2427 /* Punch out the updown control */
2428 if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2430 GetClientRect(infoPtr->hwndUpDown, &r);
2431 if (rInvalidate.right > clientRect.right - r.left)
2432 rInvalidate.right = rInvalidate.right - (r.right - r.left);
2434 rInvalidate.right = clientRect.right - r.left;
2437 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2438 rInvalidate.left, rInvalidate.top,
2439 rInvalidate.right, rInvalidate.bottom);
2441 InvalidateRect(infoPtr->hwnd, &rInvalidate, TRUE);
2444 static inline LRESULT TAB_Paint (TAB_INFO *infoPtr, HDC hdcPaint)
2453 hdc = BeginPaint (infoPtr->hwnd, &ps);
2454 TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2456 ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2459 TAB_Refresh (infoPtr, hdc);
2462 EndPaint (infoPtr->hwnd, &ps);
2468 TAB_InsertItemT (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2475 GetClientRect (infoPtr->hwnd, &rect);
2476 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", infoPtr->hwnd,
2477 rect.top, rect.left, rect.bottom, rect.right);
2479 pti = (TCITEMW *)lParam;
2480 iItem = (INT)wParam;
2482 if (iItem < 0) return -1;
2483 if (iItem > infoPtr->uNumItem)
2484 iItem = infoPtr->uNumItem;
2486 TAB_DumpItemExternalT(pti, iItem, bUnicode);
2489 if (infoPtr->uNumItem == 0) {
2490 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2491 infoPtr->uNumItem++;
2492 infoPtr->iSelected = 0;
2495 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2497 infoPtr->uNumItem++;
2498 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2500 /* pre insert copy */
2502 memcpy (infoPtr->items, oldItems,
2503 iItem * TAB_ITEM_SIZE(infoPtr));
2506 /* post insert copy */
2507 if (iItem < infoPtr->uNumItem - 1) {
2508 memcpy (TAB_GetItem(infoPtr, iItem + 1),
2509 oldItems + iItem * TAB_ITEM_SIZE(infoPtr),
2510 (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2514 if (iItem <= infoPtr->iSelected)
2515 infoPtr->iSelected++;
2520 item = TAB_GetItem(infoPtr, iItem);
2522 item->mask = pti->mask;
2523 item->pszText = NULL;
2525 if (pti->mask & TCIF_TEXT)
2528 Str_SetPtrW (&item->pszText, pti->pszText);
2530 Str_SetPtrAtoW (&item->pszText, (LPSTR)pti->pszText);
2533 if (pti->mask & TCIF_IMAGE)
2534 item->iImage = pti->iImage;
2538 if (pti->mask & TCIF_PARAM)
2539 memcpy(item->extra, &pti->lParam, infoPtr->cbInfo);
2541 memset(item->extra, 0, infoPtr->cbInfo);
2543 TAB_SetItemBounds(infoPtr);
2544 if (infoPtr->uNumItem > 1)
2545 TAB_InvalidateTabArea(infoPtr);
2547 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2549 TRACE("[%p]: added item %d %s\n",
2550 infoPtr->hwnd, iItem, debugstr_w(item->pszText));
2556 TAB_SetItemSize (TAB_INFO *infoPtr, LPARAM lParam)
2558 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2560 BOOL bNeedPaint = FALSE;
2562 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2564 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2565 if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2567 infoPtr->tabWidth = max((INT)LOWORD(lParam), infoPtr->tabMinWidth);
2571 if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2573 if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2574 infoPtr->tabHeight = (INT)HIWORD(lParam);
2578 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2579 HIWORD(lResult), LOWORD(lResult),
2580 infoPtr->tabHeight, infoPtr->tabWidth);
2584 TAB_SetItemBounds(infoPtr);
2585 RedrawWindow(infoPtr->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2591 static inline LRESULT TAB_SetMinTabWidth (TAB_INFO *infoPtr, INT cx)
2595 TRACE("(%p,%d)\n", infoPtr, cx);
2598 oldcx = infoPtr->tabMinWidth;
2599 infoPtr->tabMinWidth = (cx==-1)?DEFAULT_TAB_WIDTH:cx;
2605 static inline LRESULT
2606 TAB_HighlightItem (TAB_INFO *infoPtr, INT iItem, BOOL fHighlight)
2610 TRACE("(%p,%d,%s)\n", infoPtr, iItem, fHighlight ? "true" : "false");
2612 if (!infoPtr || iItem < 0 || iItem >= infoPtr->uNumItem)
2615 lpState = &TAB_GetItem(infoPtr, iItem)->dwState;
2618 *lpState |= TCIS_HIGHLIGHTED;
2620 *lpState &= ~TCIS_HIGHLIGHTED;
2626 TAB_SetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2630 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2632 if (iItem < 0 || iItem >= infoPtr->uNumItem)
2635 TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2637 wineItem = TAB_GetItem(infoPtr, iItem);
2639 if (tabItem->mask & TCIF_IMAGE)
2640 wineItem->iImage = tabItem->iImage;
2642 if (tabItem->mask & TCIF_PARAM)
2643 memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2645 if (tabItem->mask & TCIF_RTLREADING)
2646 FIXME("TCIF_RTLREADING\n");
2648 if (tabItem->mask & TCIF_STATE)
2649 wineItem->dwState = tabItem->dwState;
2651 if (tabItem->mask & TCIF_TEXT)
2653 if (wineItem->pszText)
2655 Free(wineItem->pszText);
2656 wineItem->pszText = NULL;
2659 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2661 Str_SetPtrAtoW(&wineItem->pszText, (LPSTR)tabItem->pszText);
2664 /* Update and repaint tabs */
2665 TAB_SetItemBounds(infoPtr);
2666 TAB_InvalidateTabArea(infoPtr);
2671 static inline LRESULT TAB_GetItemCount (const TAB_INFO *infoPtr)
2673 return infoPtr->uNumItem;
2678 TAB_GetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2682 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2684 if (iItem < 0 || iItem >= infoPtr->uNumItem)
2687 wineItem = TAB_GetItem(infoPtr, iItem);
2689 if (tabItem->mask & TCIF_IMAGE)
2690 tabItem->iImage = wineItem->iImage;
2692 if (tabItem->mask & TCIF_PARAM)
2693 memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2695 if (tabItem->mask & TCIF_RTLREADING)
2696 FIXME("TCIF_RTLREADING\n");
2698 if (tabItem->mask & TCIF_STATE)
2699 tabItem->dwState = wineItem->dwState;
2701 if (tabItem->mask & TCIF_TEXT)
2704 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2706 Str_GetPtrWtoA (wineItem->pszText, (LPSTR)tabItem->pszText, tabItem->cchTextMax);
2709 TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2715 static LRESULT TAB_DeleteItem (TAB_INFO *infoPtr, INT iItem)
2717 BOOL bResult = FALSE;
2719 TRACE("(%p, %d)\n", infoPtr, iItem);
2721 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2723 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
2724 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2726 TAB_InvalidateTabArea(infoPtr);
2728 if ((item->mask & TCIF_TEXT) && item->pszText)
2729 Free(item->pszText);
2731 infoPtr->uNumItem--;
2733 if (!infoPtr->uNumItem)
2735 infoPtr->items = NULL;
2736 if (infoPtr->iHotTracked >= 0)
2738 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
2739 infoPtr->iHotTracked = -1;
2744 infoPtr->items = Alloc(TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2747 memcpy(infoPtr->items, oldItems, iItem * TAB_ITEM_SIZE(infoPtr));
2749 if (iItem < infoPtr->uNumItem)
2750 memcpy(TAB_GetItem(infoPtr, iItem),
2751 oldItems + (iItem + 1) * TAB_ITEM_SIZE(infoPtr),
2752 (infoPtr->uNumItem - iItem) * TAB_ITEM_SIZE(infoPtr));
2754 if (iItem <= infoPtr->iHotTracked)
2756 /* When tabs move left/up, the hot track item may change */
2757 FIXME("Recalc hot track");
2762 /* Readjust the selected index */
2763 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2764 infoPtr->iSelected--;
2766 if (iItem < infoPtr->iSelected)
2767 infoPtr->iSelected--;
2769 if (infoPtr->uNumItem == 0)
2770 infoPtr->iSelected = -1;
2772 /* Reposition and repaint tabs */
2773 TAB_SetItemBounds(infoPtr);
2781 static inline LRESULT TAB_DeleteAllItems (TAB_INFO *infoPtr)
2783 TRACE("(%p)\n", infoPtr);
2784 while (infoPtr->uNumItem)
2785 TAB_DeleteItem (infoPtr, 0);
2790 static inline LRESULT TAB_GetFont (const TAB_INFO *infoPtr)
2792 TRACE("(%p) returning %p\n", infoPtr, infoPtr->hFont);
2793 return (LRESULT)infoPtr->hFont;
2796 static inline LRESULT TAB_SetFont (TAB_INFO *infoPtr, HFONT hNewFont)
2798 TRACE("(%p,%p)\n", infoPtr, hNewFont);
2800 infoPtr->hFont = hNewFont;
2802 TAB_SetItemBounds(infoPtr);
2804 TAB_InvalidateTabArea(infoPtr);
2810 static inline LRESULT TAB_GetImageList (const TAB_INFO *infoPtr)
2813 return (LRESULT)infoPtr->himl;
2816 static inline LRESULT TAB_SetImageList (TAB_INFO *infoPtr, HIMAGELIST himlNew)
2818 HIMAGELIST himlPrev = infoPtr->himl;
2820 infoPtr->himl = himlNew;
2821 return (LRESULT)himlPrev;
2824 static inline LRESULT TAB_GetUnicodeFormat (const TAB_INFO *infoPtr)
2826 return infoPtr->bUnicode;
2829 static inline LRESULT TAB_SetUnicodeFormat (TAB_INFO *infoPtr, BOOL bUnicode)
2831 BOOL bTemp = infoPtr->bUnicode;
2833 infoPtr->bUnicode = bUnicode;
2838 static inline LRESULT TAB_Size (TAB_INFO *infoPtr)
2840 /* I'm not really sure what the following code was meant to do.
2841 This is what it is doing:
2842 When WM_SIZE is sent with SIZE_RESTORED, the control
2843 gets positioned in the top left corner.
2847 UINT uPosFlags,cx,cy;
2851 parent = GetParent (hwnd);
2852 GetClientRect(parent, &parent_rect);
2855 if (GetWindowLongW(hwnd, GWL_STYLE) & CCS_NORESIZE)
2856 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2858 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2859 cx, cy, uPosFlags | SWP_NOZORDER);
2861 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2864 /* Recompute the size/position of the tabs. */
2865 TAB_SetItemBounds (infoPtr);
2867 /* Force a repaint of the control. */
2868 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2874 static LRESULT TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2877 TEXTMETRICW fontMetrics;
2882 infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
2884 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
2886 infoPtr->hwnd = hwnd;
2887 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
2888 infoPtr->uNumItem = 0;
2889 infoPtr->uNumRows = 0;
2890 infoPtr->uHItemPadding = 6;
2891 infoPtr->uVItemPadding = 3;
2892 infoPtr->uHItemPadding_s = 6;
2893 infoPtr->uVItemPadding_s = 3;
2896 infoPtr->hcurArrow = LoadCursorW (0, (LPWSTR)IDC_ARROW);
2897 infoPtr->iSelected = -1;
2898 infoPtr->iHotTracked = -1;
2899 infoPtr->uFocus = -1;
2900 infoPtr->hwndToolTip = 0;
2901 infoPtr->DoRedraw = TRUE;
2902 infoPtr->needsScrolling = FALSE;
2903 infoPtr->hwndUpDown = 0;
2904 infoPtr->leftmostVisible = 0;
2905 infoPtr->fHeightSet = FALSE;
2906 infoPtr->bUnicode = IsWindowUnicode (hwnd);
2907 infoPtr->cbInfo = sizeof(LPARAM);
2909 TRACE("Created tab control, hwnd [%p]\n", hwnd);
2911 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2912 if you don't specify it in CreateWindow. This is necessary in
2913 order for paint to work correctly. This follows windows behaviour. */
2914 dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
2915 SetWindowLongW(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2917 if (dwStyle & TCS_TOOLTIPS) {
2918 /* Create tooltip control */
2919 infoPtr->hwndToolTip =
2920 CreateWindowExW (0, TOOLTIPS_CLASSW, NULL, 0,
2921 CW_USEDEFAULT, CW_USEDEFAULT,
2922 CW_USEDEFAULT, CW_USEDEFAULT,
2925 /* Send NM_TOOLTIPSCREATED notification */
2926 if (infoPtr->hwndToolTip) {
2927 NMTOOLTIPSCREATED nmttc;
2929 nmttc.hdr.hwndFrom = hwnd;
2930 nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
2931 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2932 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2934 SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
2935 (WPARAM)GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc);
2940 * We need to get text information so we need a DC and we need to select
2944 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
2946 /* Use the system font to determine the initial height of a tab. */
2947 GetTextMetricsW(hdc, &fontMetrics);
2950 * Make sure there is enough space for the letters + growing the
2951 * selected item + extra space for the selected item.
2953 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
2954 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
2955 infoPtr->uVItemPadding;
2957 /* Initialize the width of a tab. */
2958 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
2959 infoPtr->tabMinWidth = 0;
2961 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
2963 SelectObject (hdc, hOldFont);
2964 ReleaseDC(hwnd, hdc);
2970 TAB_Destroy (TAB_INFO *infoPtr)
2977 SetWindowLongPtrW(infoPtr->hwnd, 0, 0);
2979 if (infoPtr->items) {
2980 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
2981 if (TAB_GetItem(infoPtr, iItem)->pszText)
2982 Free (TAB_GetItem(infoPtr, iItem)->pszText);
2984 Free (infoPtr->items);
2987 if (infoPtr->hwndToolTip)
2988 DestroyWindow (infoPtr->hwndToolTip);
2990 if (infoPtr->hwndUpDown)
2991 DestroyWindow(infoPtr->hwndUpDown);
2993 if (infoPtr->iHotTracked >= 0)
2994 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
3000 static LRESULT TAB_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
3004 return WVR_ALIGNTOP;
3007 static inline LRESULT
3008 TAB_SetItemExtra (TAB_INFO *infoPtr, INT cbInfo)
3010 if (!infoPtr || cbInfo <= 0)
3013 if (infoPtr->uNumItem)
3015 /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
3019 infoPtr->cbInfo = cbInfo;
3023 static LRESULT WINAPI
3024 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3026 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3028 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3029 if (!infoPtr && (uMsg != WM_CREATE))
3030 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
3034 case TCM_GETIMAGELIST:
3035 return TAB_GetImageList (infoPtr);
3037 case TCM_SETIMAGELIST:
3038 return TAB_SetImageList (infoPtr, (HIMAGELIST)lParam);
3040 case TCM_GETITEMCOUNT:
3041 return TAB_GetItemCount (infoPtr);
3045 return TAB_GetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_GETITEMW);
3049 return TAB_SetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_SETITEMW);
3051 case TCM_DELETEITEM:
3052 return TAB_DeleteItem (infoPtr, (INT)wParam);
3054 case TCM_DELETEALLITEMS:
3055 return TAB_DeleteAllItems (infoPtr);
3057 case TCM_GETITEMRECT:
3058 return TAB_GetItemRect (infoPtr, wParam, lParam);
3061 return TAB_GetCurSel (infoPtr);
3064 return TAB_HitTest (infoPtr, (LPTCHITTESTINFO)lParam);
3067 return TAB_SetCurSel (infoPtr, (INT)wParam);
3069 case TCM_INSERTITEMA:
3070 case TCM_INSERTITEMW:
3071 return TAB_InsertItemT (infoPtr, wParam, lParam, uMsg == TCM_INSERTITEMW);
3073 case TCM_SETITEMEXTRA:
3074 return TAB_SetItemExtra (infoPtr, (int)wParam);
3076 case TCM_ADJUSTRECT:
3077 return TAB_AdjustRect (infoPtr, (BOOL)wParam, (LPRECT)lParam);
3079 case TCM_SETITEMSIZE:
3080 return TAB_SetItemSize (infoPtr, lParam);
3082 case TCM_REMOVEIMAGE:
3083 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3086 case TCM_SETPADDING:
3087 return TAB_SetPadding (infoPtr, lParam);
3089 case TCM_GETROWCOUNT:
3090 return TAB_GetRowCount(infoPtr);
3092 case TCM_GETUNICODEFORMAT:
3093 return TAB_GetUnicodeFormat (infoPtr);
3095 case TCM_SETUNICODEFORMAT:
3096 return TAB_SetUnicodeFormat (infoPtr, (BOOL)wParam);
3098 case TCM_HIGHLIGHTITEM:
3099 return TAB_HighlightItem (infoPtr, (INT)wParam, (BOOL)LOWORD(lParam));
3101 case TCM_GETTOOLTIPS:
3102 return TAB_GetToolTips (infoPtr);
3104 case TCM_SETTOOLTIPS:
3105 return TAB_SetToolTips (infoPtr, (HWND)wParam);
3107 case TCM_GETCURFOCUS:
3108 return TAB_GetCurFocus (infoPtr);
3110 case TCM_SETCURFOCUS:
3111 return TAB_SetCurFocus (infoPtr, (INT)wParam);
3113 case TCM_SETMINTABWIDTH:
3114 return TAB_SetMinTabWidth(infoPtr, (INT)lParam);
3116 case TCM_DESELECTALL:
3117 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3120 case TCM_GETEXTENDEDSTYLE:
3121 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3124 case TCM_SETEXTENDEDSTYLE:
3125 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3129 return TAB_GetFont (infoPtr);
3132 return TAB_SetFont (infoPtr, (HFONT)wParam);
3135 return TAB_Create (hwnd, wParam, lParam);
3138 return TAB_Destroy (infoPtr);
3141 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3143 case WM_LBUTTONDOWN:
3144 return TAB_LButtonDown (infoPtr, wParam, lParam);
3147 return TAB_LButtonUp (infoPtr);
3150 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3152 case WM_RBUTTONDOWN:
3153 return TAB_RButtonDown (infoPtr);
3156 return TAB_MouseMove (infoPtr, wParam, lParam);
3159 return TAB_Paint (infoPtr, (HDC)wParam);
3162 return TAB_Size (infoPtr);
3165 return TAB_SetRedraw (infoPtr, (BOOL)wParam);
3168 return TAB_OnHScroll(infoPtr, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3170 case WM_STYLECHANGED:
3171 TAB_SetItemBounds (infoPtr);
3172 InvalidateRect(hwnd, NULL, TRUE);
3175 case WM_SYSCOLORCHANGE:
3176 COMCTL32_RefreshSysColors();
3181 TAB_FocusChanging(infoPtr);
3182 break; /* Don't disturb normal focus behavior */
3185 return TAB_KeyUp(infoPtr, wParam);
3187 return TAB_NCHitTest(infoPtr, lParam);
3190 return TAB_NCCalcSize(hwnd, wParam, lParam);
3193 if (uMsg >= WM_USER && uMsg < WM_APP)
3194 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3195 uMsg, wParam, lParam);
3198 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
3207 ZeroMemory (&wndClass, sizeof(WNDCLASSW));
3208 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3209 wndClass.lpfnWndProc = TAB_WindowProc;
3210 wndClass.cbClsExtra = 0;
3211 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3212 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3213 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
3214 wndClass.lpszClassName = WC_TABCONTROLW;
3216 RegisterClassW (&wndClass);
3221 TAB_Unregister (void)
3223 UnregisterClassW (WC_TABCONTROLW, NULL);