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
31 * TCM_GETEXTENDEDSTYLE
32 * TCM_SETEXTENDEDSTYLE
46 #include "wine/debug.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(tab);
57 RECT rect; /* bounding rectangle of the item relative to the
58 * leftmost item (the leftmost item, 0, would have a
59 * "left" member of 0 in this rectangle)
61 * additionally the top member holds the row number
62 * and bottom is unused and should be 0 */
63 BYTE extra[1]; /* Space for caller supplied info, variable size */
66 /* The size of a tab item depends on how much extra data is requested */
67 #define TAB_ITEM_SIZE(infoPtr) (sizeof(TAB_ITEM) - sizeof(BYTE) + infoPtr->cbInfo)
71 HWND hwnd; /* Tab control window */
72 HWND hwndNotify; /* notification window (parent) */
73 UINT uNumItem; /* number of tab items */
74 UINT uNumRows; /* number of tab rows */
75 INT tabHeight; /* height of the tab row */
76 INT tabWidth; /* width of tabs */
77 INT tabMinWidth; /* minimum width of items */
78 USHORT uHItemPadding; /* amount of horizontal padding, in pixels */
79 USHORT uVItemPadding; /* amount of vertical padding, in pixels */
80 USHORT uHItemPadding_s; /* Set amount of horizontal padding, in pixels */
81 USHORT uVItemPadding_s; /* Set amount of vertical padding, in pixels */
82 HFONT hFont; /* handle to the current font */
83 HCURSOR hcurArrow; /* handle to the current cursor */
84 HIMAGELIST himl; /* handle to an image list (may be 0) */
85 HWND hwndToolTip; /* handle to tab's tooltip */
86 INT leftmostVisible; /* Used for scrolling, this member contains
87 * the index of the first visible item */
88 INT iSelected; /* the currently selected item */
89 INT iHotTracked; /* the highlighted item under the mouse */
90 INT uFocus; /* item which has the focus */
91 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
92 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
93 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
94 * the size of the control */
95 BOOL fHeightSet; /* was the height of the tabs explicitly set? */
96 BOOL bUnicode; /* Unicode control? */
97 HWND hwndUpDown; /* Updown control used for scrolling */
98 INT cbInfo; /* Number of bytes of caller supplied info per tab */
101 /******************************************************************************
102 * Positioning constants
104 #define SELECTED_TAB_OFFSET 2
105 #define ROUND_CORNER_SIZE 2
106 #define DISPLAY_AREA_PADDINGX 2
107 #define DISPLAY_AREA_PADDINGY 2
108 #define CONTROL_BORDER_SIZEX 2
109 #define CONTROL_BORDER_SIZEY 2
110 #define BUTTON_SPACINGX 3
111 #define BUTTON_SPACINGY 3
112 #define FLAT_BTN_SPACINGX 8
113 #define DEFAULT_TAB_WIDTH 96
115 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongPtrW(hwnd,0))
116 /* Since items are variable sized, cannot directly access them */
117 #define TAB_GetItem(info,i) \
118 ((TAB_ITEM*)((LPBYTE)info->items + (i) * TAB_ITEM_SIZE(info)))
120 /******************************************************************************
121 * Hot-tracking timer constants
123 #define TAB_HOTTRACK_TIMER 1
124 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
126 /******************************************************************************
129 static void TAB_InvalidateTabArea(TAB_INFO *);
130 static void TAB_EnsureSelectionVisible(TAB_INFO *);
131 static void TAB_DrawItemInterior(TAB_INFO *, HDC, INT, RECT*);
134 TAB_SendSimpleNotify (const TAB_INFO *infoPtr, UINT code)
138 nmhdr.hwndFrom = infoPtr->hwnd;
139 nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID);
142 return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
143 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
147 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
148 WPARAM wParam, LPARAM lParam)
156 msg.time = GetMessageTime ();
157 msg.pt.x = LOWORD(GetMessagePos ());
158 msg.pt.y = HIWORD(GetMessagePos ());
160 SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
164 TAB_DumpItemExternalT(TCITEMW *pti, UINT iItem, BOOL isW)
167 TRACE("external tab %d, mask=0x%08x, dwState=0x%08lx, dwStateMask=0x%08lx, cchTextMax=0x%08x\n",
168 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
169 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
170 iItem, pti->iImage, pti->lParam, isW ? debugstr_w(pti->pszText) : debugstr_a((LPSTR)pti->pszText));
175 TAB_DumpItemInternal(TAB_INFO *infoPtr, UINT iItem)
180 ti = TAB_GetItem(infoPtr, iItem);
181 TRACE("tab %d, mask=0x%08x, dwState=0x%08lx, pszText=%s, iImage=%d\n",
182 iItem, ti->mask, ti->dwState, debugstr_w(ti->pszText),
184 TRACE("tab %d, rect.left=%ld, rect.top(row)=%ld\n",
185 iItem, ti->rect.left, ti->rect.top);
190 * the index of the selected tab, or -1 if no tab is selected. */
191 static inline LRESULT TAB_GetCurSel (const TAB_INFO *infoPtr)
193 return infoPtr->iSelected;
197 * the index of the tab item that has the focus
199 * we have not to return negative value
201 * test for windows */
202 static inline LRESULT
203 TAB_GetCurFocus (const TAB_INFO *infoPtr)
205 if (infoPtr->uFocus<0)
207 FIXME("we have not to return negative value");
210 return infoPtr->uFocus;
213 static inline LRESULT TAB_GetToolTips (const TAB_INFO *infoPtr)
215 if (infoPtr == NULL) return 0;
216 return (LRESULT)infoPtr->hwndToolTip;
219 static inline LRESULT TAB_SetCurSel (TAB_INFO *infoPtr, INT iItem)
223 if (iItem >= 0 && iItem < infoPtr->uNumItem) {
224 prevItem=infoPtr->iSelected;
225 infoPtr->iSelected=iItem;
226 TAB_EnsureSelectionVisible(infoPtr);
227 TAB_InvalidateTabArea(infoPtr);
232 static LRESULT TAB_SetCurFocus (TAB_INFO *infoPtr, INT iItem)
234 if (iItem < 0 || iItem >= infoPtr->uNumItem) return 0;
236 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS) {
237 FIXME("Should set input focus\n");
239 int oldFocus = infoPtr->uFocus;
240 if (infoPtr->iSelected != iItem || oldFocus == -1 ) {
241 infoPtr->uFocus = iItem;
242 if (oldFocus != -1) {
243 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING)) {
244 infoPtr->iSelected = iItem;
245 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
248 infoPtr->iSelected = iItem;
249 TAB_EnsureSelectionVisible(infoPtr);
250 TAB_InvalidateTabArea(infoPtr);
257 static inline LRESULT
258 TAB_SetToolTips (TAB_INFO *infoPtr, HWND hwndToolTip)
261 infoPtr->hwndToolTip = hwndToolTip;
265 static inline LRESULT
266 TAB_SetPadding (TAB_INFO *infoPtr, LPARAM lParam)
270 infoPtr->uHItemPadding_s=LOWORD(lParam);
271 infoPtr->uVItemPadding_s=HIWORD(lParam);
276 /******************************************************************************
277 * TAB_InternalGetItemRect
279 * This method will calculate the rectangle representing a given tab item in
280 * client coordinates. This method takes scrolling into account.
282 * This method returns TRUE if the item is visible in the window and FALSE
283 * if it is completely outside the client area.
285 static BOOL TAB_InternalGetItemRect(
286 const TAB_INFO* infoPtr,
291 RECT tmpItemRect,clientRect;
292 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
294 /* Perform a sanity check and a trivial visibility check. */
295 if ( (infoPtr->uNumItem <= 0) ||
296 (itemIndex >= infoPtr->uNumItem) ||
297 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
301 * Avoid special cases in this procedure by assigning the "out"
302 * parameters if the caller didn't supply them
304 if (itemRect == NULL)
305 itemRect = &tmpItemRect;
307 /* Retrieve the unmodified item rect. */
308 *itemRect = TAB_GetItem(infoPtr,itemIndex)->rect;
310 /* calculate the times bottom and top based on the row */
311 GetClientRect(infoPtr->hwnd, &clientRect);
313 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
315 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * infoPtr->tabHeight -
316 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
317 itemRect->left = itemRect->right - infoPtr->tabHeight;
319 else if (lStyle & TCS_VERTICAL)
321 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * infoPtr->tabHeight +
322 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
323 itemRect->right = itemRect->left + infoPtr->tabHeight;
325 else if (lStyle & TCS_BOTTOM)
327 itemRect->bottom = clientRect.bottom - itemRect->top * infoPtr->tabHeight -
328 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
329 itemRect->top = itemRect->bottom - infoPtr->tabHeight;
331 else /* not TCS_BOTTOM and not TCS_VERTICAL */
333 itemRect->top = clientRect.top + itemRect->top * infoPtr->tabHeight +
334 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
335 itemRect->bottom = itemRect->top + infoPtr->tabHeight;
339 * "scroll" it to make sure the item at the very left of the
340 * tab control is the leftmost visible tab.
342 if(lStyle & TCS_VERTICAL)
346 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.top);
349 * Move the rectangle so the first item is slightly offset from
350 * the bottom of the tab control.
354 SELECTED_TAB_OFFSET);
359 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.left,
363 * Move the rectangle so the first item is slightly offset from
364 * the left of the tab control.
370 TRACE("item %d tab h=%d, rect=(%ld,%ld)-(%ld,%ld)\n",
371 itemIndex, infoPtr->tabHeight,
372 itemRect->left, itemRect->top, itemRect->right, itemRect->bottom);
374 /* Now, calculate the position of the item as if it were selected. */
375 if (selectedRect!=NULL)
377 CopyRect(selectedRect, itemRect);
379 /* The rectangle of a selected item is a bit wider. */
380 if(lStyle & TCS_VERTICAL)
381 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
383 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
385 /* If it also a bit higher. */
386 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
388 selectedRect->left -= 2; /* the border is thicker on the right */
389 selectedRect->right += SELECTED_TAB_OFFSET;
391 else if (lStyle & TCS_VERTICAL)
393 selectedRect->left -= SELECTED_TAB_OFFSET;
394 selectedRect->right += 1;
396 else if (lStyle & TCS_BOTTOM)
398 selectedRect->bottom += SELECTED_TAB_OFFSET;
400 else /* not TCS_BOTTOM and not TCS_VERTICAL */
402 selectedRect->top -= SELECTED_TAB_OFFSET;
403 selectedRect->bottom -= 1;
407 /* Check for visibility */
408 if (lStyle & TCS_VERTICAL)
409 return (itemRect->top < clientRect.bottom) && (itemRect->bottom > clientRect.top);
411 return (itemRect->left < clientRect.right) && (itemRect->right > clientRect.left);
415 TAB_GetItemRect(TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
417 return TAB_InternalGetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam, (LPRECT)NULL);
420 /******************************************************************************
423 * This method is called to handle keyboard input
425 static LRESULT TAB_KeyUp(TAB_INFO* infoPtr, WPARAM keyCode)
432 newItem = infoPtr->uFocus - 1;
435 newItem = infoPtr->uFocus + 1;
440 * If we changed to a valid item, change the selection
443 newItem < infoPtr->uNumItem &&
444 infoPtr->uFocus != newItem)
446 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
448 infoPtr->iSelected = newItem;
449 infoPtr->uFocus = newItem;
450 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
452 TAB_EnsureSelectionVisible(infoPtr);
453 TAB_InvalidateTabArea(infoPtr);
460 /******************************************************************************
463 * This method is called whenever the focus goes in or out of this control
464 * it is used to update the visual state of the control.
466 static void TAB_FocusChanging(const TAB_INFO *infoPtr)
472 * Get the rectangle for the item.
474 isVisible = TAB_InternalGetItemRect(infoPtr,
480 * If the rectangle is not completely invisible, invalidate that
481 * portion of the window.
485 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
486 selectedRect.left,selectedRect.top,
487 selectedRect.right,selectedRect.bottom);
488 InvalidateRect(infoPtr->hwnd, &selectedRect, TRUE);
492 static INT TAB_InternalHitTest (
501 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
503 TAB_InternalGetItemRect(infoPtr, iCount, &rect, NULL);
505 if (PtInRect(&rect, pt))
507 *flags = TCHT_ONITEM;
512 *flags = TCHT_NOWHERE;
516 static inline LRESULT
517 TAB_HitTest (TAB_INFO *infoPtr, LPTCHITTESTINFO lptest)
519 return TAB_InternalHitTest (infoPtr, lptest->pt, &lptest->flags);
522 /******************************************************************************
525 * Napster v2b5 has a tab control for its main navigation which has a client
526 * area that covers the whole area of the dialog pages.
527 * That's why it receives all msgs for that area and the underlying dialog ctrls
529 * So I decided that we should handle WM_NCHITTEST here and return
530 * HTTRANSPARENT if we don't hit the tab control buttons.
531 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
532 * doesn't do it that way. Maybe depends on tab control styles ?
534 static inline LRESULT
535 TAB_NCHitTest (TAB_INFO *infoPtr, LPARAM lParam)
540 pt.x = LOWORD(lParam);
541 pt.y = HIWORD(lParam);
542 ScreenToClient(infoPtr->hwnd, &pt);
544 if (TAB_InternalHitTest(infoPtr, pt, &dummyflag) == -1)
545 return HTTRANSPARENT;
551 TAB_LButtonDown (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
556 if (infoPtr->hwndToolTip)
557 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
558 WM_LBUTTONDOWN, wParam, lParam);
560 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
561 SetFocus (infoPtr->hwnd);
564 if (infoPtr->hwndToolTip)
565 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
566 WM_LBUTTONDOWN, wParam, lParam);
568 pt.x = (INT)LOWORD(lParam);
569 pt.y = (INT)HIWORD(lParam);
571 newItem = TAB_InternalHitTest (infoPtr, pt, &dummy);
573 TRACE("On Tab, item %d\n", newItem);
575 if (newItem != -1 && infoPtr->iSelected != newItem)
577 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
579 infoPtr->iSelected = newItem;
580 infoPtr->uFocus = newItem;
581 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
583 TAB_EnsureSelectionVisible(infoPtr);
585 TAB_InvalidateTabArea(infoPtr);
591 static inline LRESULT
592 TAB_LButtonUp (const TAB_INFO *infoPtr)
594 TAB_SendSimpleNotify(infoPtr, NM_CLICK);
599 static inline LRESULT
600 TAB_RButtonDown (const TAB_INFO *infoPtr)
602 TAB_SendSimpleNotify(infoPtr, NM_RCLICK);
606 /******************************************************************************
607 * TAB_DrawLoneItemInterior
609 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
610 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
611 * up the device context and font. This routine does the same setup but
612 * only calls TAB_DrawItemInterior for the single specified item.
615 TAB_DrawLoneItemInterior(TAB_INFO* infoPtr, int iItem)
617 HDC hdc = GetDC(infoPtr->hwnd);
620 /* Clip UpDown control to not draw over it */
621 if (infoPtr->needsScrolling)
623 GetWindowRect(infoPtr->hwnd, &rC);
624 GetWindowRect(infoPtr->hwndUpDown, &r);
625 ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top);
627 TAB_DrawItemInterior(infoPtr, hdc, iItem, NULL);
628 ReleaseDC(infoPtr->hwnd, hdc);
631 /******************************************************************************
632 * TAB_HotTrackTimerProc
634 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
635 * timer is setup so we can check if the mouse is moved out of our window.
636 * (We don't get an event when the mouse leaves, the mouse-move events just
637 * stop being delivered to our window and just start being delivered to
638 * another window.) This function is called when the timer triggers so
639 * we can check if the mouse has left our window. If so, we un-highlight
640 * the hot-tracked tab.
643 TAB_HotTrackTimerProc
645 HWND hwnd, /* handle of window for timer messages */
646 UINT uMsg, /* WM_TIMER message */
647 UINT idEvent, /* timer identifier */
648 DWORD dwTime /* current system time */
651 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
653 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
658 ** If we can't get the cursor position, or if the cursor is outside our
659 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
660 ** "outside" even if it is within our bounding rect if another window
661 ** overlaps. Note also that the case where the cursor stayed within our
662 ** window but has moved off the hot-tracked tab will be handled by the
663 ** WM_MOUSEMOVE event.
665 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
667 /* Redraw iHotTracked to look normal */
668 INT iRedraw = infoPtr->iHotTracked;
669 infoPtr->iHotTracked = -1;
670 TAB_DrawLoneItemInterior(infoPtr, iRedraw);
672 /* Kill this timer */
673 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
678 /******************************************************************************
681 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
682 * should be highlighted. This function determines which tab in a tab control,
683 * if any, is under the mouse and records that information. The caller may
684 * supply output parameters to receive the item number of the tab item which
685 * was highlighted but isn't any longer and of the tab item which is now
686 * highlighted but wasn't previously. The caller can use this information to
687 * selectively redraw those tab items.
689 * If the caller has a mouse position, it can supply it through the pos
690 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
691 * supplies NULL and this function determines the current mouse position
699 int* out_redrawLeave,
706 if (out_redrawLeave != NULL)
707 *out_redrawLeave = -1;
708 if (out_redrawEnter != NULL)
709 *out_redrawEnter = -1;
711 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_HOTTRACK)
719 ScreenToClient(infoPtr->hwnd, &pt);
727 item = TAB_InternalHitTest(infoPtr, pt, &flags);
730 if (item != infoPtr->iHotTracked)
732 if (infoPtr->iHotTracked >= 0)
734 /* Mark currently hot-tracked to be redrawn to look normal */
735 if (out_redrawLeave != NULL)
736 *out_redrawLeave = infoPtr->iHotTracked;
740 /* Kill timer which forces recheck of mouse pos */
741 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
746 /* Start timer so we recheck mouse pos */
747 UINT timerID = SetTimer
751 TAB_HOTTRACK_TIMER_INTERVAL,
752 TAB_HotTrackTimerProc
756 return; /* Hot tracking not available */
759 infoPtr->iHotTracked = item;
763 /* Mark new hot-tracked to be redrawn to look highlighted */
764 if (out_redrawEnter != NULL)
765 *out_redrawEnter = item;
770 /******************************************************************************
773 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
776 TAB_MouseMove (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
781 if (infoPtr->hwndToolTip)
782 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
783 WM_LBUTTONDOWN, wParam, lParam);
785 /* Determine which tab to highlight. Redraw tabs which change highlight
787 TAB_RecalcHotTrack(infoPtr, &lParam, &redrawLeave, &redrawEnter);
789 if (redrawLeave != -1)
790 TAB_DrawLoneItemInterior(infoPtr, redrawLeave);
791 if (redrawEnter != -1)
792 TAB_DrawLoneItemInterior(infoPtr, redrawEnter);
797 /******************************************************************************
800 * Calculates the tab control's display area given the window rectangle or
801 * the window rectangle given the requested display rectangle.
803 static LRESULT TAB_AdjustRect(
808 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
809 LONG *iRightBottom, *iLeftTop;
811 TRACE ("hwnd=%p fLarger=%d (%ld,%ld)-(%ld,%ld)\n", infoPtr->hwnd, fLarger, prc->left, prc->top, prc->right, prc->bottom);
813 if(lStyle & TCS_VERTICAL)
815 iRightBottom = &(prc->right);
816 iLeftTop = &(prc->left);
820 iRightBottom = &(prc->bottom);
821 iLeftTop = &(prc->top);
824 if (fLarger) /* Go from display rectangle */
826 /* Add the height of the tabs. */
827 if (lStyle & TCS_BOTTOM)
828 *iRightBottom += infoPtr->tabHeight * infoPtr->uNumRows;
830 *iLeftTop -= infoPtr->tabHeight * infoPtr->uNumRows +
831 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
833 /* Inflate the rectangle for the padding */
834 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
836 /* Inflate for the border */
837 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEY);
839 else /* Go from window rectangle. */
841 /* Deflate the rectangle for the border */
842 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEY);
844 /* Deflate the rectangle for the padding */
845 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
847 /* Remove the height of the tabs. */
848 if (lStyle & TCS_BOTTOM)
849 *iRightBottom -= infoPtr->tabHeight * infoPtr->uNumRows;
851 *iLeftTop += (infoPtr->tabHeight) * infoPtr->uNumRows +
852 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
858 /******************************************************************************
861 * This method will handle the notification from the scroll control and
862 * perform the scrolling operation on the tab control.
864 static LRESULT TAB_OnHScroll(
870 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
872 if(nPos < infoPtr->leftmostVisible)
873 infoPtr->leftmostVisible--;
875 infoPtr->leftmostVisible++;
877 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
878 TAB_InvalidateTabArea(infoPtr);
879 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
880 MAKELONG(infoPtr->leftmostVisible, 0));
886 /******************************************************************************
889 * This method will check the current scrolling state and make sure the
890 * scrolling control is displayed (or not).
892 static void TAB_SetupScrolling(
895 const RECT* clientRect)
897 static const WCHAR msctls_updown32W[] = { 'm','s','c','t','l','s','_','u','p','d','o','w','n','3','2',0 };
898 static const WCHAR emptyW[] = { 0 };
900 DWORD lStyle = GetWindowLongW(hwnd, GWL_STYLE);
902 if (infoPtr->needsScrolling)
908 * Calculate the position of the scroll control.
910 if(lStyle & TCS_VERTICAL)
912 controlPos.right = clientRect->right;
913 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
915 if (lStyle & TCS_BOTTOM)
917 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
918 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
922 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
923 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
928 controlPos.right = clientRect->right;
929 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
931 if (lStyle & TCS_BOTTOM)
933 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
934 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
938 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
939 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
944 * If we don't have a scroll control yet, we want to create one.
945 * If we have one, we want to make sure it's positioned properly.
947 if (infoPtr->hwndUpDown==0)
949 infoPtr->hwndUpDown = CreateWindowW(msctls_updown32W, emptyW,
950 WS_VISIBLE | WS_CHILD | UDS_HORZ,
951 controlPos.left, controlPos.top,
952 controlPos.right - controlPos.left,
953 controlPos.bottom - controlPos.top,
954 hwnd, NULL, NULL, NULL);
958 SetWindowPos(infoPtr->hwndUpDown,
960 controlPos.left, controlPos.top,
961 controlPos.right - controlPos.left,
962 controlPos.bottom - controlPos.top,
963 SWP_SHOWWINDOW | SWP_NOZORDER);
966 /* Now calculate upper limit of the updown control range.
967 * We do this by calculating how many tabs will be offscreen when the
968 * last tab is visible.
970 if(infoPtr->uNumItem)
972 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
973 maxRange = infoPtr->uNumItem;
974 tabwidth = TAB_GetItem(infoPtr, infoPtr->uNumItem - 1)->rect.right;
976 for(; maxRange > 0; maxRange--)
978 if(tabwidth - TAB_GetItem(infoPtr,maxRange - 1)->rect.left > vsize)
982 if(maxRange == infoPtr->uNumItem)
988 /* If we once had a scroll control... hide it */
989 if (infoPtr->hwndUpDown!=0)
990 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
992 if (infoPtr->hwndUpDown)
993 SendMessageW(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
996 /******************************************************************************
999 * This method will calculate the position rectangles of all the items in the
1000 * control. The rectangle calculated starts at 0 for the first item in the
1001 * list and ignores scrolling and selection.
1002 * It also uses the current font to determine the height of the tab row and
1003 * it checks if all the tabs fit in the client area of the window. If they
1004 * don't, a scrolling control is added.
1006 static void TAB_SetItemBounds (TAB_INFO *infoPtr)
1008 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1009 TEXTMETRICW fontMetrics;
1012 INT curItemRowCount;
1013 HFONT hFont, hOldFont;
1023 * We need to get text information so we need a DC and we need to select
1026 hdc = GetDC(infoPtr->hwnd);
1028 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1029 hOldFont = SelectObject (hdc, hFont);
1032 * We will base the rectangle calculations on the client rectangle
1035 GetClientRect(infoPtr->hwnd, &clientRect);
1037 /* if TCS_VERTICAL then swap the height and width so this code places the
1038 tabs along the top of the rectangle and we can just rotate them after
1039 rather than duplicate all of the below code */
1040 if(lStyle & TCS_VERTICAL)
1042 iTemp = clientRect.bottom;
1043 clientRect.bottom = clientRect.right;
1044 clientRect.right = iTemp;
1047 /* Now use hPadding and vPadding */
1048 infoPtr->uHItemPadding = infoPtr->uHItemPadding_s;
1049 infoPtr->uVItemPadding = infoPtr->uVItemPadding_s;
1051 /* The leftmost item will be "0" aligned */
1053 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1055 if (!(infoPtr->fHeightSet))
1058 int icon_height = 0;
1060 /* Use the current font to determine the height of a tab. */
1061 GetTextMetricsW(hdc, &fontMetrics);
1063 /* Get the icon height */
1065 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1067 /* Take the highest between font or icon */
1068 if (fontMetrics.tmHeight > icon_height)
1069 item_height = fontMetrics.tmHeight + 2;
1071 item_height = icon_height;
1074 * Make sure there is enough space for the letters + icon + growing the
1075 * selected item + extra space for the selected item.
1077 infoPtr->tabHeight = item_height +
1078 ((lStyle & TCS_BUTTONS) ? 2 : 1) *
1079 infoPtr->uVItemPadding;
1081 TRACE("tabH=%d, tmH=%ld, iconh=%d\n",
1082 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1085 TRACE("client right=%ld\n", clientRect.right);
1087 /* Get the icon width */
1090 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1092 if (lStyle & TCS_FIXEDWIDTH)
1095 /* Add padding if icon is present */
1096 icon_width += infoPtr->uHItemPadding;
1099 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1101 TAB_ITEM *curr = TAB_GetItem(infoPtr, curItem);
1103 /* Set the leftmost position of the tab. */
1104 curr->rect.left = curItemLeftPos;
1106 if ((lStyle & TCS_FIXEDWIDTH) || !curr->pszText)
1108 curr->rect.right = curr->rect.left +
1109 max(infoPtr->tabWidth, icon_width);
1115 /* Calculate how wide the tab is depending on the text it contains */
1116 GetTextExtentPoint32W(hdc, curr->pszText,
1117 lstrlenW(curr->pszText), &size);
1119 curr->rect.right = curr->rect.left + size.cx + icon_width +
1120 num * infoPtr->uHItemPadding;
1121 TRACE("for <%s>, l,r=%ld,%ld, num=%d\n",
1122 debugstr_w(curr->pszText), curr->rect.left, curr->rect.right, num);
1126 * Check if this is a multiline tab control and if so
1127 * check to see if we should wrap the tabs
1129 * Wrap all these tabs. We will arrange them evenly later.
1133 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1135 (clientRect.right - CONTROL_BORDER_SIZEX - DISPLAY_AREA_PADDINGX)))
1137 curr->rect.right -= curr->rect.left;
1139 curr->rect.left = 0;
1141 TRACE("wrapping <%s>, l,r=%ld,%ld\n", debugstr_w(curr->pszText),
1142 curr->rect.left, curr->rect.right);
1145 curr->rect.bottom = 0;
1146 curr->rect.top = curItemRowCount - 1;
1148 TRACE("TextSize: %li\n", size.cx);
1149 TRACE("Rect: T %li, L %li, B %li, R %li\n", curr->rect.top,
1150 curr->rect.left, curr->rect.bottom, curr->rect.right);
1153 * The leftmost position of the next item is the rightmost position
1156 if (lStyle & TCS_BUTTONS)
1158 curItemLeftPos = curr->rect.right + BUTTON_SPACINGX;
1159 if (lStyle & TCS_FLATBUTTONS)
1160 curItemLeftPos += FLAT_BTN_SPACINGX;
1163 curItemLeftPos = curr->rect.right;
1166 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1169 * Check if we need a scrolling control.
1171 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1174 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1175 if(!infoPtr->needsScrolling)
1176 infoPtr->leftmostVisible = 0;
1181 * No scrolling in Multiline or Vertical styles.
1183 infoPtr->needsScrolling = FALSE;
1184 infoPtr->leftmostVisible = 0;
1186 TAB_SetupScrolling(infoPtr->hwnd, infoPtr, &clientRect);
1188 /* Set the number of rows */
1189 infoPtr->uNumRows = curItemRowCount;
1191 /* Arrange all tabs evenly if style says so */
1192 if (!(lStyle & TCS_RAGGEDRIGHT) && ((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->uNumItem > 0))
1194 INT tabPerRow,remTab,iRow;
1199 * Ok windows tries to even out the rows. place the same
1200 * number of tabs in each row. So lets give that a shot
1203 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1204 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1206 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1207 iItm<infoPtr->uNumItem;
1210 /* normalize the current rect */
1211 TAB_ITEM *curr = TAB_GetItem(infoPtr, iItm);
1213 /* shift the item to the left side of the clientRect */
1214 curr->rect.right -= curr->rect.left;
1215 curr->rect.left = 0;
1217 TRACE("r=%ld, cl=%d, cl.r=%ld, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1218 curr->rect.right, curItemLeftPos, clientRect.right,
1219 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1221 /* if we have reached the maximum number of tabs on this row */
1222 /* move to the next row, reset our current item left position and */
1223 /* the count of items on this row */
1225 if (lStyle & TCS_VERTICAL) {
1226 /* Vert: Add the remaining tabs in the *last* remainder rows */
1227 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1233 /* Horz: Add the remaining tabs in the *first* remainder rows */
1234 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1241 /* shift the item to the right to place it as the next item in this row */
1242 curr->rect.left += curItemLeftPos;
1243 curr->rect.right += curItemLeftPos;
1244 curr->rect.top = iRow;
1245 if (lStyle & TCS_BUTTONS)
1247 curItemLeftPos = curr->rect.right + 1;
1248 if (lStyle & TCS_FLATBUTTONS)
1249 curItemLeftPos += FLAT_BTN_SPACINGX;
1252 curItemLeftPos = curr->rect.right;
1254 TRACE("arranging <%s>, l,r=%ld,%ld, row=%ld\n",
1255 debugstr_w(curr->pszText), curr->rect.left,
1256 curr->rect.right, curr->rect.top);
1263 INT widthDiff, iIndexStart=0, iIndexEnd=0;
1267 while(iIndexStart < infoPtr->uNumItem)
1269 TAB_ITEM *start = TAB_GetItem(infoPtr, iIndexStart);
1272 * find the index of the row
1274 /* find the first item on the next row */
1275 for (iIndexEnd=iIndexStart;
1276 (iIndexEnd < infoPtr->uNumItem) &&
1277 (TAB_GetItem(infoPtr, iIndexEnd)->rect.top ==
1280 /* intentionally blank */;
1283 * we need to justify these tabs so they fill the whole given
1287 /* find the amount of space remaining on this row */
1288 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1289 TAB_GetItem(infoPtr, iIndexEnd - 1)->rect.right;
1291 /* iCount is the number of tab items on this row */
1292 iCount = iIndexEnd - iIndexStart;
1296 remainder = widthDiff % iCount;
1297 widthDiff = widthDiff / iCount;
1298 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1299 for (iIndex=iIndexStart, iCount=0; iIndex < iIndexEnd; iIndex++, iCount++)
1301 TAB_ITEM *item = TAB_GetItem(infoPtr, iIndex);
1303 item->rect.left += iCount * widthDiff;
1304 item->rect.right += (iCount + 1) * widthDiff;
1306 TRACE("adjusting 1 <%s>, l,r=%ld,%ld\n",
1307 debugstr_w(item->pszText),
1308 item->rect.left, item->rect.right);
1311 TAB_GetItem(infoPtr, iIndex - 1)->rect.right += remainder;
1313 else /* we have only one item on this row, make it take up the entire row */
1315 start->rect.left = clientRect.left;
1316 start->rect.right = clientRect.right - 4;
1318 TRACE("adjusting 2 <%s>, l,r=%ld,%ld\n",
1319 debugstr_w(start->pszText),
1320 start->rect.left, start->rect.right);
1325 iIndexStart = iIndexEnd;
1330 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1331 if(lStyle & TCS_VERTICAL)
1334 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1336 rcItem = &TAB_GetItem(infoPtr, iIndex)->rect;
1338 rcOriginal = *rcItem;
1340 /* this is rotating the items by 90 degrees clockwise around the center of the control */
1341 rcItem->top = (rcOriginal.left - clientRect.left);
1342 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1343 rcItem->left = rcOriginal.top;
1344 rcItem->right = rcOriginal.bottom;
1348 TAB_EnsureSelectionVisible(infoPtr);
1349 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
1352 SelectObject (hdc, hOldFont);
1353 ReleaseDC (infoPtr->hwnd, hdc);
1358 TAB_EraseTabInterior
1366 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1367 HBRUSH hbr = CreateSolidBrush (comctl32_color.clrBtnFace);
1368 BOOL deleteBrush = TRUE;
1369 RECT rTemp = *drawRect;
1371 InflateRect(&rTemp, -2, -2);
1372 if (lStyle & TCS_BUTTONS)
1374 if (iItem == infoPtr->iSelected)
1376 /* Background color */
1377 if (!(lStyle & TCS_OWNERDRAWFIXED))
1380 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1382 SetTextColor(hdc, comctl32_color.clr3dFace);
1383 SetBkColor(hdc, comctl32_color.clr3dHilight);
1385 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1386 * we better use 0x55aa bitmap brush to make scrollbar's background
1387 * look different from the window background.
1389 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1390 hbr = COMCTL32_hPattern55AABrush;
1392 deleteBrush = FALSE;
1394 FillRect(hdc, &rTemp, hbr);
1396 else /* ! selected */
1398 if (lStyle & TCS_FLATBUTTONS)
1400 FillRect(hdc, drawRect, hbr);
1401 if (iItem == infoPtr->iHotTracked)
1402 DrawEdge(hdc, drawRect, EDGE_RAISED, BF_SOFT|BF_RECT);
1405 FillRect(hdc, &rTemp, hbr);
1409 else /* !TCS_BUTTONS */
1411 FillRect(hdc, &rTemp, hbr);
1415 if (deleteBrush) DeleteObject(hbr);
1418 /******************************************************************************
1419 * TAB_DrawItemInterior
1421 * This method is used to draw the interior (text and icon) of a single tab
1422 * into the tab control.
1425 TAB_DrawItemInterior
1433 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1442 /* if (drawRect == NULL) */
1449 * Get the rectangle for the item.
1451 isVisible = TAB_InternalGetItemRect(infoPtr, iItem, &itemRect, &selectedRect);
1456 * Make sure drawRect points to something valid; simplifies code.
1458 drawRect = &localRect;
1461 * This logic copied from the part of TAB_DrawItem which draws
1462 * the tab background. It's important to keep it in sync. I
1463 * would have liked to avoid code duplication, but couldn't figure
1464 * out how without making spaghetti of TAB_DrawItem.
1466 if (iItem == infoPtr->iSelected)
1467 *drawRect = selectedRect;
1469 *drawRect = itemRect;
1471 if (lStyle & TCS_BUTTONS)
1473 if (iItem == infoPtr->iSelected)
1475 drawRect->left += 4;
1477 drawRect->right -= 4;
1478 drawRect->bottom -= 1;
1482 drawRect->left += 2;
1484 drawRect->right -= 2;
1485 drawRect->bottom -= 2;
1490 if ((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1492 if (iItem != infoPtr->iSelected)
1494 drawRect->left += 2;
1496 drawRect->bottom -= 2;
1499 else if (lStyle & TCS_VERTICAL)
1501 if (iItem == infoPtr->iSelected)
1503 drawRect->right += 1;
1508 drawRect->right -= 2;
1509 drawRect->bottom -= 2;
1512 else if (lStyle & TCS_BOTTOM)
1514 if (iItem == infoPtr->iSelected)
1520 InflateRect(drawRect, -2, -2);
1521 drawRect->bottom += 2;
1526 if (iItem == infoPtr->iSelected)
1528 drawRect->bottom += 3;
1532 drawRect->bottom -= 2;
1533 InflateRect(drawRect, -2, 0);
1538 TRACE("drawRect=(%ld,%ld)-(%ld,%ld)\n",
1539 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom);
1541 /* Clear interior */
1542 TAB_EraseTabInterior (infoPtr, hdc, iItem, drawRect);
1544 /* Draw the focus rectangle */
1545 if (!(lStyle & TCS_FOCUSNEVER) &&
1546 (GetFocus() == infoPtr->hwnd) &&
1547 (iItem == infoPtr->uFocus) )
1549 RECT rFocus = *drawRect;
1550 InflateRect(&rFocus, -3, -3);
1551 if (lStyle & TCS_BOTTOM && !(lStyle & TCS_VERTICAL))
1553 if (lStyle & TCS_BUTTONS)
1559 DrawFocusRect(hdc, &rFocus);
1565 htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1566 holdPen = SelectObject(hdc, htextPen);
1567 hOldFont = SelectObject(hdc, infoPtr->hFont);
1570 * Setup for text output
1572 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1573 SetTextColor(hdc, (((iItem == infoPtr->iHotTracked) && !(lStyle & TCS_FLATBUTTONS)) |
1574 (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED)) ?
1575 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1578 * if owner draw, tell the owner to draw
1580 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(infoPtr->hwnd))
1586 drawRect->right -= 1;
1587 if ( iItem == infoPtr->iSelected )
1589 drawRect->right -= 1;
1590 drawRect->left += 1;
1594 * get the control id
1596 id = (UINT)GetWindowLongPtrW( infoPtr->hwnd, GWLP_ID );
1599 * put together the DRAWITEMSTRUCT
1601 dis.CtlType = ODT_TAB;
1604 dis.itemAction = ODA_DRAWENTIRE;
1606 if ( iItem == infoPtr->iSelected )
1607 dis.itemState |= ODS_SELECTED;
1608 if (infoPtr->uFocus == iItem)
1609 dis.itemState |= ODS_FOCUS;
1610 dis.hwndItem = infoPtr->hwnd;
1612 CopyRect(&dis.rcItem,drawRect);
1613 dis.itemData = (ULONG_PTR)TAB_GetItem(infoPtr, iItem)->extra;
1616 * send the draw message
1618 SendMessageW( infoPtr->hwndNotify, WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1622 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
1626 /* used to center the icon and text in the tab */
1628 INT center_offset_h, center_offset_v;
1630 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1631 rcImage = *drawRect;
1635 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1637 /* get the rectangle that the text fits in */
1640 DrawTextW(hdc, item->pszText, -1, &rcText, DT_CALCRECT);
1643 * If not owner draw, then do the drawing ourselves.
1647 if (infoPtr->himl && (item->mask & TCIF_IMAGE))
1652 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1654 if(lStyle & TCS_VERTICAL)
1656 center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1657 center_offset_v = ((drawRect->right - drawRect->left) - (cx + infoPtr->uVItemPadding)) / 2;
1661 center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1662 center_offset_v = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uVItemPadding)) / 2;
1665 if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1666 center_offset_h = infoPtr->uHItemPadding;
1668 if (center_offset_h < 2)
1669 center_offset_h = 2;
1671 if (center_offset_v < 0)
1672 center_offset_v = 0;
1674 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1675 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1676 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1677 (rcText.right-rcText.left));
1679 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1681 rcImage.top = drawRect->top + center_offset_h;
1682 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1683 /* right side of the tab, but the image still uses the left as its x position */
1684 /* this keeps the image always drawn off of the same side of the tab */
1685 rcImage.left = drawRect->right - cx - center_offset_v;
1686 drawRect->top += cy + infoPtr->uHItemPadding;
1688 else if(lStyle & TCS_VERTICAL)
1690 rcImage.top = drawRect->bottom - cy - center_offset_h;
1691 rcImage.left = drawRect->left + center_offset_v;
1692 drawRect->bottom -= cy + infoPtr->uHItemPadding;
1694 else /* normal style, whether TCS_BOTTOM or not */
1696 rcImage.left = drawRect->left + center_offset_h;
1697 rcImage.top = drawRect->top + center_offset_v;
1698 drawRect->left += cx + infoPtr->uHItemPadding;
1701 TRACE("drawing image=%d, left=%ld, top=%ld\n",
1702 item->iImage, rcImage.left, rcImage.top-1);
1714 /* Now position text */
1715 if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT)
1716 center_offset_h = infoPtr->uHItemPadding;
1718 if(lStyle & TCS_VERTICAL)
1719 center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1721 center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1723 if(lStyle & TCS_VERTICAL)
1725 if(lStyle & TCS_BOTTOM)
1726 drawRect->top+=center_offset_h;
1728 drawRect->bottom-=center_offset_h;
1730 center_offset_v = ((drawRect->right - drawRect->left) - (rcText.bottom - rcText.top)) / 2;
1734 drawRect->left += center_offset_h;
1735 center_offset_v = ((drawRect->bottom - drawRect->top) - (rcText.bottom - rcText.top)) / 2;
1738 /* if an item is selected, the text is shifted up instead of down */
1739 if (iItem == infoPtr->iSelected)
1740 center_offset_v -= infoPtr->uVItemPadding / 2;
1742 center_offset_v += infoPtr->uVItemPadding / 2;
1744 if (center_offset_v < 0)
1745 center_offset_v = 0;
1747 if(lStyle & TCS_VERTICAL)
1748 drawRect->left += center_offset_v;
1750 drawRect->top += center_offset_v;
1753 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1755 static const WCHAR ArialW[] = { 'A','r','i','a','l',0 };
1758 INT nEscapement = 900;
1759 INT nOrientation = 900;
1761 if(lStyle & TCS_BOTTOM)
1764 nOrientation = -900;
1767 /* to get a font with the escapement and orientation we are looking for, we need to */
1768 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1769 if (!GetObjectW((infoPtr->hFont) ?
1770 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1771 sizeof(LOGFONTW),&logfont))
1775 lstrcpyW(logfont.lfFaceName, ArialW);
1776 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1778 logfont.lfWeight = FW_NORMAL;
1779 logfont.lfItalic = 0;
1780 logfont.lfUnderline = 0;
1781 logfont.lfStrikeOut = 0;
1784 logfont.lfEscapement = nEscapement;
1785 logfont.lfOrientation = nOrientation;
1786 hFont = CreateFontIndirectW(&logfont);
1787 SelectObject(hdc, hFont);
1792 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1793 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1797 lstrlenW(item->pszText),
1801 DeleteObject(hFont);
1805 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1806 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1807 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1808 (rcText.right-rcText.left));
1815 lstrlenW(item->pszText),
1817 DT_LEFT | DT_SINGLELINE
1822 *drawRect = rcTemp; /* restore drawRect */
1828 SelectObject(hdc, hOldFont);
1829 SetBkMode(hdc, oldBkMode);
1830 SelectObject(hdc, holdPen);
1831 DeleteObject( htextPen );
1834 /******************************************************************************
1837 * This method is used to draw a single tab into the tab control.
1839 static void TAB_DrawItem(
1844 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1848 RECT r, fillRect, r1;
1851 COLORREF bkgnd, corner;
1854 * Get the rectangle for the item.
1856 isVisible = TAB_InternalGetItemRect(infoPtr,
1865 /* Clip UpDown control to not draw over it */
1866 if (infoPtr->needsScrolling)
1868 GetWindowRect(infoPtr->hwnd, &rC);
1869 GetWindowRect(infoPtr->hwndUpDown, &rUD);
1870 ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
1873 /* If you need to see what the control is doing,
1874 * then override these variables. They will change what
1875 * fill colors are used for filling the tabs, and the
1876 * corners when drawing the edge.
1878 bkgnd = comctl32_color.clrBtnFace;
1879 corner = comctl32_color.clrBtnFace;
1881 if (lStyle & TCS_BUTTONS)
1883 /* Get item rectangle */
1886 /* Separators between flat buttons */
1887 if (lStyle & TCS_FLATBUTTONS)
1890 r1.right += (FLAT_BTN_SPACINGX -2);
1891 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1894 if (iItem == infoPtr->iSelected)
1896 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1898 OffsetRect(&r, 1, 1);
1900 else /* ! selected */
1902 if (!(lStyle & TCS_FLATBUTTONS))
1903 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1906 else /* !TCS_BUTTONS */
1908 /* We draw a rectangle of different sizes depending on the selection
1910 if (iItem == infoPtr->iSelected) {
1912 GetClientRect (infoPtr->hwnd, &rect);
1913 clRight = rect.right;
1914 clBottom = rect.bottom;
1921 * Erase the background. (Delay it but setup rectangle.)
1922 * This is necessary when drawing the selected item since it is larger
1923 * than the others, it might overlap with stuff already drawn by the
1928 if(lStyle & TCS_VERTICAL)
1930 /* These are for adjusting the drawing of a Selected tab */
1931 /* The initial values are for the normal case of non-Selected */
1932 int ZZ = 1; /* Do not strech if selected */
1933 if (iItem == infoPtr->iSelected) {
1936 /* if leftmost draw the line longer */
1937 if(selectedRect.top == 0)
1938 fillRect.top += CONTROL_BORDER_SIZEY;
1939 /* if rightmost draw the line longer */
1940 if(selectedRect.bottom == clBottom)
1941 fillRect.bottom -= CONTROL_BORDER_SIZEY;
1944 if (lStyle & TCS_BOTTOM)
1946 /* Adjust both rectangles to match native */
1949 TRACE("<right> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1951 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1952 r.left,r.top,r.right,r.bottom);
1954 /* Clear interior */
1955 SetBkColor(hdc, bkgnd);
1956 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1958 /* Draw rectangular edge around tab */
1959 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
1961 /* Now erase the top corner and draw diagonal edge */
1962 SetBkColor(hdc, corner);
1963 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1966 r1.bottom = r1.top + ROUND_CORNER_SIZE;
1967 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1969 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
1971 /* Now erase the bottom corner and draw diagonal edge */
1972 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1973 r1.bottom = r.bottom;
1975 r1.top = r1.bottom - ROUND_CORNER_SIZE;
1976 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1978 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
1980 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
1984 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
1990 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1992 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1993 r.left,r.top,r.right,r.bottom);
1995 /* Clear interior */
1996 SetBkColor(hdc, bkgnd);
1997 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1999 /* Draw rectangular edge around tab */
2000 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2002 /* Now erase the top corner and draw diagonal edge */
2003 SetBkColor(hdc, corner);
2006 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2007 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2008 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2010 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2012 /* Now erase the bottom corner and draw diagonal edge */
2014 r1.bottom = r.bottom;
2015 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2016 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2017 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2019 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2022 else /* ! TCS_VERTICAL */
2024 /* These are for adjusting the drawing of a Selected tab */
2025 /* The initial values are for the normal case of non-Selected */
2026 if (iItem == infoPtr->iSelected) {
2027 /* if leftmost draw the line longer */
2028 if(selectedRect.left == 0)
2029 fillRect.left += CONTROL_BORDER_SIZEX;
2030 /* if rightmost draw the line longer */
2031 if(selectedRect.right == clRight)
2032 fillRect.right -= CONTROL_BORDER_SIZEX;
2035 if (lStyle & TCS_BOTTOM)
2037 /* Adjust both rectangles for topmost row */
2038 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2044 TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2046 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2047 r.left,r.top,r.right,r.bottom);
2049 /* Clear interior */
2050 SetBkColor(hdc, bkgnd);
2051 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2053 /* Draw rectangular edge around tab */
2054 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2056 /* Now erase the righthand corner and draw diagonal edge */
2057 SetBkColor(hdc, corner);
2058 r1.left = r.right - ROUND_CORNER_SIZE;
2059 r1.bottom = r.bottom;
2061 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2062 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2064 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2066 /* Now erase the lefthand corner and draw diagonal edge */
2068 r1.bottom = r.bottom;
2069 r1.right = r1.left + ROUND_CORNER_SIZE;
2070 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2071 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2073 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2075 if (iItem == infoPtr->iSelected)
2079 if (selectedRect.left == 0)
2084 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2091 /* Adjust both rectangles for bottommost row */
2092 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2094 fillRect.bottom += 3;
2098 TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2100 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2101 r.left,r.top,r.right,r.bottom);
2103 /* Clear interior */
2104 SetBkColor(hdc, bkgnd);
2105 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2107 /* Draw rectangular edge around tab */
2108 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2110 /* Now erase the righthand corner and draw diagonal edge */
2111 SetBkColor(hdc, corner);
2112 r1.left = r.right - ROUND_CORNER_SIZE;
2115 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2116 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2118 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2120 /* Now erase the lefthand corner and draw diagonal edge */
2123 r1.right = r1.left + ROUND_CORNER_SIZE;
2124 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2125 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2127 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2132 TAB_DumpItemInternal(infoPtr, iItem);
2134 /* This modifies r to be the text rectangle. */
2135 TAB_DrawItemInterior(infoPtr, hdc, iItem, &r);
2139 /******************************************************************************
2142 * This method is used to draw the raised border around the tab control
2145 static void TAB_DrawBorder (TAB_INFO *infoPtr, HDC hdc)
2148 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2150 GetClientRect (infoPtr->hwnd, &rect);
2153 * Adjust for the style
2156 if (infoPtr->uNumItem)
2158 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2159 rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2160 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2161 rect.right -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2162 else if(lStyle & TCS_VERTICAL)
2163 rect.left += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2164 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2165 rect.top += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2168 TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2169 rect.left, rect.top, rect.right, rect.bottom);
2171 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2174 /******************************************************************************
2177 * This method repaints the tab control..
2179 static void TAB_Refresh (TAB_INFO *infoPtr, HDC hdc)
2184 if (!infoPtr->DoRedraw)
2187 hOldFont = SelectObject (hdc, infoPtr->hFont);
2189 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS)
2191 for (i = 0; i < infoPtr->uNumItem; i++)
2192 TAB_DrawItem (infoPtr, hdc, i);
2196 /* Draw all the non selected item first */
2197 for (i = 0; i < infoPtr->uNumItem; i++)
2199 if (i != infoPtr->iSelected)
2200 TAB_DrawItem (infoPtr, hdc, i);
2203 /* Now, draw the border, draw it before the selected item
2204 * since the selected item overwrites part of the border. */
2205 TAB_DrawBorder (infoPtr, hdc);
2207 /* Then, draw the selected item */
2208 TAB_DrawItem (infoPtr, hdc, infoPtr->iSelected);
2210 /* If we haven't set the current focus yet, set it now.
2211 * Only happens when we first paint the tab controls */
2212 if (infoPtr->uFocus == -1)
2213 TAB_SetCurFocus(infoPtr, infoPtr->iSelected);
2216 SelectObject (hdc, hOldFont);
2219 static inline DWORD TAB_GetRowCount (const TAB_INFO *infoPtr)
2221 return infoPtr->uNumRows;
2224 static inline LRESULT TAB_SetRedraw (TAB_INFO *infoPtr, BOOL doRedraw)
2226 infoPtr->DoRedraw = doRedraw;
2230 /******************************************************************************
2231 * TAB_EnsureSelectionVisible
2233 * This method will make sure that the current selection is completely
2234 * visible by scrolling until it is.
2236 static void TAB_EnsureSelectionVisible(
2239 INT iSelected = infoPtr->iSelected;
2240 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2241 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2243 /* set the items row to the bottommost row or topmost row depending on
2245 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2247 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2251 if(lStyle & TCS_VERTICAL)
2252 newselected = selected->rect.left;
2254 newselected = selected->rect.top;
2256 /* the target row is always (number of rows - 1)
2257 as row 0 is furthest from the clientRect */
2258 iTargetRow = infoPtr->uNumRows - 1;
2260 if (newselected != iTargetRow)
2263 if(lStyle & TCS_VERTICAL)
2265 for (i=0; i < infoPtr->uNumItem; i++)
2267 /* move everything in the row of the selected item to the iTargetRow */
2268 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2270 if (item->rect.left == newselected )
2271 item->rect.left = iTargetRow;
2274 if (item->rect.left > newselected)
2281 for (i=0; i < infoPtr->uNumItem; i++)
2283 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2285 if (item->rect.top == newselected )
2286 item->rect.top = iTargetRow;
2289 if (item->rect.top > newselected)
2294 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2299 * Do the trivial cases first.
2301 if ( (!infoPtr->needsScrolling) ||
2302 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2305 if (infoPtr->leftmostVisible >= iSelected)
2307 infoPtr->leftmostVisible = iSelected;
2311 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2316 /* Calculate the part of the client area that is visible */
2317 GetClientRect(infoPtr->hwnd, &r);
2320 GetClientRect(infoPtr->hwndUpDown, &r);
2323 if ((selected->rect.right -
2324 selected->rect.left) >= width )
2326 /* Special case: width of selected item is greater than visible
2329 infoPtr->leftmostVisible = iSelected;
2333 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2335 if ((selected->rect.right - TAB_GetItem(infoPtr, i)->rect.left) < width)
2338 infoPtr->leftmostVisible = i;
2342 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2343 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2345 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2346 MAKELONG(infoPtr->leftmostVisible, 0));
2349 /******************************************************************************
2350 * TAB_InvalidateTabArea
2352 * This method will invalidate the portion of the control that contains the
2353 * tabs. It is called when the state of the control changes and needs
2356 static void TAB_InvalidateTabArea(TAB_INFO* infoPtr)
2358 RECT clientRect, rInvalidate, rAdjClient;
2359 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2360 INT lastRow = infoPtr->uNumRows - 1;
2363 if (lastRow < 0) return;
2365 GetClientRect(infoPtr->hwnd, &clientRect);
2366 rInvalidate = clientRect;
2367 rAdjClient = clientRect;
2369 TAB_AdjustRect(infoPtr, 0, &rAdjClient);
2371 TAB_InternalGetItemRect(infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2372 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2374 rInvalidate.left = rAdjClient.right;
2375 if (infoPtr->uNumRows == 1)
2376 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2378 else if(lStyle & TCS_VERTICAL)
2380 rInvalidate.right = rAdjClient.left;
2381 if (infoPtr->uNumRows == 1)
2382 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2384 else if (lStyle & TCS_BOTTOM)
2386 rInvalidate.top = rAdjClient.bottom;
2387 if (infoPtr->uNumRows == 1)
2388 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2392 rInvalidate.bottom = rAdjClient.top;
2393 if (infoPtr->uNumRows == 1)
2394 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2397 /* Punch out the updown control */
2398 if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2400 GetClientRect(infoPtr->hwndUpDown, &r);
2401 if (rInvalidate.right > clientRect.right - r.left)
2402 rInvalidate.right = rInvalidate.right - (r.right - r.left);
2404 rInvalidate.right = clientRect.right - r.left;
2407 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2408 rInvalidate.left, rInvalidate.top,
2409 rInvalidate.right, rInvalidate.bottom);
2411 InvalidateRect(infoPtr->hwnd, &rInvalidate, TRUE);
2414 static inline LRESULT TAB_Paint (TAB_INFO *infoPtr, HDC hdcPaint)
2423 hdc = BeginPaint (infoPtr->hwnd, &ps);
2424 TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2426 ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2429 TAB_Refresh (infoPtr, hdc);
2432 EndPaint (infoPtr->hwnd, &ps);
2438 TAB_InsertItemT (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2445 GetClientRect (infoPtr->hwnd, &rect);
2446 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", infoPtr->hwnd,
2447 rect.top, rect.left, rect.bottom, rect.right);
2449 pti = (TCITEMW *)lParam;
2450 iItem = (INT)wParam;
2452 if (iItem < 0) return -1;
2453 if (iItem > infoPtr->uNumItem)
2454 iItem = infoPtr->uNumItem;
2456 TAB_DumpItemExternalT(pti, iItem, bUnicode);
2459 if (infoPtr->uNumItem == 0) {
2460 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2461 infoPtr->uNumItem++;
2462 infoPtr->iSelected = 0;
2465 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2467 infoPtr->uNumItem++;
2468 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2470 /* pre insert copy */
2472 memcpy (infoPtr->items, oldItems,
2473 iItem * TAB_ITEM_SIZE(infoPtr));
2476 /* post insert copy */
2477 if (iItem < infoPtr->uNumItem - 1) {
2478 memcpy (TAB_GetItem(infoPtr, iItem + 1),
2479 oldItems + iItem * TAB_ITEM_SIZE(infoPtr),
2480 (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2484 if (iItem <= infoPtr->iSelected)
2485 infoPtr->iSelected++;
2490 item = TAB_GetItem(infoPtr, iItem);
2492 item->mask = pti->mask;
2493 item->pszText = NULL;
2495 if (pti->mask & TCIF_TEXT)
2498 Str_SetPtrW (&item->pszText, pti->pszText);
2500 Str_SetPtrAtoW (&item->pszText, (LPSTR)pti->pszText);
2503 if (pti->mask & TCIF_IMAGE)
2504 item->iImage = pti->iImage;
2508 if (pti->mask & TCIF_PARAM)
2509 memcpy(item->extra, &pti->lParam, infoPtr->cbInfo);
2511 memset(item->extra, 0, infoPtr->cbInfo);
2513 TAB_SetItemBounds(infoPtr);
2514 if (infoPtr->uNumItem > 1)
2515 TAB_InvalidateTabArea(infoPtr);
2517 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2519 TRACE("[%p]: added item %d %s\n",
2520 infoPtr->hwnd, iItem, debugstr_w(item->pszText));
2526 TAB_SetItemSize (TAB_INFO *infoPtr, LPARAM lParam)
2528 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2530 BOOL bNeedPaint = FALSE;
2532 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2534 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2535 if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2537 infoPtr->tabWidth = max((INT)LOWORD(lParam), infoPtr->tabMinWidth);
2541 if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2543 if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2544 infoPtr->tabHeight = (INT)HIWORD(lParam);
2548 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2549 HIWORD(lResult), LOWORD(lResult),
2550 infoPtr->tabHeight, infoPtr->tabWidth);
2554 TAB_SetItemBounds(infoPtr);
2555 RedrawWindow(infoPtr->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2561 static inline LRESULT TAB_SetMinTabWidth (TAB_INFO *infoPtr, INT cx)
2565 TRACE("(%p,%d)\n", infoPtr, cx);
2568 oldcx = infoPtr->tabMinWidth;
2569 infoPtr->tabMinWidth = (cx==-1)?DEFAULT_TAB_WIDTH:cx;
2575 static inline LRESULT
2576 TAB_HighlightItem (TAB_INFO *infoPtr, INT iItem, BOOL fHighlight)
2580 TRACE("(%p,%d,%s)\n", infoPtr, iItem, fHighlight ? "true" : "false");
2582 if (!infoPtr || iItem < 0 || iItem >= infoPtr->uNumItem)
2585 lpState = &TAB_GetItem(infoPtr, iItem)->dwState;
2588 *lpState |= TCIS_HIGHLIGHTED;
2590 *lpState &= ~TCIS_HIGHLIGHTED;
2596 TAB_SetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2600 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2602 if (iItem < 0 || iItem >= infoPtr->uNumItem)
2605 TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2607 wineItem = TAB_GetItem(infoPtr, iItem);
2609 if (tabItem->mask & TCIF_IMAGE)
2610 wineItem->iImage = tabItem->iImage;
2612 if (tabItem->mask & TCIF_PARAM)
2613 memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2615 if (tabItem->mask & TCIF_RTLREADING)
2616 FIXME("TCIF_RTLREADING\n");
2618 if (tabItem->mask & TCIF_STATE)
2619 wineItem->dwState = tabItem->dwState;
2621 if (tabItem->mask & TCIF_TEXT)
2623 if (wineItem->pszText)
2625 Free(wineItem->pszText);
2626 wineItem->pszText = NULL;
2629 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2631 Str_SetPtrAtoW(&wineItem->pszText, (LPSTR)tabItem->pszText);
2634 /* Update and repaint tabs */
2635 TAB_SetItemBounds(infoPtr);
2636 TAB_InvalidateTabArea(infoPtr);
2641 static inline LRESULT TAB_GetItemCount (const TAB_INFO *infoPtr)
2643 return infoPtr->uNumItem;
2648 TAB_GetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2652 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2654 if (iItem < 0 || iItem >= infoPtr->uNumItem)
2657 wineItem = TAB_GetItem(infoPtr, iItem);
2659 if (tabItem->mask & TCIF_IMAGE)
2660 tabItem->iImage = wineItem->iImage;
2662 if (tabItem->mask & TCIF_PARAM)
2663 memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2665 if (tabItem->mask & TCIF_RTLREADING)
2666 FIXME("TCIF_RTLREADING\n");
2668 if (tabItem->mask & TCIF_STATE)
2669 tabItem->dwState = wineItem->dwState;
2671 if (tabItem->mask & TCIF_TEXT)
2674 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2676 Str_GetPtrWtoA (wineItem->pszText, (LPSTR)tabItem->pszText, tabItem->cchTextMax);
2679 TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2685 static LRESULT TAB_DeleteItem (TAB_INFO *infoPtr, INT iItem)
2687 BOOL bResult = FALSE;
2689 TRACE("(%p, %d)\n", infoPtr, iItem);
2691 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2693 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
2694 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2696 TAB_InvalidateTabArea(infoPtr);
2698 if ((item->mask & TCIF_TEXT) && item->pszText)
2699 Free(item->pszText);
2701 infoPtr->uNumItem--;
2703 if (!infoPtr->uNumItem)
2705 infoPtr->items = NULL;
2706 if (infoPtr->iHotTracked >= 0)
2708 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
2709 infoPtr->iHotTracked = -1;
2714 infoPtr->items = Alloc(TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2717 memcpy(infoPtr->items, oldItems, iItem * TAB_ITEM_SIZE(infoPtr));
2719 if (iItem < infoPtr->uNumItem)
2720 memcpy(TAB_GetItem(infoPtr, iItem),
2721 oldItems + (iItem + 1) * TAB_ITEM_SIZE(infoPtr),
2722 (infoPtr->uNumItem - iItem) * TAB_ITEM_SIZE(infoPtr));
2724 if (iItem <= infoPtr->iHotTracked)
2726 /* When tabs move left/up, the hot track item may change */
2727 FIXME("Recalc hot track");
2732 /* Readjust the selected index */
2733 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2734 infoPtr->iSelected--;
2736 if (iItem < infoPtr->iSelected)
2737 infoPtr->iSelected--;
2739 if (infoPtr->uNumItem == 0)
2740 infoPtr->iSelected = -1;
2742 /* Reposition and repaint tabs */
2743 TAB_SetItemBounds(infoPtr);
2751 static inline LRESULT TAB_DeleteAllItems (TAB_INFO *infoPtr)
2753 TRACE("(%p)\n", infoPtr);
2754 while (infoPtr->uNumItem)
2755 TAB_DeleteItem (infoPtr, 0);
2760 static inline LRESULT TAB_GetFont (const TAB_INFO *infoPtr)
2762 TRACE("(%p) returning %p\n", infoPtr, infoPtr->hFont);
2763 return (LRESULT)infoPtr->hFont;
2766 static inline LRESULT TAB_SetFont (TAB_INFO *infoPtr, HFONT hNewFont)
2768 TRACE("(%p,%p)\n", infoPtr, hNewFont);
2770 infoPtr->hFont = hNewFont;
2772 TAB_SetItemBounds(infoPtr);
2774 TAB_InvalidateTabArea(infoPtr);
2780 static inline LRESULT TAB_GetImageList (const TAB_INFO *infoPtr)
2783 return (LRESULT)infoPtr->himl;
2786 static inline LRESULT TAB_SetImageList (TAB_INFO *infoPtr, HIMAGELIST himlNew)
2788 HIMAGELIST himlPrev = infoPtr->himl;
2790 infoPtr->himl = himlNew;
2791 return (LRESULT)himlPrev;
2794 static inline LRESULT TAB_GetUnicodeFormat (const TAB_INFO *infoPtr)
2796 return infoPtr->bUnicode;
2799 static inline LRESULT TAB_SetUnicodeFormat (TAB_INFO *infoPtr, BOOL bUnicode)
2801 BOOL bTemp = infoPtr->bUnicode;
2803 infoPtr->bUnicode = bUnicode;
2808 static inline LRESULT TAB_Size (TAB_INFO *infoPtr)
2810 /* I'm not really sure what the following code was meant to do.
2811 This is what it is doing:
2812 When WM_SIZE is sent with SIZE_RESTORED, the control
2813 gets positioned in the top left corner.
2817 UINT uPosFlags,cx,cy;
2821 parent = GetParent (hwnd);
2822 GetClientRect(parent, &parent_rect);
2825 if (GetWindowLongW(hwnd, GWL_STYLE) & CCS_NORESIZE)
2826 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2828 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2829 cx, cy, uPosFlags | SWP_NOZORDER);
2831 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2834 /* Recompute the size/position of the tabs. */
2835 TAB_SetItemBounds (infoPtr);
2837 /* Force a repaint of the control. */
2838 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2844 static LRESULT TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2847 TEXTMETRICW fontMetrics;
2852 infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
2854 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
2856 infoPtr->hwnd = hwnd;
2857 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
2858 infoPtr->uNumItem = 0;
2859 infoPtr->uNumRows = 0;
2860 infoPtr->uHItemPadding = 6;
2861 infoPtr->uVItemPadding = 3;
2862 infoPtr->uHItemPadding_s = 6;
2863 infoPtr->uVItemPadding_s = 3;
2866 infoPtr->hcurArrow = LoadCursorW (0, (LPWSTR)IDC_ARROW);
2867 infoPtr->iSelected = -1;
2868 infoPtr->iHotTracked = -1;
2869 infoPtr->uFocus = -1;
2870 infoPtr->hwndToolTip = 0;
2871 infoPtr->DoRedraw = TRUE;
2872 infoPtr->needsScrolling = FALSE;
2873 infoPtr->hwndUpDown = 0;
2874 infoPtr->leftmostVisible = 0;
2875 infoPtr->fHeightSet = FALSE;
2876 infoPtr->bUnicode = IsWindowUnicode (hwnd);
2877 infoPtr->cbInfo = sizeof(LPARAM);
2879 TRACE("Created tab control, hwnd [%p]\n", hwnd);
2881 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2882 if you don't specify it in CreateWindow. This is necessary in
2883 order for paint to work correctly. This follows windows behaviour. */
2884 dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
2885 SetWindowLongW(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2887 if (dwStyle & TCS_TOOLTIPS) {
2888 /* Create tooltip control */
2889 infoPtr->hwndToolTip =
2890 CreateWindowExW (0, TOOLTIPS_CLASSW, NULL, 0,
2891 CW_USEDEFAULT, CW_USEDEFAULT,
2892 CW_USEDEFAULT, CW_USEDEFAULT,
2895 /* Send NM_TOOLTIPSCREATED notification */
2896 if (infoPtr->hwndToolTip) {
2897 NMTOOLTIPSCREATED nmttc;
2899 nmttc.hdr.hwndFrom = hwnd;
2900 nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
2901 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2902 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2904 SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
2905 (WPARAM)GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc);
2910 * We need to get text information so we need a DC and we need to select
2914 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
2916 /* Use the system font to determine the initial height of a tab. */
2917 GetTextMetricsW(hdc, &fontMetrics);
2920 * Make sure there is enough space for the letters + growing the
2921 * selected item + extra space for the selected item.
2923 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
2924 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
2925 infoPtr->uVItemPadding;
2927 /* Initialize the width of a tab. */
2928 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
2929 infoPtr->tabMinWidth = 0;
2931 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
2933 SelectObject (hdc, hOldFont);
2934 ReleaseDC(hwnd, hdc);
2940 TAB_Destroy (TAB_INFO *infoPtr)
2947 SetWindowLongPtrW(infoPtr->hwnd, 0, 0);
2949 if (infoPtr->items) {
2950 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
2951 if (TAB_GetItem(infoPtr, iItem)->pszText)
2952 Free (TAB_GetItem(infoPtr, iItem)->pszText);
2954 Free (infoPtr->items);
2957 if (infoPtr->hwndToolTip)
2958 DestroyWindow (infoPtr->hwndToolTip);
2960 if (infoPtr->hwndUpDown)
2961 DestroyWindow(infoPtr->hwndUpDown);
2963 if (infoPtr->iHotTracked >= 0)
2964 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
2970 static LRESULT TAB_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
2974 return WVR_ALIGNTOP;
2977 static inline LRESULT
2978 TAB_SetItemExtra (TAB_INFO *infoPtr, INT cbInfo)
2980 if (!infoPtr || cbInfo <= 0)
2983 if (infoPtr->uNumItem)
2985 /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
2989 infoPtr->cbInfo = cbInfo;
2993 static LRESULT WINAPI
2994 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2996 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2998 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
2999 if (!infoPtr && (uMsg != WM_CREATE))
3000 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
3004 case TCM_GETIMAGELIST:
3005 return TAB_GetImageList (infoPtr);
3007 case TCM_SETIMAGELIST:
3008 return TAB_SetImageList (infoPtr, (HIMAGELIST)lParam);
3010 case TCM_GETITEMCOUNT:
3011 return TAB_GetItemCount (infoPtr);
3015 return TAB_GetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_GETITEMW);
3019 return TAB_SetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_SETITEMW);
3021 case TCM_DELETEITEM:
3022 return TAB_DeleteItem (infoPtr, (INT)wParam);
3024 case TCM_DELETEALLITEMS:
3025 return TAB_DeleteAllItems (infoPtr);
3027 case TCM_GETITEMRECT:
3028 return TAB_GetItemRect (infoPtr, wParam, lParam);
3031 return TAB_GetCurSel (infoPtr);
3034 return TAB_HitTest (infoPtr, (LPTCHITTESTINFO)lParam);
3037 return TAB_SetCurSel (infoPtr, (INT)wParam);
3039 case TCM_INSERTITEMA:
3040 case TCM_INSERTITEMW:
3041 return TAB_InsertItemT (infoPtr, wParam, lParam, uMsg == TCM_INSERTITEMW);
3043 case TCM_SETITEMEXTRA:
3044 return TAB_SetItemExtra (infoPtr, (int)wParam);
3046 case TCM_ADJUSTRECT:
3047 return TAB_AdjustRect (infoPtr, (BOOL)wParam, (LPRECT)lParam);
3049 case TCM_SETITEMSIZE:
3050 return TAB_SetItemSize (infoPtr, lParam);
3052 case TCM_REMOVEIMAGE:
3053 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3056 case TCM_SETPADDING:
3057 return TAB_SetPadding (infoPtr, lParam);
3059 case TCM_GETROWCOUNT:
3060 return TAB_GetRowCount(infoPtr);
3062 case TCM_GETUNICODEFORMAT:
3063 return TAB_GetUnicodeFormat (infoPtr);
3065 case TCM_SETUNICODEFORMAT:
3066 return TAB_SetUnicodeFormat (infoPtr, (BOOL)wParam);
3068 case TCM_HIGHLIGHTITEM:
3069 return TAB_HighlightItem (infoPtr, (INT)wParam, (BOOL)LOWORD(lParam));
3071 case TCM_GETTOOLTIPS:
3072 return TAB_GetToolTips (infoPtr);
3074 case TCM_SETTOOLTIPS:
3075 return TAB_SetToolTips (infoPtr, (HWND)wParam);
3077 case TCM_GETCURFOCUS:
3078 return TAB_GetCurFocus (infoPtr);
3080 case TCM_SETCURFOCUS:
3081 return TAB_SetCurFocus (infoPtr, (INT)wParam);
3083 case TCM_SETMINTABWIDTH:
3084 return TAB_SetMinTabWidth(infoPtr, (INT)lParam);
3086 case TCM_DESELECTALL:
3087 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3090 case TCM_GETEXTENDEDSTYLE:
3091 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3094 case TCM_SETEXTENDEDSTYLE:
3095 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3099 return TAB_GetFont (infoPtr);
3102 return TAB_SetFont (infoPtr, (HFONT)wParam);
3105 return TAB_Create (hwnd, wParam, lParam);
3108 return TAB_Destroy (infoPtr);
3111 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3113 case WM_LBUTTONDOWN:
3114 return TAB_LButtonDown (infoPtr, wParam, lParam);
3117 return TAB_LButtonUp (infoPtr);
3120 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3122 case WM_RBUTTONDOWN:
3123 return TAB_RButtonDown (infoPtr);
3126 return TAB_MouseMove (infoPtr, wParam, lParam);
3129 return TAB_Paint (infoPtr, (HDC)wParam);
3132 return TAB_Size (infoPtr);
3135 return TAB_SetRedraw (infoPtr, (BOOL)wParam);
3138 return TAB_OnHScroll(infoPtr, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3140 case WM_STYLECHANGED:
3141 TAB_SetItemBounds (infoPtr);
3142 InvalidateRect(hwnd, NULL, TRUE);
3145 case WM_SYSCOLORCHANGE:
3146 COMCTL32_RefreshSysColors();
3151 TAB_FocusChanging(infoPtr);
3152 break; /* Don't disturb normal focus behavior */
3155 return TAB_KeyUp(infoPtr, wParam);
3157 return TAB_NCHitTest(infoPtr, lParam);
3160 return TAB_NCCalcSize(hwnd, wParam, lParam);
3163 if (uMsg >= WM_USER && uMsg < WM_APP)
3164 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3165 uMsg, wParam, lParam);
3168 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
3177 ZeroMemory (&wndClass, sizeof(WNDCLASSW));
3178 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3179 wndClass.lpfnWndProc = TAB_WindowProc;
3180 wndClass.cbClsExtra = 0;
3181 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3182 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3183 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
3184 wndClass.lpszClassName = WC_TABCONTROLW;
3186 RegisterClassW (&wndClass);
3191 TAB_Unregister (void)
3193 UnregisterClassW (WC_TABCONTROLW, NULL);