4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
7 * Copyright 2003 Vitaliy Margolen
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 * This code was audited for completeness against the documented features
26 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
28 * Unless otherwise noted, we believe this code to be complete, as per
29 * the specification mentioned above.
30 * If you discover missing features, or bugs, please note them below.
35 * TCS_MULTISELECT - implement for VK_SPACE selection
67 #include "wine/debug.h"
70 WINE_DEFAULT_DEBUG_CHANNEL(tab
);
77 RECT rect
; /* bounding rectangle of the item relative to the
78 * leftmost item (the leftmost item, 0, would have a
79 * "left" member of 0 in this rectangle)
81 * additionally the top member holds the row number
82 * and bottom is unused and should be 0 */
83 BYTE extra
[1]; /* Space for caller supplied info, variable size */
86 /* The size of a tab item depends on how much extra data is requested */
87 #define TAB_ITEM_SIZE(infoPtr) (FIELD_OFFSET(TAB_ITEM, extra[(infoPtr)->cbInfo]))
91 HWND hwnd
; /* Tab control window */
92 HWND hwndNotify
; /* notification window (parent) */
93 UINT uNumItem
; /* number of tab items */
94 UINT uNumRows
; /* number of tab rows */
95 INT tabHeight
; /* height of the tab row */
96 INT tabWidth
; /* width of tabs */
97 INT tabMinWidth
; /* minimum width of items */
98 USHORT uHItemPadding
; /* amount of horizontal padding, in pixels */
99 USHORT uVItemPadding
; /* amount of vertical padding, in pixels */
100 USHORT uHItemPadding_s
; /* Set amount of horizontal padding, in pixels */
101 USHORT uVItemPadding_s
; /* Set amount of vertical padding, in pixels */
102 HFONT hFont
; /* handle to the current font */
103 HCURSOR hcurArrow
; /* handle to the current cursor */
104 HIMAGELIST himl
; /* handle to an image list (may be 0) */
105 HWND hwndToolTip
; /* handle to tab's tooltip */
106 INT leftmostVisible
; /* Used for scrolling, this member contains
107 * the index of the first visible item */
108 INT iSelected
; /* the currently selected item */
109 INT iHotTracked
; /* the highlighted item under the mouse */
110 INT uFocus
; /* item which has the focus */
111 TAB_ITEM
* items
; /* pointer to an array of TAB_ITEM's */
112 BOOL DoRedraw
; /* flag for redrawing when tab contents is changed*/
113 BOOL needsScrolling
; /* TRUE if the size of the tabs is greater than
114 * the size of the control */
115 BOOL fHeightSet
; /* was the height of the tabs explicitly set? */
116 BOOL bUnicode
; /* Unicode control? */
117 HWND hwndUpDown
; /* Updown control used for scrolling */
118 INT cbInfo
; /* Number of bytes of caller supplied info per tab */
120 DWORD exStyle
; /* Extended style used, currently:
121 TCS_EX_FLATSEPARATORS, TCS_EX_REGISTERDROP */
122 DWORD dwStyle
; /* the cached window GWL_STYLE */
125 /******************************************************************************
126 * Positioning constants
128 #define SELECTED_TAB_OFFSET 2
129 #define ROUND_CORNER_SIZE 2
130 #define DISPLAY_AREA_PADDINGX 2
131 #define DISPLAY_AREA_PADDINGY 2
132 #define CONTROL_BORDER_SIZEX 2
133 #define CONTROL_BORDER_SIZEY 2
134 #define BUTTON_SPACINGX 3
135 #define BUTTON_SPACINGY 3
136 #define FLAT_BTN_SPACINGX 8
137 #define DEFAULT_MIN_TAB_WIDTH 54
138 #define DEFAULT_PADDING_X 6
139 #define EXTRA_ICON_PADDING 3
141 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongPtrW(hwnd,0))
142 /* Since items are variable sized, cannot directly access them */
143 #define TAB_GetItem(info,i) \
144 ((TAB_ITEM*)((LPBYTE)info->items + (i) * TAB_ITEM_SIZE(info)))
146 #define GET_DEFAULT_MIN_TAB_WIDTH(infoPtr) (DEFAULT_MIN_TAB_WIDTH - (DEFAULT_PADDING_X - (infoPtr)->uHItemPadding) * 2)
148 /******************************************************************************
149 * Hot-tracking timer constants
151 #define TAB_HOTTRACK_TIMER 1
152 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
154 static const WCHAR themeClass
[] = { 'T','a','b',0 };
156 /******************************************************************************
159 static void TAB_InvalidateTabArea(const TAB_INFO
*);
160 static void TAB_EnsureSelectionVisible(TAB_INFO
*);
161 static void TAB_DrawItemInterior(const TAB_INFO
*, HDC
, INT
, RECT
*);
162 static LRESULT
TAB_DeselectAll(TAB_INFO
*, BOOL
);
163 static BOOL
TAB_InternalGetItemRect(const TAB_INFO
*, INT
, RECT
*, RECT
*);
166 TAB_SendSimpleNotify (const TAB_INFO
*infoPtr
, UINT code
)
170 nmhdr
.hwndFrom
= infoPtr
->hwnd
;
171 nmhdr
.idFrom
= GetWindowLongPtrW(infoPtr
->hwnd
, GWLP_ID
);
174 return (BOOL
) SendMessageW (infoPtr
->hwndNotify
, WM_NOTIFY
,
175 nmhdr
.idFrom
, (LPARAM
) &nmhdr
);
179 TAB_RelayEvent (HWND hwndTip
, HWND hwndMsg
, UINT uMsg
,
180 WPARAM wParam
, LPARAM lParam
)
188 msg
.time
= GetMessageTime ();
189 msg
.pt
.x
= (short)LOWORD(GetMessagePos ());
190 msg
.pt
.y
= (short)HIWORD(GetMessagePos ());
192 SendMessageW (hwndTip
, TTM_RELAYEVENT
, 0, (LPARAM
)&msg
);
196 TAB_DumpItemExternalT(const TCITEMW
*pti
, UINT iItem
, BOOL isW
)
199 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
200 iItem
, pti
->mask
, pti
->dwState
, pti
->dwStateMask
, pti
->cchTextMax
);
201 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
202 iItem
, pti
->iImage
, pti
->lParam
, isW
? debugstr_w(pti
->pszText
) : debugstr_a((LPSTR
)pti
->pszText
));
207 TAB_DumpItemInternal(const TAB_INFO
*infoPtr
, UINT iItem
)
212 ti
= TAB_GetItem(infoPtr
, iItem
);
213 TRACE("tab %d, dwState=0x%08x, pszText=%s, iImage=%d\n",
214 iItem
, ti
->dwState
, debugstr_w(ti
->pszText
), ti
->iImage
);
215 TRACE("tab %d, rect.left=%d, rect.top(row)=%d\n",
216 iItem
, ti
->rect
.left
, ti
->rect
.top
);
221 * the index of the selected tab, or -1 if no tab is selected. */
222 static inline LRESULT
TAB_GetCurSel (const TAB_INFO
*infoPtr
)
224 TRACE("(%p)\n", infoPtr
);
225 return infoPtr
->iSelected
;
229 * the index of the tab item that has the focus. */
230 static inline LRESULT
231 TAB_GetCurFocus (const TAB_INFO
*infoPtr
)
233 TRACE("(%p)\n", infoPtr
);
234 return infoPtr
->uFocus
;
237 static inline LRESULT
TAB_GetToolTips (const TAB_INFO
*infoPtr
)
239 TRACE("(%p)\n", infoPtr
);
240 return (LRESULT
)infoPtr
->hwndToolTip
;
243 static inline LRESULT
TAB_SetCurSel (TAB_INFO
*infoPtr
, INT iItem
)
245 INT prevItem
= infoPtr
->iSelected
;
247 TRACE("(%p %d)\n", infoPtr
, iItem
);
250 infoPtr
->iSelected
= -1;
251 else if (iItem
>= infoPtr
->uNumItem
)
254 if (prevItem
!= iItem
) {
256 TAB_GetItem(infoPtr
, prevItem
)->dwState
&= ~TCIS_BUTTONPRESSED
;
257 TAB_GetItem(infoPtr
, iItem
)->dwState
|= TCIS_BUTTONPRESSED
;
259 infoPtr
->iSelected
= iItem
;
260 infoPtr
->uFocus
= iItem
;
261 TAB_EnsureSelectionVisible(infoPtr
);
262 TAB_InvalidateTabArea(infoPtr
);
268 static LRESULT
TAB_SetCurFocus (TAB_INFO
*infoPtr
, INT iItem
)
270 TRACE("(%p %d)\n", infoPtr
, iItem
);
273 infoPtr
->uFocus
= -1;
274 if (infoPtr
->iSelected
!= -1) {
275 infoPtr
->iSelected
= -1;
276 TAB_SendSimpleNotify(infoPtr
, TCN_SELCHANGE
);
277 TAB_InvalidateTabArea(infoPtr
);
280 else if (iItem
< infoPtr
->uNumItem
) {
281 if (infoPtr
->dwStyle
& TCS_BUTTONS
) {
282 /* set focus to new item, leave selection as is */
283 if (infoPtr
->uFocus
!= iItem
) {
284 INT prev_focus
= infoPtr
->uFocus
;
287 infoPtr
->uFocus
= iItem
;
289 if (prev_focus
!= infoPtr
->iSelected
) {
290 if (TAB_InternalGetItemRect(infoPtr
, prev_focus
, &r
, NULL
))
291 InvalidateRect(infoPtr
->hwnd
, &r
, FALSE
);
294 if (TAB_InternalGetItemRect(infoPtr
, iItem
, &r
, NULL
))
295 InvalidateRect(infoPtr
->hwnd
, &r
, FALSE
);
297 TAB_SendSimpleNotify(infoPtr
, TCN_FOCUSCHANGE
);
300 INT oldFocus
= infoPtr
->uFocus
;
301 if (infoPtr
->iSelected
!= iItem
|| oldFocus
== -1 ) {
302 infoPtr
->uFocus
= iItem
;
303 if (oldFocus
!= -1) {
304 if (!TAB_SendSimpleNotify(infoPtr
, TCN_SELCHANGING
)) {
305 infoPtr
->iSelected
= iItem
;
306 TAB_SendSimpleNotify(infoPtr
, TCN_SELCHANGE
);
309 infoPtr
->iSelected
= iItem
;
310 TAB_EnsureSelectionVisible(infoPtr
);
311 TAB_InvalidateTabArea(infoPtr
);
319 static inline LRESULT
320 TAB_SetToolTips (TAB_INFO
*infoPtr
, HWND hwndToolTip
)
322 TRACE("%p %p\n", infoPtr
, hwndToolTip
);
323 infoPtr
->hwndToolTip
= hwndToolTip
;
327 static inline LRESULT
328 TAB_SetPadding (TAB_INFO
*infoPtr
, LPARAM lParam
)
330 TRACE("(%p %d %d)\n", infoPtr
, LOWORD(lParam
), HIWORD(lParam
));
331 infoPtr
->uHItemPadding_s
= LOWORD(lParam
);
332 infoPtr
->uVItemPadding_s
= HIWORD(lParam
);
337 /******************************************************************************
338 * TAB_InternalGetItemRect
340 * This method will calculate the rectangle representing a given tab item in
341 * client coordinates. This method takes scrolling into account.
343 * This method returns TRUE if the item is visible in the window and FALSE
344 * if it is completely outside the client area.
346 static BOOL
TAB_InternalGetItemRect(
347 const TAB_INFO
* infoPtr
,
352 RECT tmpItemRect
,clientRect
;
354 /* Perform a sanity check and a trivial visibility check. */
355 if ( (infoPtr
->uNumItem
<= 0) ||
356 (itemIndex
>= infoPtr
->uNumItem
) ||
357 (!(((infoPtr
->dwStyle
& TCS_MULTILINE
) || (infoPtr
->dwStyle
& TCS_VERTICAL
))) &&
358 (itemIndex
< infoPtr
->leftmostVisible
)))
360 TRACE("Not Visible\n");
361 /* need to initialize these to empty rects */
364 memset(itemRect
,0,sizeof(RECT
));
365 itemRect
->bottom
= infoPtr
->tabHeight
;
368 memset(selectedRect
,0,sizeof(RECT
));
373 * Avoid special cases in this procedure by assigning the "out"
374 * parameters if the caller didn't supply them
376 if (itemRect
== NULL
)
377 itemRect
= &tmpItemRect
;
379 /* Retrieve the unmodified item rect. */
380 *itemRect
= TAB_GetItem(infoPtr
,itemIndex
)->rect
;
382 /* calculate the times bottom and top based on the row */
383 GetClientRect(infoPtr
->hwnd
, &clientRect
);
385 if ((infoPtr
->dwStyle
& TCS_BOTTOM
) && (infoPtr
->dwStyle
& TCS_VERTICAL
))
387 itemRect
->right
= clientRect
.right
- SELECTED_TAB_OFFSET
- itemRect
->left
* infoPtr
->tabHeight
-
388 ((infoPtr
->dwStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGX
: 0);
389 itemRect
->left
= itemRect
->right
- infoPtr
->tabHeight
;
391 else if (infoPtr
->dwStyle
& TCS_VERTICAL
)
393 itemRect
->left
= clientRect
.left
+ SELECTED_TAB_OFFSET
+ itemRect
->left
* infoPtr
->tabHeight
+
394 ((infoPtr
->dwStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGX
: 0);
395 itemRect
->right
= itemRect
->left
+ infoPtr
->tabHeight
;
397 else if (infoPtr
->dwStyle
& TCS_BOTTOM
)
399 itemRect
->bottom
= clientRect
.bottom
- itemRect
->top
* infoPtr
->tabHeight
-
400 ((infoPtr
->dwStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: SELECTED_TAB_OFFSET
);
401 itemRect
->top
= itemRect
->bottom
- infoPtr
->tabHeight
;
403 else /* not TCS_BOTTOM and not TCS_VERTICAL */
405 itemRect
->top
= clientRect
.top
+ itemRect
->top
* infoPtr
->tabHeight
+
406 ((infoPtr
->dwStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: SELECTED_TAB_OFFSET
);
407 itemRect
->bottom
= itemRect
->top
+ infoPtr
->tabHeight
;
411 * "scroll" it to make sure the item at the very left of the
412 * tab control is the leftmost visible tab.
414 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
418 -TAB_GetItem(infoPtr
, infoPtr
->leftmostVisible
)->rect
.top
);
421 * Move the rectangle so the first item is slightly offset from
422 * the bottom of the tab control.
426 SELECTED_TAB_OFFSET
);
431 -TAB_GetItem(infoPtr
, infoPtr
->leftmostVisible
)->rect
.left
,
435 * Move the rectangle so the first item is slightly offset from
436 * the left of the tab control.
442 TRACE("item %d tab h=%d, rect=(%s)\n",
443 itemIndex
, infoPtr
->tabHeight
, wine_dbgstr_rect(itemRect
));
445 /* Now, calculate the position of the item as if it were selected. */
446 if (selectedRect
!=NULL
)
448 CopyRect(selectedRect
, itemRect
);
450 /* The rectangle of a selected item is a bit wider. */
451 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
452 InflateRect(selectedRect
, 0, SELECTED_TAB_OFFSET
);
454 InflateRect(selectedRect
, SELECTED_TAB_OFFSET
, 0);
456 /* If it also a bit higher. */
457 if ((infoPtr
->dwStyle
& TCS_BOTTOM
) && (infoPtr
->dwStyle
& TCS_VERTICAL
))
459 selectedRect
->left
-= 2; /* the border is thicker on the right */
460 selectedRect
->right
+= SELECTED_TAB_OFFSET
;
462 else if (infoPtr
->dwStyle
& TCS_VERTICAL
)
464 selectedRect
->left
-= SELECTED_TAB_OFFSET
;
465 selectedRect
->right
+= 1;
467 else if (infoPtr
->dwStyle
& TCS_BOTTOM
)
469 selectedRect
->bottom
+= SELECTED_TAB_OFFSET
;
471 else /* not TCS_BOTTOM and not TCS_VERTICAL */
473 selectedRect
->top
-= SELECTED_TAB_OFFSET
;
474 selectedRect
->bottom
-= 1;
478 /* Check for visibility */
479 if (infoPtr
->dwStyle
& TCS_VERTICAL
)
480 return (itemRect
->top
< clientRect
.bottom
) && (itemRect
->bottom
> clientRect
.top
);
482 return (itemRect
->left
< clientRect
.right
) && (itemRect
->right
> clientRect
.left
);
486 TAB_GetItemRect(const TAB_INFO
*infoPtr
, INT item
, RECT
*rect
)
488 TRACE("(%p, %d, %p)\n", infoPtr
, item
, rect
);
489 return TAB_InternalGetItemRect(infoPtr
, item
, rect
, NULL
);
492 /******************************************************************************
495 * This method is called to handle keyboard input
497 static LRESULT
TAB_KeyDown(TAB_INFO
* infoPtr
, WPARAM keyCode
, LPARAM lParam
)
502 /* TCN_KEYDOWN notification sent always */
503 nm
.hdr
.hwndFrom
= infoPtr
->hwnd
;
504 nm
.hdr
.idFrom
= GetWindowLongPtrW(infoPtr
->hwnd
, GWLP_ID
);
505 nm
.hdr
.code
= TCN_KEYDOWN
;
508 SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, nm
.hdr
.idFrom
, (LPARAM
)&nm
);
513 newItem
= infoPtr
->uFocus
- 1;
516 newItem
= infoPtr
->uFocus
+ 1;
520 /* If we changed to a valid item, change focused item */
521 if (newItem
>= 0 && newItem
< infoPtr
->uNumItem
&& infoPtr
->uFocus
!= newItem
)
522 TAB_SetCurFocus(infoPtr
, newItem
);
528 * WM_KILLFOCUS handler
530 static void TAB_KillFocus(TAB_INFO
*infoPtr
)
532 /* clear current focused item back to selected for TCS_BUTTONS */
533 if ((infoPtr
->dwStyle
& TCS_BUTTONS
) && (infoPtr
->uFocus
!= infoPtr
->iSelected
))
537 if (TAB_InternalGetItemRect(infoPtr
, infoPtr
->uFocus
, &r
, NULL
))
538 InvalidateRect(infoPtr
->hwnd
, &r
, FALSE
);
540 infoPtr
->uFocus
= infoPtr
->iSelected
;
544 /******************************************************************************
547 * This method is called whenever the focus goes in or out of this control
548 * it is used to update the visual state of the control.
550 static void TAB_FocusChanging(const TAB_INFO
*infoPtr
)
556 * Get the rectangle for the item.
558 isVisible
= TAB_InternalGetItemRect(infoPtr
,
564 * If the rectangle is not completely invisible, invalidate that
565 * portion of the window.
569 TRACE("invalidate (%s)\n", wine_dbgstr_rect(&selectedRect
));
570 InvalidateRect(infoPtr
->hwnd
, &selectedRect
, TRUE
);
574 static INT
TAB_InternalHitTest (const TAB_INFO
*infoPtr
, POINT pt
, UINT
*flags
)
579 for (iCount
= 0; iCount
< infoPtr
->uNumItem
; iCount
++)
581 TAB_InternalGetItemRect(infoPtr
, iCount
, &rect
, NULL
);
583 if (PtInRect(&rect
, pt
))
585 *flags
= TCHT_ONITEM
;
590 *flags
= TCHT_NOWHERE
;
594 static inline LRESULT
595 TAB_HitTest (const TAB_INFO
*infoPtr
, LPTCHITTESTINFO lptest
)
597 TRACE("(%p, %p)\n", infoPtr
, lptest
);
598 return TAB_InternalHitTest (infoPtr
, lptest
->pt
, &lptest
->flags
);
601 /******************************************************************************
604 * Napster v2b5 has a tab control for its main navigation which has a client
605 * area that covers the whole area of the dialog pages.
606 * That's why it receives all msgs for that area and the underlying dialog ctrls
608 * So I decided that we should handle WM_NCHITTEST here and return
609 * HTTRANSPARENT if we don't hit the tab control buttons.
610 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
611 * doesn't do it that way. Maybe depends on tab control styles ?
613 static inline LRESULT
614 TAB_NCHitTest (const TAB_INFO
*infoPtr
, LPARAM lParam
)
619 pt
.x
= (short)LOWORD(lParam
);
620 pt
.y
= (short)HIWORD(lParam
);
621 ScreenToClient(infoPtr
->hwnd
, &pt
);
623 if (TAB_InternalHitTest(infoPtr
, pt
, &dummyflag
) == -1)
624 return HTTRANSPARENT
;
630 TAB_LButtonDown (TAB_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
636 if (infoPtr
->hwndToolTip
)
637 TAB_RelayEvent (infoPtr
->hwndToolTip
, infoPtr
->hwnd
,
638 WM_LBUTTONDOWN
, wParam
, lParam
);
640 if (!(infoPtr
->dwStyle
& TCS_FOCUSNEVER
)) {
641 SetFocus (infoPtr
->hwnd
);
644 if (infoPtr
->hwndToolTip
)
645 TAB_RelayEvent (infoPtr
->hwndToolTip
, infoPtr
->hwnd
,
646 WM_LBUTTONDOWN
, wParam
, lParam
);
648 pt
.x
= (short)LOWORD(lParam
);
649 pt
.y
= (short)HIWORD(lParam
);
651 newItem
= TAB_InternalHitTest (infoPtr
, pt
, &dummy
);
653 TRACE("On Tab, item %d\n", newItem
);
655 if ((newItem
!= -1) && (infoPtr
->iSelected
!= newItem
))
657 if ((infoPtr
->dwStyle
& TCS_BUTTONS
) && (infoPtr
->dwStyle
& TCS_MULTISELECT
) &&
658 (wParam
& MK_CONTROL
))
662 /* toggle multiselection */
663 TAB_GetItem(infoPtr
, newItem
)->dwState
^= TCIS_BUTTONPRESSED
;
664 if (TAB_InternalGetItemRect (infoPtr
, newItem
, &r
, NULL
))
665 InvalidateRect (infoPtr
->hwnd
, &r
, TRUE
);
670 BOOL pressed
= FALSE
;
672 /* any button pressed ? */
673 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
674 if ((TAB_GetItem (infoPtr
, i
)->dwState
& TCIS_BUTTONPRESSED
) &&
675 (infoPtr
->iSelected
!= i
))
681 TAB_SendSimpleNotify(infoPtr
, TCN_SELCHANGING
);
684 TAB_DeselectAll (infoPtr
, FALSE
);
686 TAB_SetCurSel(infoPtr
, newItem
);
688 TAB_SendSimpleNotify(infoPtr
, TCN_SELCHANGE
);
695 static inline LRESULT
696 TAB_LButtonUp (const TAB_INFO
*infoPtr
)
698 TAB_SendSimpleNotify(infoPtr
, NM_CLICK
);
703 static inline LRESULT
704 TAB_RButtonDown (const TAB_INFO
*infoPtr
)
706 TAB_SendSimpleNotify(infoPtr
, NM_RCLICK
);
710 /******************************************************************************
711 * TAB_DrawLoneItemInterior
713 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
714 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
715 * up the device context and font. This routine does the same setup but
716 * only calls TAB_DrawItemInterior for the single specified item.
719 TAB_DrawLoneItemInterior(const TAB_INFO
* infoPtr
, int iItem
)
721 HDC hdc
= GetDC(infoPtr
->hwnd
);
724 /* Clip UpDown control to not draw over it */
725 if (infoPtr
->needsScrolling
)
727 GetWindowRect(infoPtr
->hwnd
, &rC
);
728 GetWindowRect(infoPtr
->hwndUpDown
, &r
);
729 ExcludeClipRect(hdc
, r
.left
- rC
.left
, r
.top
- rC
.top
, r
.right
- rC
.left
, r
.bottom
- rC
.top
);
731 TAB_DrawItemInterior(infoPtr
, hdc
, iItem
, NULL
);
732 ReleaseDC(infoPtr
->hwnd
, hdc
);
735 /* update a tab after hottracking - invalidate it or just redraw the interior,
736 * based on whether theming is used or not */
737 static inline void hottrack_refresh(const TAB_INFO
*infoPtr
, int tabIndex
)
739 if (tabIndex
== -1) return;
741 if (GetWindowTheme (infoPtr
->hwnd
))
744 TAB_InternalGetItemRect(infoPtr
, tabIndex
, &rect
, NULL
);
745 InvalidateRect (infoPtr
->hwnd
, &rect
, FALSE
);
748 TAB_DrawLoneItemInterior(infoPtr
, tabIndex
);
751 /******************************************************************************
752 * TAB_HotTrackTimerProc
754 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
755 * timer is setup so we can check if the mouse is moved out of our window.
756 * (We don't get an event when the mouse leaves, the mouse-move events just
757 * stop being delivered to our window and just start being delivered to
758 * another window.) This function is called when the timer triggers so
759 * we can check if the mouse has left our window. If so, we un-highlight
760 * the hot-tracked tab.
763 TAB_HotTrackTimerProc
765 HWND hwnd
, /* handle of window for timer messages */
766 UINT uMsg
, /* WM_TIMER message */
767 UINT_PTR idEvent
, /* timer identifier */
768 DWORD dwTime
/* current system time */
771 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
773 if (infoPtr
!= NULL
&& infoPtr
->iHotTracked
>= 0)
778 ** If we can't get the cursor position, or if the cursor is outside our
779 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
780 ** "outside" even if it is within our bounding rect if another window
781 ** overlaps. Note also that the case where the cursor stayed within our
782 ** window but has moved off the hot-tracked tab will be handled by the
783 ** WM_MOUSEMOVE event.
785 if (!GetCursorPos(&pt
) || WindowFromPoint(pt
) != hwnd
)
787 /* Redraw iHotTracked to look normal */
788 INT iRedraw
= infoPtr
->iHotTracked
;
789 infoPtr
->iHotTracked
= -1;
790 hottrack_refresh (infoPtr
, iRedraw
);
792 /* Kill this timer */
793 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
798 /******************************************************************************
801 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
802 * should be highlighted. This function determines which tab in a tab control,
803 * if any, is under the mouse and records that information. The caller may
804 * supply output parameters to receive the item number of the tab item which
805 * was highlighted but isn't any longer and of the tab item which is now
806 * highlighted but wasn't previously. The caller can use this information to
807 * selectively redraw those tab items.
809 * If the caller has a mouse position, it can supply it through the pos
810 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
811 * supplies NULL and this function determines the current mouse position
819 int* out_redrawLeave
,
826 if (out_redrawLeave
!= NULL
)
827 *out_redrawLeave
= -1;
828 if (out_redrawEnter
!= NULL
)
829 *out_redrawEnter
= -1;
831 if ((infoPtr
->dwStyle
& TCS_HOTTRACK
) || GetWindowTheme(infoPtr
->hwnd
))
839 ScreenToClient(infoPtr
->hwnd
, &pt
);
843 pt
.x
= (short)LOWORD(*pos
);
844 pt
.y
= (short)HIWORD(*pos
);
847 item
= TAB_InternalHitTest(infoPtr
, pt
, &flags
);
850 if (item
!= infoPtr
->iHotTracked
)
852 if (infoPtr
->iHotTracked
>= 0)
854 /* Mark currently hot-tracked to be redrawn to look normal */
855 if (out_redrawLeave
!= NULL
)
856 *out_redrawLeave
= infoPtr
->iHotTracked
;
860 /* Kill timer which forces recheck of mouse pos */
861 KillTimer(infoPtr
->hwnd
, TAB_HOTTRACK_TIMER
);
866 /* Start timer so we recheck mouse pos */
867 UINT timerID
= SetTimer
871 TAB_HOTTRACK_TIMER_INTERVAL
,
872 TAB_HotTrackTimerProc
876 return; /* Hot tracking not available */
879 infoPtr
->iHotTracked
= item
;
883 /* Mark new hot-tracked to be redrawn to look highlighted */
884 if (out_redrawEnter
!= NULL
)
885 *out_redrawEnter
= item
;
890 /******************************************************************************
893 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
896 TAB_MouseMove (TAB_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
901 if (infoPtr
->hwndToolTip
)
902 TAB_RelayEvent (infoPtr
->hwndToolTip
, infoPtr
->hwnd
,
903 WM_LBUTTONDOWN
, wParam
, lParam
);
905 /* Determine which tab to highlight. Redraw tabs which change highlight
907 TAB_RecalcHotTrack(infoPtr
, &lParam
, &redrawLeave
, &redrawEnter
);
909 hottrack_refresh (infoPtr
, redrawLeave
);
910 hottrack_refresh (infoPtr
, redrawEnter
);
915 /******************************************************************************
918 * Calculates the tab control's display area given the window rectangle or
919 * the window rectangle given the requested display rectangle.
921 static LRESULT
TAB_AdjustRect(const TAB_INFO
*infoPtr
, WPARAM fLarger
, LPRECT prc
)
923 LONG
*iRightBottom
, *iLeftTop
;
925 TRACE ("hwnd=%p fLarger=%ld (%s)\n", infoPtr
->hwnd
, fLarger
,
926 wine_dbgstr_rect(prc
));
930 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
932 iRightBottom
= &(prc
->right
);
933 iLeftTop
= &(prc
->left
);
937 iRightBottom
= &(prc
->bottom
);
938 iLeftTop
= &(prc
->top
);
941 if (fLarger
) /* Go from display rectangle */
943 /* Add the height of the tabs. */
944 if (infoPtr
->dwStyle
& TCS_BOTTOM
)
945 *iRightBottom
+= infoPtr
->tabHeight
* infoPtr
->uNumRows
;
947 *iLeftTop
-= infoPtr
->tabHeight
* infoPtr
->uNumRows
+
948 ((infoPtr
->dwStyle
& TCS_BUTTONS
)? 3 * (infoPtr
->uNumRows
- 1) : 0);
950 /* Inflate the rectangle for the padding */
951 InflateRect(prc
, DISPLAY_AREA_PADDINGX
, DISPLAY_AREA_PADDINGY
);
953 /* Inflate for the border */
954 InflateRect(prc
, CONTROL_BORDER_SIZEX
, CONTROL_BORDER_SIZEY
);
956 else /* Go from window rectangle. */
958 /* Deflate the rectangle for the border */
959 InflateRect(prc
, -CONTROL_BORDER_SIZEX
, -CONTROL_BORDER_SIZEY
);
961 /* Deflate the rectangle for the padding */
962 InflateRect(prc
, -DISPLAY_AREA_PADDINGX
, -DISPLAY_AREA_PADDINGY
);
964 /* Remove the height of the tabs. */
965 if (infoPtr
->dwStyle
& TCS_BOTTOM
)
966 *iRightBottom
-= infoPtr
->tabHeight
* infoPtr
->uNumRows
;
968 *iLeftTop
+= (infoPtr
->tabHeight
) * infoPtr
->uNumRows
+
969 ((infoPtr
->dwStyle
& TCS_BUTTONS
)? 3 * (infoPtr
->uNumRows
- 1) : 0);
975 /******************************************************************************
978 * This method will handle the notification from the scroll control and
979 * perform the scrolling operation on the tab control.
981 static LRESULT
TAB_OnHScroll(TAB_INFO
*infoPtr
, int nScrollCode
, int nPos
)
983 if(nScrollCode
== SB_THUMBPOSITION
&& nPos
!= infoPtr
->leftmostVisible
)
985 if(nPos
< infoPtr
->leftmostVisible
)
986 infoPtr
->leftmostVisible
--;
988 infoPtr
->leftmostVisible
++;
990 TAB_RecalcHotTrack(infoPtr
, NULL
, NULL
, NULL
);
991 TAB_InvalidateTabArea(infoPtr
);
992 SendMessageW(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
993 MAKELONG(infoPtr
->leftmostVisible
, 0));
999 /******************************************************************************
1000 * TAB_SetupScrolling
1002 * This method will check the current scrolling state and make sure the
1003 * scrolling control is displayed (or not).
1005 static void TAB_SetupScrolling(
1007 const RECT
* clientRect
)
1009 static const WCHAR emptyW
[] = { 0 };
1012 if (infoPtr
->needsScrolling
)
1015 INT vsize
, tabwidth
;
1018 * Calculate the position of the scroll control.
1020 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
1022 controlPos
.right
= clientRect
->right
;
1023 controlPos
.left
= controlPos
.right
- 2 * GetSystemMetrics(SM_CXHSCROLL
);
1025 if (infoPtr
->dwStyle
& TCS_BOTTOM
)
1027 controlPos
.top
= clientRect
->bottom
- infoPtr
->tabHeight
;
1028 controlPos
.bottom
= controlPos
.top
+ GetSystemMetrics(SM_CYHSCROLL
);
1032 controlPos
.bottom
= clientRect
->top
+ infoPtr
->tabHeight
;
1033 controlPos
.top
= controlPos
.bottom
- GetSystemMetrics(SM_CYHSCROLL
);
1038 controlPos
.right
= clientRect
->right
;
1039 controlPos
.left
= controlPos
.right
- 2 * GetSystemMetrics(SM_CXHSCROLL
);
1041 if (infoPtr
->dwStyle
& TCS_BOTTOM
)
1043 controlPos
.top
= clientRect
->bottom
- infoPtr
->tabHeight
;
1044 controlPos
.bottom
= controlPos
.top
+ GetSystemMetrics(SM_CYHSCROLL
);
1048 controlPos
.bottom
= clientRect
->top
+ infoPtr
->tabHeight
;
1049 controlPos
.top
= controlPos
.bottom
- GetSystemMetrics(SM_CYHSCROLL
);
1054 * If we don't have a scroll control yet, we want to create one.
1055 * If we have one, we want to make sure it's positioned properly.
1057 if (infoPtr
->hwndUpDown
==0)
1059 infoPtr
->hwndUpDown
= CreateWindowW(UPDOWN_CLASSW
, emptyW
,
1060 WS_VISIBLE
| WS_CHILD
| UDS_HORZ
,
1061 controlPos
.left
, controlPos
.top
,
1062 controlPos
.right
- controlPos
.left
,
1063 controlPos
.bottom
- controlPos
.top
,
1064 infoPtr
->hwnd
, NULL
, NULL
, NULL
);
1068 SetWindowPos(infoPtr
->hwndUpDown
,
1070 controlPos
.left
, controlPos
.top
,
1071 controlPos
.right
- controlPos
.left
,
1072 controlPos
.bottom
- controlPos
.top
,
1073 SWP_SHOWWINDOW
| SWP_NOZORDER
);
1076 /* Now calculate upper limit of the updown control range.
1077 * We do this by calculating how many tabs will be offscreen when the
1078 * last tab is visible.
1080 if(infoPtr
->uNumItem
)
1082 vsize
= clientRect
->right
- (controlPos
.right
- controlPos
.left
+ 1);
1083 maxRange
= infoPtr
->uNumItem
;
1084 tabwidth
= TAB_GetItem(infoPtr
, infoPtr
->uNumItem
- 1)->rect
.right
;
1086 for(; maxRange
> 0; maxRange
--)
1088 if(tabwidth
- TAB_GetItem(infoPtr
,maxRange
- 1)->rect
.left
> vsize
)
1092 if(maxRange
== infoPtr
->uNumItem
)
1098 /* If we once had a scroll control... hide it */
1099 if (infoPtr
->hwndUpDown
)
1100 ShowWindow(infoPtr
->hwndUpDown
, SW_HIDE
);
1102 if (infoPtr
->hwndUpDown
)
1103 SendMessageW(infoPtr
->hwndUpDown
, UDM_SETRANGE32
, 0, maxRange
);
1106 /******************************************************************************
1109 * This method will calculate the position rectangles of all the items in the
1110 * control. The rectangle calculated starts at 0 for the first item in the
1111 * list and ignores scrolling and selection.
1112 * It also uses the current font to determine the height of the tab row and
1113 * it checks if all the tabs fit in the client area of the window. If they
1114 * don't, a scrolling control is added.
1116 static void TAB_SetItemBounds (TAB_INFO
*infoPtr
)
1118 TEXTMETRICW fontMetrics
;
1121 INT curItemRowCount
;
1122 HFONT hFont
, hOldFont
;
1131 * We need to get text information so we need a DC and we need to select
1134 hdc
= GetDC(infoPtr
->hwnd
);
1136 hFont
= infoPtr
->hFont
? infoPtr
->hFont
: GetStockObject (SYSTEM_FONT
);
1137 hOldFont
= SelectObject (hdc
, hFont
);
1140 * We will base the rectangle calculations on the client rectangle
1143 GetClientRect(infoPtr
->hwnd
, &clientRect
);
1145 /* if TCS_VERTICAL then swap the height and width so this code places the
1146 tabs along the top of the rectangle and we can just rotate them after
1147 rather than duplicate all of the below code */
1148 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
1150 iTemp
= clientRect
.bottom
;
1151 clientRect
.bottom
= clientRect
.right
;
1152 clientRect
.right
= iTemp
;
1155 /* Now use hPadding and vPadding */
1156 infoPtr
->uHItemPadding
= infoPtr
->uHItemPadding_s
;
1157 infoPtr
->uVItemPadding
= infoPtr
->uVItemPadding_s
;
1159 /* The leftmost item will be "0" aligned */
1161 curItemRowCount
= infoPtr
->uNumItem
? 1 : 0;
1163 if (!(infoPtr
->fHeightSet
))
1166 INT icon_height
= 0, cx
;
1168 /* Use the current font to determine the height of a tab. */
1169 GetTextMetricsW(hdc
, &fontMetrics
);
1171 /* Get the icon height */
1173 ImageList_GetIconSize(infoPtr
->himl
, &cx
, &icon_height
);
1175 /* Take the highest between font or icon */
1176 if (fontMetrics
.tmHeight
> icon_height
)
1177 item_height
= fontMetrics
.tmHeight
+ 2;
1179 item_height
= icon_height
;
1182 * Make sure there is enough space for the letters + icon + growing the
1183 * selected item + extra space for the selected item.
1185 infoPtr
->tabHeight
= item_height
+
1186 ((infoPtr
->dwStyle
& TCS_BUTTONS
) ? 2 : 1) *
1187 infoPtr
->uVItemPadding
;
1189 TRACE("tabH=%d, tmH=%d, iconh=%d\n",
1190 infoPtr
->tabHeight
, fontMetrics
.tmHeight
, icon_height
);
1193 TRACE("client right=%d\n", clientRect
.right
);
1195 /* Get the icon width */
1200 ImageList_GetIconSize(infoPtr
->himl
, &icon_width
, &cy
);
1202 if (infoPtr
->dwStyle
& TCS_FIXEDWIDTH
)
1205 /* Add padding if icon is present */
1206 icon_width
+= infoPtr
->uHItemPadding
;
1209 for (curItem
= 0; curItem
< infoPtr
->uNumItem
; curItem
++)
1211 TAB_ITEM
*curr
= TAB_GetItem(infoPtr
, curItem
);
1213 /* Set the leftmost position of the tab. */
1214 curr
->rect
.left
= curItemLeftPos
;
1216 if (infoPtr
->dwStyle
& TCS_FIXEDWIDTH
)
1218 curr
->rect
.right
= curr
->rect
.left
+
1219 max(infoPtr
->tabWidth
, icon_width
);
1221 else if (!curr
->pszText
)
1223 /* If no text use minimum tab width including padding. */
1224 if (infoPtr
->tabMinWidth
< 0)
1225 curr
->rect
.right
= curr
->rect
.left
+ GET_DEFAULT_MIN_TAB_WIDTH(infoPtr
);
1228 curr
->rect
.right
= curr
->rect
.left
+ infoPtr
->tabMinWidth
;
1230 /* Add extra padding if icon is present */
1231 if (infoPtr
->himl
&& infoPtr
->tabMinWidth
> 0 && infoPtr
->tabMinWidth
< DEFAULT_MIN_TAB_WIDTH
1232 && infoPtr
->uHItemPadding
> 1)
1233 curr
->rect
.right
+= EXTRA_ICON_PADDING
* (infoPtr
->uHItemPadding
-1);
1240 /* Calculate how wide the tab is depending on the text it contains */
1241 GetTextExtentPoint32W(hdc
, curr
->pszText
,
1242 lstrlenW(curr
->pszText
), &size
);
1244 tabwidth
= size
.cx
+ icon_width
+ 2 * infoPtr
->uHItemPadding
;
1246 if (infoPtr
->tabMinWidth
< 0)
1247 tabwidth
= max(tabwidth
, GET_DEFAULT_MIN_TAB_WIDTH(infoPtr
));
1249 tabwidth
= max(tabwidth
, infoPtr
->tabMinWidth
);
1251 curr
->rect
.right
= curr
->rect
.left
+ tabwidth
;
1252 TRACE("for <%s>, l,r=%d,%d\n",
1253 debugstr_w(curr
->pszText
), curr
->rect
.left
, curr
->rect
.right
);
1257 * Check if this is a multiline tab control and if so
1258 * check to see if we should wrap the tabs
1260 * Wrap all these tabs. We will arrange them evenly later.
1264 if (((infoPtr
->dwStyle
& TCS_MULTILINE
) || (infoPtr
->dwStyle
& TCS_VERTICAL
)) &&
1266 (clientRect
.right
- CONTROL_BORDER_SIZEX
- DISPLAY_AREA_PADDINGX
)))
1268 curr
->rect
.right
-= curr
->rect
.left
;
1270 curr
->rect
.left
= 0;
1272 TRACE("wrapping <%s>, l,r=%d,%d\n", debugstr_w(curr
->pszText
),
1273 curr
->rect
.left
, curr
->rect
.right
);
1276 curr
->rect
.bottom
= 0;
1277 curr
->rect
.top
= curItemRowCount
- 1;
1279 TRACE("Rect: %s\n", wine_dbgstr_rect(&curr
->rect
));
1282 * The leftmost position of the next item is the rightmost position
1285 if (infoPtr
->dwStyle
& TCS_BUTTONS
)
1287 curItemLeftPos
= curr
->rect
.right
+ BUTTON_SPACINGX
;
1288 if (infoPtr
->dwStyle
& TCS_FLATBUTTONS
)
1289 curItemLeftPos
+= FLAT_BTN_SPACINGX
;
1292 curItemLeftPos
= curr
->rect
.right
;
1295 if (!((infoPtr
->dwStyle
& TCS_MULTILINE
) || (infoPtr
->dwStyle
& TCS_VERTICAL
)))
1298 * Check if we need a scrolling control.
1300 infoPtr
->needsScrolling
= (curItemLeftPos
+ (2 * SELECTED_TAB_OFFSET
) >
1303 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1304 if(!infoPtr
->needsScrolling
)
1305 infoPtr
->leftmostVisible
= 0;
1310 * No scrolling in Multiline or Vertical styles.
1312 infoPtr
->needsScrolling
= FALSE
;
1313 infoPtr
->leftmostVisible
= 0;
1315 TAB_SetupScrolling(infoPtr
, &clientRect
);
1317 /* Set the number of rows */
1318 infoPtr
->uNumRows
= curItemRowCount
;
1320 /* Arrange all tabs evenly if style says so */
1321 if (!(infoPtr
->dwStyle
& TCS_RAGGEDRIGHT
) &&
1322 ((infoPtr
->dwStyle
& TCS_MULTILINE
) || (infoPtr
->dwStyle
& TCS_VERTICAL
)) &&
1323 (infoPtr
->uNumItem
> 0) &&
1324 (infoPtr
->uNumRows
> 1))
1326 INT tabPerRow
,remTab
,iRow
;
1331 * Ok windows tries to even out the rows. place the same
1332 * number of tabs in each row. So lets give that a shot
1335 tabPerRow
= infoPtr
->uNumItem
/ (infoPtr
->uNumRows
);
1336 remTab
= infoPtr
->uNumItem
% (infoPtr
->uNumRows
);
1338 for (iItm
=0,iRow
=0,iCount
=0,curItemLeftPos
=0;
1339 iItm
<infoPtr
->uNumItem
;
1342 /* normalize the current rect */
1343 TAB_ITEM
*curr
= TAB_GetItem(infoPtr
, iItm
);
1345 /* shift the item to the left side of the clientRect */
1346 curr
->rect
.right
-= curr
->rect
.left
;
1347 curr
->rect
.left
= 0;
1349 TRACE("r=%d, cl=%d, cl.r=%d, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1350 curr
->rect
.right
, curItemLeftPos
, clientRect
.right
,
1351 iCount
, iRow
, infoPtr
->uNumRows
, remTab
, tabPerRow
);
1353 /* if we have reached the maximum number of tabs on this row */
1354 /* move to the next row, reset our current item left position and */
1355 /* the count of items on this row */
1357 if (infoPtr
->dwStyle
& TCS_VERTICAL
) {
1358 /* Vert: Add the remaining tabs in the *last* remainder rows */
1359 if (iCount
>= ((iRow
>=(INT
)infoPtr
->uNumRows
- remTab
)?tabPerRow
+ 1:tabPerRow
)) {
1365 /* Horz: Add the remaining tabs in the *first* remainder rows */
1366 if (iCount
>= ((iRow
<remTab
)?tabPerRow
+ 1:tabPerRow
)) {
1373 /* shift the item to the right to place it as the next item in this row */
1374 curr
->rect
.left
+= curItemLeftPos
;
1375 curr
->rect
.right
+= curItemLeftPos
;
1376 curr
->rect
.top
= iRow
;
1377 if (infoPtr
->dwStyle
& TCS_BUTTONS
)
1379 curItemLeftPos
= curr
->rect
.right
+ 1;
1380 if (infoPtr
->dwStyle
& TCS_FLATBUTTONS
)
1381 curItemLeftPos
+= FLAT_BTN_SPACINGX
;
1384 curItemLeftPos
= curr
->rect
.right
;
1386 TRACE("arranging <%s>, l,r=%d,%d, row=%d\n",
1387 debugstr_w(curr
->pszText
), curr
->rect
.left
,
1388 curr
->rect
.right
, curr
->rect
.top
);
1395 INT widthDiff
, iIndexStart
=0, iIndexEnd
=0;
1399 while(iIndexStart
< infoPtr
->uNumItem
)
1401 TAB_ITEM
*start
= TAB_GetItem(infoPtr
, iIndexStart
);
1404 * find the index of the row
1406 /* find the first item on the next row */
1407 for (iIndexEnd
=iIndexStart
;
1408 (iIndexEnd
< infoPtr
->uNumItem
) &&
1409 (TAB_GetItem(infoPtr
, iIndexEnd
)->rect
.top
==
1412 /* intentionally blank */;
1415 * we need to justify these tabs so they fill the whole given
1419 /* find the amount of space remaining on this row */
1420 widthDiff
= clientRect
.right
- (2 * SELECTED_TAB_OFFSET
) -
1421 TAB_GetItem(infoPtr
, iIndexEnd
- 1)->rect
.right
;
1423 /* iCount is the number of tab items on this row */
1424 iCount
= iIndexEnd
- iIndexStart
;
1428 remainder
= widthDiff
% iCount
;
1429 widthDiff
= widthDiff
/ iCount
;
1430 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1431 for (iIndex
=iIndexStart
, iCount
=0; iIndex
< iIndexEnd
; iIndex
++, iCount
++)
1433 TAB_ITEM
*item
= TAB_GetItem(infoPtr
, iIndex
);
1435 item
->rect
.left
+= iCount
* widthDiff
;
1436 item
->rect
.right
+= (iCount
+ 1) * widthDiff
;
1438 TRACE("adjusting 1 <%s>, l,r=%d,%d\n",
1439 debugstr_w(item
->pszText
),
1440 item
->rect
.left
, item
->rect
.right
);
1443 TAB_GetItem(infoPtr
, iIndex
- 1)->rect
.right
+= remainder
;
1445 else /* we have only one item on this row, make it take up the entire row */
1447 start
->rect
.left
= clientRect
.left
;
1448 start
->rect
.right
= clientRect
.right
- 4;
1450 TRACE("adjusting 2 <%s>, l,r=%d,%d\n",
1451 debugstr_w(start
->pszText
),
1452 start
->rect
.left
, start
->rect
.right
);
1457 iIndexStart
= iIndexEnd
;
1462 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1463 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
1466 for(iIndex
= 0; iIndex
< infoPtr
->uNumItem
; iIndex
++)
1468 rcItem
= &TAB_GetItem(infoPtr
, iIndex
)->rect
;
1470 rcOriginal
= *rcItem
;
1472 /* this is rotating the items by 90 degrees clockwise around the center of the control */
1473 rcItem
->top
= (rcOriginal
.left
- clientRect
.left
);
1474 rcItem
->bottom
= rcItem
->top
+ (rcOriginal
.right
- rcOriginal
.left
);
1475 rcItem
->left
= rcOriginal
.top
;
1476 rcItem
->right
= rcOriginal
.bottom
;
1480 TAB_EnsureSelectionVisible(infoPtr
);
1481 TAB_RecalcHotTrack(infoPtr
, NULL
, NULL
, NULL
);
1484 SelectObject (hdc
, hOldFont
);
1485 ReleaseDC (infoPtr
->hwnd
, hdc
);
1490 TAB_EraseTabInterior(const TAB_INFO
*infoPtr
, HDC hdc
, INT iItem
, const RECT
*drawRect
)
1492 HBRUSH hbr
= CreateSolidBrush (comctl32_color
.clrBtnFace
);
1493 BOOL deleteBrush
= TRUE
;
1494 RECT rTemp
= *drawRect
;
1496 if (infoPtr
->dwStyle
& TCS_BUTTONS
)
1498 if (iItem
== infoPtr
->iSelected
)
1500 /* Background color */
1501 if (!(infoPtr
->dwStyle
& TCS_OWNERDRAWFIXED
))
1504 hbr
= GetSysColorBrush(COLOR_SCROLLBAR
);
1506 SetTextColor(hdc
, comctl32_color
.clr3dFace
);
1507 SetBkColor(hdc
, comctl32_color
.clr3dHilight
);
1509 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1510 * we better use 0x55aa bitmap brush to make scrollbar's background
1511 * look different from the window background.
1513 if (comctl32_color
.clr3dHilight
== comctl32_color
.clrWindow
)
1514 hbr
= COMCTL32_hPattern55AABrush
;
1516 deleteBrush
= FALSE
;
1518 FillRect(hdc
, &rTemp
, hbr
);
1520 else /* ! selected */
1522 if (infoPtr
->dwStyle
& TCS_FLATBUTTONS
)
1524 InflateRect(&rTemp
, 2, 2);
1525 FillRect(hdc
, &rTemp
, hbr
);
1526 if (iItem
== infoPtr
->iHotTracked
||
1527 (iItem
!= infoPtr
->iSelected
&& iItem
== infoPtr
->uFocus
))
1528 DrawEdge(hdc
, &rTemp
, BDR_RAISEDINNER
, BF_RECT
);
1531 FillRect(hdc
, &rTemp
, hbr
);
1535 else /* !TCS_BUTTONS */
1537 InflateRect(&rTemp
, -2, -2);
1538 if (!GetWindowTheme (infoPtr
->hwnd
))
1539 FillRect(hdc
, &rTemp
, hbr
);
1542 /* highlighting is drawn on top of previous fills */
1543 if (TAB_GetItem(infoPtr
, iItem
)->dwState
& TCIS_HIGHLIGHTED
)
1548 deleteBrush
= FALSE
;
1550 hbr
= GetSysColorBrush(COLOR_HIGHLIGHT
);
1551 FillRect(hdc
, &rTemp
, hbr
);
1555 if (deleteBrush
) DeleteObject(hbr
);
1558 /******************************************************************************
1559 * TAB_DrawItemInterior
1561 * This method is used to draw the interior (text and icon) of a single tab
1562 * into the tab control.
1565 TAB_DrawItemInterior(const TAB_INFO
*infoPtr
, HDC hdc
, INT iItem
, RECT
*drawRect
)
1574 /* if (drawRect == NULL) */
1581 * Get the rectangle for the item.
1583 isVisible
= TAB_InternalGetItemRect(infoPtr
, iItem
, &itemRect
, &selectedRect
);
1588 * Make sure drawRect points to something valid; simplifies code.
1590 drawRect
= &localRect
;
1593 * This logic copied from the part of TAB_DrawItem which draws
1594 * the tab background. It's important to keep it in sync. I
1595 * would have liked to avoid code duplication, but couldn't figure
1596 * out how without making spaghetti of TAB_DrawItem.
1598 if (iItem
== infoPtr
->iSelected
)
1599 *drawRect
= selectedRect
;
1601 *drawRect
= itemRect
;
1603 if (infoPtr
->dwStyle
& TCS_BUTTONS
)
1605 if (iItem
== infoPtr
->iSelected
)
1607 drawRect
->left
+= 4;
1609 drawRect
->right
-= 4;
1611 if (infoPtr
->dwStyle
& TCS_VERTICAL
)
1613 if (!(infoPtr
->dwStyle
& TCS_BOTTOM
)) drawRect
->right
+= 1;
1614 drawRect
->bottom
-= 4;
1618 if (infoPtr
->dwStyle
& TCS_BOTTOM
)
1621 drawRect
->bottom
-= 4;
1624 drawRect
->bottom
-= 1;
1629 drawRect
->left
+= 2;
1631 drawRect
->right
-= 2;
1632 drawRect
->bottom
-= 2;
1637 if ((infoPtr
->dwStyle
& TCS_VERTICAL
) && (infoPtr
->dwStyle
& TCS_BOTTOM
))
1639 if (iItem
!= infoPtr
->iSelected
)
1641 drawRect
->left
+= 2;
1643 drawRect
->bottom
-= 2;
1646 else if (infoPtr
->dwStyle
& TCS_VERTICAL
)
1648 if (iItem
== infoPtr
->iSelected
)
1650 drawRect
->right
+= 1;
1655 drawRect
->right
-= 2;
1656 drawRect
->bottom
-= 2;
1659 else if (infoPtr
->dwStyle
& TCS_BOTTOM
)
1661 if (iItem
== infoPtr
->iSelected
)
1667 InflateRect(drawRect
, -2, -2);
1668 drawRect
->bottom
+= 2;
1673 if (iItem
== infoPtr
->iSelected
)
1675 drawRect
->bottom
+= 3;
1679 drawRect
->bottom
-= 2;
1680 InflateRect(drawRect
, -2, 0);
1685 TRACE("drawRect=(%s)\n", wine_dbgstr_rect(drawRect
));
1687 /* Clear interior */
1688 TAB_EraseTabInterior (infoPtr
, hdc
, iItem
, drawRect
);
1690 /* Draw the focus rectangle */
1691 if (!(infoPtr
->dwStyle
& TCS_FOCUSNEVER
) &&
1692 (GetFocus() == infoPtr
->hwnd
) &&
1693 (iItem
== infoPtr
->uFocus
) )
1695 RECT rFocus
= *drawRect
;
1697 if (!(infoPtr
->dwStyle
& TCS_BUTTONS
)) InflateRect(&rFocus
, -3, -3);
1698 if (infoPtr
->dwStyle
& TCS_BOTTOM
&& !(infoPtr
->dwStyle
& TCS_VERTICAL
))
1701 /* focus should stay on selected item for TCS_BUTTONS style */
1702 if (!((infoPtr
->dwStyle
& TCS_BUTTONS
) && (infoPtr
->iSelected
!= iItem
)))
1703 DrawFocusRect(hdc
, &rFocus
);
1709 htextPen
= CreatePen( PS_SOLID
, 1, comctl32_color
.clrBtnText
);
1710 holdPen
= SelectObject(hdc
, htextPen
);
1711 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
1714 * Setup for text output
1716 oldBkMode
= SetBkMode(hdc
, TRANSPARENT
);
1717 if (!GetWindowTheme (infoPtr
->hwnd
) || (infoPtr
->dwStyle
& TCS_BUTTONS
))
1719 if ((infoPtr
->dwStyle
& TCS_HOTTRACK
) && (iItem
== infoPtr
->iHotTracked
) &&
1720 !(infoPtr
->dwStyle
& TCS_FLATBUTTONS
))
1721 SetTextColor(hdc
, comctl32_color
.clrHighlight
);
1722 else if (TAB_GetItem(infoPtr
, iItem
)->dwState
& TCIS_HIGHLIGHTED
)
1723 SetTextColor(hdc
, comctl32_color
.clrHighlightText
);
1725 SetTextColor(hdc
, comctl32_color
.clrBtnText
);
1729 * if owner draw, tell the owner to draw
1731 if ((infoPtr
->dwStyle
& TCS_OWNERDRAWFIXED
) && GetParent(infoPtr
->hwnd
))
1737 drawRect
->right
-= 1;
1738 if ( iItem
== infoPtr
->iSelected
)
1740 drawRect
->right
-= 1;
1741 drawRect
->left
+= 1;
1745 * get the control id
1747 id
= (UINT
)GetWindowLongPtrW( infoPtr
->hwnd
, GWLP_ID
);
1750 * put together the DRAWITEMSTRUCT
1752 dis
.CtlType
= ODT_TAB
;
1755 dis
.itemAction
= ODA_DRAWENTIRE
;
1757 if ( iItem
== infoPtr
->iSelected
)
1758 dis
.itemState
|= ODS_SELECTED
;
1759 if (infoPtr
->uFocus
== iItem
)
1760 dis
.itemState
|= ODS_FOCUS
;
1761 dis
.hwndItem
= infoPtr
->hwnd
;
1763 CopyRect(&dis
.rcItem
,drawRect
);
1764 dis
.itemData
= (ULONG_PTR
)TAB_GetItem(infoPtr
, iItem
)->extra
;
1767 * send the draw message
1769 SendMessageW( infoPtr
->hwndNotify
, WM_DRAWITEM
, id
, (LPARAM
)&dis
);
1773 TAB_ITEM
*item
= TAB_GetItem(infoPtr
, iItem
);
1777 /* used to center the icon and text in the tab */
1779 INT center_offset_h
, center_offset_v
;
1781 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1782 rcImage
= *drawRect
;
1786 rcText
.left
= rcText
.top
= rcText
.right
= rcText
.bottom
= 0;
1788 /* get the rectangle that the text fits in */
1791 DrawTextW(hdc
, item
->pszText
, -1, &rcText
, DT_CALCRECT
);
1794 * If not owner draw, then do the drawing ourselves.
1798 if (infoPtr
->himl
&& item
->iImage
!= -1)
1803 ImageList_GetIconSize(infoPtr
->himl
, &cx
, &cy
);
1805 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
1807 center_offset_h
= ((drawRect
->bottom
- drawRect
->top
) - (cy
+ infoPtr
->uHItemPadding
+ (rcText
.right
- rcText
.left
))) / 2;
1808 center_offset_v
= ((drawRect
->right
- drawRect
->left
) - cx
) / 2;
1812 center_offset_h
= ((drawRect
->right
- drawRect
->left
) - (cx
+ infoPtr
->uHItemPadding
+ (rcText
.right
- rcText
.left
))) / 2;
1813 center_offset_v
= ((drawRect
->bottom
- drawRect
->top
) - cy
) / 2;
1816 /* if an item is selected, the icon is shifted up instead of down */
1817 if (iItem
== infoPtr
->iSelected
)
1818 center_offset_v
-= infoPtr
->uVItemPadding
/ 2;
1820 center_offset_v
+= infoPtr
->uVItemPadding
/ 2;
1822 if (infoPtr
->dwStyle
& TCS_FIXEDWIDTH
&& infoPtr
->dwStyle
& (TCS_FORCELABELLEFT
| TCS_FORCEICONLEFT
))
1823 center_offset_h
= infoPtr
->uHItemPadding
;
1825 if (center_offset_h
< 2)
1826 center_offset_h
= 2;
1828 if (center_offset_v
< 0)
1829 center_offset_v
= 0;
1831 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%s), textlen=%d\n",
1832 debugstr_w(item
->pszText
), center_offset_h
, center_offset_v
,
1833 wine_dbgstr_rect(drawRect
), (rcText
.right
-rcText
.left
));
1835 if((infoPtr
->dwStyle
& TCS_VERTICAL
) && (infoPtr
->dwStyle
& TCS_BOTTOM
))
1837 rcImage
.top
= drawRect
->top
+ center_offset_h
;
1838 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1839 /* right side of the tab, but the image still uses the left as its x position */
1840 /* this keeps the image always drawn off of the same side of the tab */
1841 rcImage
.left
= drawRect
->right
- cx
- center_offset_v
;
1842 drawRect
->top
+= cy
+ infoPtr
->uHItemPadding
;
1844 else if(infoPtr
->dwStyle
& TCS_VERTICAL
)
1846 rcImage
.top
= drawRect
->bottom
- cy
- center_offset_h
;
1847 rcImage
.left
= drawRect
->left
+ center_offset_v
;
1848 drawRect
->bottom
-= cy
+ infoPtr
->uHItemPadding
;
1850 else /* normal style, whether TCS_BOTTOM or not */
1852 rcImage
.left
= drawRect
->left
+ center_offset_h
;
1853 rcImage
.top
= drawRect
->top
+ center_offset_v
;
1854 drawRect
->left
+= cx
+ infoPtr
->uHItemPadding
;
1857 TRACE("drawing image=%d, left=%d, top=%d\n",
1858 item
->iImage
, rcImage
.left
, rcImage
.top
-1);
1870 /* Now position text */
1871 if (infoPtr
->dwStyle
& TCS_FIXEDWIDTH
&& infoPtr
->dwStyle
& TCS_FORCELABELLEFT
)
1872 center_offset_h
= infoPtr
->uHItemPadding
;
1874 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
1875 center_offset_h
= ((drawRect
->bottom
- drawRect
->top
) - (rcText
.right
- rcText
.left
)) / 2;
1877 center_offset_h
= ((drawRect
->right
- drawRect
->left
) - (rcText
.right
- rcText
.left
)) / 2;
1879 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
1881 if(infoPtr
->dwStyle
& TCS_BOTTOM
)
1882 drawRect
->top
+=center_offset_h
;
1884 drawRect
->bottom
-=center_offset_h
;
1886 center_offset_v
= ((drawRect
->right
- drawRect
->left
) - (rcText
.bottom
- rcText
.top
)) / 2;
1890 drawRect
->left
+= center_offset_h
;
1891 center_offset_v
= ((drawRect
->bottom
- drawRect
->top
) - (rcText
.bottom
- rcText
.top
)) / 2;
1894 /* if an item is selected, the text is shifted up instead of down */
1895 if (iItem
== infoPtr
->iSelected
)
1896 center_offset_v
-= infoPtr
->uVItemPadding
/ 2;
1898 center_offset_v
+= infoPtr
->uVItemPadding
/ 2;
1900 if (center_offset_v
< 0)
1901 center_offset_v
= 0;
1903 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
1904 drawRect
->left
+= center_offset_v
;
1906 drawRect
->top
+= center_offset_v
;
1909 if(infoPtr
->dwStyle
& TCS_VERTICAL
) /* if we are vertical rotate the text and each character */
1911 static const WCHAR ArialW
[] = { 'A','r','i','a','l',0 };
1914 INT nEscapement
= 900;
1915 INT nOrientation
= 900;
1917 if(infoPtr
->dwStyle
& TCS_BOTTOM
)
1920 nOrientation
= -900;
1923 /* to get a font with the escapement and orientation we are looking for, we need to */
1924 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1925 if (!GetObjectW((infoPtr
->hFont
) ?
1926 infoPtr
->hFont
: GetStockObject(SYSTEM_FONT
),
1927 sizeof(LOGFONTW
),&logfont
))
1931 lstrcpyW(logfont
.lfFaceName
, ArialW
);
1932 logfont
.lfHeight
= -MulDiv(iPointSize
, GetDeviceCaps(hdc
, LOGPIXELSY
),
1934 logfont
.lfWeight
= FW_NORMAL
;
1935 logfont
.lfItalic
= 0;
1936 logfont
.lfUnderline
= 0;
1937 logfont
.lfStrikeOut
= 0;
1940 logfont
.lfEscapement
= nEscapement
;
1941 logfont
.lfOrientation
= nOrientation
;
1942 hFont
= CreateFontIndirectW(&logfont
);
1943 SelectObject(hdc
, hFont
);
1948 (infoPtr
->dwStyle
& TCS_BOTTOM
) ? drawRect
->right
: drawRect
->left
,
1949 (!(infoPtr
->dwStyle
& TCS_BOTTOM
)) ? drawRect
->bottom
: drawRect
->top
,
1953 lstrlenW(item
->pszText
),
1957 DeleteObject(hFont
);
1961 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%s), textlen=%d\n",
1962 debugstr_w(item
->pszText
), center_offset_h
, center_offset_v
,
1963 wine_dbgstr_rect(drawRect
), (rcText
.right
-rcText
.left
));
1970 lstrlenW(item
->pszText
),
1972 DT_LEFT
| DT_SINGLELINE
1977 *drawRect
= rcTemp
; /* restore drawRect */
1983 SelectObject(hdc
, hOldFont
);
1984 SetBkMode(hdc
, oldBkMode
);
1985 SelectObject(hdc
, holdPen
);
1986 DeleteObject( htextPen
);
1989 /******************************************************************************
1992 * This method is used to draw a single tab into the tab control.
1994 static void TAB_DrawItem(const TAB_INFO
*infoPtr
, HDC hdc
, INT iItem
)
1999 RECT r
, fillRect
, r1
;
2002 COLORREF bkgnd
, corner
;
2006 * Get the rectangle for the item.
2008 isVisible
= TAB_InternalGetItemRect(infoPtr
,
2017 /* Clip UpDown control to not draw over it */
2018 if (infoPtr
->needsScrolling
)
2020 GetWindowRect(infoPtr
->hwnd
, &rC
);
2021 GetWindowRect(infoPtr
->hwndUpDown
, &rUD
);
2022 ExcludeClipRect(hdc
, rUD
.left
- rC
.left
, rUD
.top
- rC
.top
, rUD
.right
- rC
.left
, rUD
.bottom
- rC
.top
);
2025 /* If you need to see what the control is doing,
2026 * then override these variables. They will change what
2027 * fill colors are used for filling the tabs, and the
2028 * corners when drawing the edge.
2030 bkgnd
= comctl32_color
.clrBtnFace
;
2031 corner
= comctl32_color
.clrBtnFace
;
2033 if (infoPtr
->dwStyle
& TCS_BUTTONS
)
2035 /* Get item rectangle */
2038 /* Separators between flat buttons */
2039 if ((infoPtr
->dwStyle
& TCS_FLATBUTTONS
) && (infoPtr
->exStyle
& TCS_EX_FLATSEPARATORS
))
2042 r1
.right
+= (FLAT_BTN_SPACINGX
-2);
2043 DrawEdge(hdc
, &r1
, EDGE_ETCHED
, BF_RIGHT
);
2046 if (iItem
== infoPtr
->iSelected
)
2048 DrawEdge(hdc
, &r
, EDGE_SUNKEN
, BF_SOFT
|BF_RECT
);
2050 OffsetRect(&r
, 1, 1);
2052 else /* ! selected */
2054 DWORD state
= TAB_GetItem(infoPtr
, iItem
)->dwState
;
2056 if ((state
& TCIS_BUTTONPRESSED
) || (iItem
== infoPtr
->uFocus
))
2057 DrawEdge(hdc
, &r
, EDGE_SUNKEN
, BF_SOFT
|BF_RECT
);
2059 if (!(infoPtr
->dwStyle
& TCS_FLATBUTTONS
))
2060 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_RECT
);
2063 else /* !TCS_BUTTONS */
2065 /* We draw a rectangle of different sizes depending on the selection
2067 if (iItem
== infoPtr
->iSelected
) {
2069 GetClientRect (infoPtr
->hwnd
, &rect
);
2070 clRight
= rect
.right
;
2071 clBottom
= rect
.bottom
;
2078 * Erase the background. (Delay it but setup rectangle.)
2079 * This is necessary when drawing the selected item since it is larger
2080 * than the others, it might overlap with stuff already drawn by the
2085 /* Draw themed tabs - but only if they are at the top.
2086 * Windows draws even side or bottom tabs themed, with wacky results.
2087 * However, since in Wine apps may get themed that did not opt in via
2088 * a manifest avoid theming when we know the result will be wrong */
2089 if ((theme
= GetWindowTheme (infoPtr
->hwnd
))
2090 && ((infoPtr
->dwStyle
& (TCS_VERTICAL
| TCS_BOTTOM
)) == 0))
2092 static const int partIds
[8] = {
2095 TABP_TABITEMLEFTEDGE
,
2096 TABP_TABITEMRIGHTEDGE
,
2097 TABP_TABITEMBOTHEDGE
,
2100 TABP_TOPTABITEMLEFTEDGE
,
2101 TABP_TOPTABITEMRIGHTEDGE
,
2102 TABP_TOPTABITEMBOTHEDGE
,
2105 int stateId
= TIS_NORMAL
;
2107 /* selected and unselected tabs have different parts */
2108 if (iItem
== infoPtr
->iSelected
)
2110 /* The part also differs on the position of a tab on a line.
2111 * "Visually" determining the position works well enough. */
2112 if(selectedRect
.left
== 0)
2114 if(selectedRect
.right
== clRight
)
2117 if (iItem
== infoPtr
->iSelected
)
2118 stateId
= TIS_SELECTED
;
2119 else if (iItem
== infoPtr
->iHotTracked
)
2121 else if (iItem
== infoPtr
->uFocus
)
2122 stateId
= TIS_FOCUSED
;
2124 /* Adjust rectangle for bottommost row */
2125 if (TAB_GetItem(infoPtr
, iItem
)->rect
.top
== infoPtr
->uNumRows
-1)
2128 DrawThemeBackground (theme
, hdc
, partIds
[partIndex
], stateId
, &r
, NULL
);
2129 GetThemeBackgroundContentRect (theme
, hdc
, partIds
[partIndex
], stateId
, &r
, &r
);
2131 else if(infoPtr
->dwStyle
& TCS_VERTICAL
)
2133 /* These are for adjusting the drawing of a Selected tab */
2134 /* The initial values are for the normal case of non-Selected */
2135 int ZZ
= 1; /* Do not stretch if selected */
2136 if (iItem
== infoPtr
->iSelected
) {
2139 /* if leftmost draw the line longer */
2140 if(selectedRect
.top
== 0)
2141 fillRect
.top
+= CONTROL_BORDER_SIZEY
;
2142 /* if rightmost draw the line longer */
2143 if(selectedRect
.bottom
== clBottom
)
2144 fillRect
.bottom
-= CONTROL_BORDER_SIZEY
;
2147 if (infoPtr
->dwStyle
& TCS_BOTTOM
)
2149 /* Adjust both rectangles to match native */
2152 TRACE("<right> item=%d, fill=(%s), edge=(%s)\n",
2153 iItem
, wine_dbgstr_rect(&fillRect
), wine_dbgstr_rect(&r
));
2155 /* Clear interior */
2156 SetBkColor(hdc
, bkgnd
);
2157 ExtTextOutW(hdc
, 0, 0, 2, &fillRect
, NULL
, 0, 0);
2159 /* Draw rectangular edge around tab */
2160 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_RIGHT
|BF_TOP
|BF_BOTTOM
);
2162 /* Now erase the top corner and draw diagonal edge */
2163 SetBkColor(hdc
, corner
);
2164 r1
.left
= r
.right
- ROUND_CORNER_SIZE
- 1;
2167 r1
.bottom
= r1
.top
+ ROUND_CORNER_SIZE
;
2168 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2170 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDTOPLEFT
);
2172 /* Now erase the bottom corner and draw diagonal edge */
2173 r1
.left
= r
.right
- ROUND_CORNER_SIZE
- 1;
2174 r1
.bottom
= r
.bottom
;
2176 r1
.top
= r1
.bottom
- ROUND_CORNER_SIZE
;
2177 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2179 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDBOTTOMLEFT
);
2181 if ((iItem
== infoPtr
->iSelected
) && (selectedRect
.top
== 0)) {
2185 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_TOP
);
2191 TRACE("<left> item=%d, fill=(%s), edge=(%s)\n",
2192 iItem
, wine_dbgstr_rect(&fillRect
), wine_dbgstr_rect(&r
));
2194 /* Clear interior */
2195 SetBkColor(hdc
, bkgnd
);
2196 ExtTextOutW(hdc
, 0, 0, 2, &fillRect
, NULL
, 0, 0);
2198 /* Draw rectangular edge around tab */
2199 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_LEFT
|BF_TOP
|BF_BOTTOM
);
2201 /* Now erase the top corner and draw diagonal edge */
2202 SetBkColor(hdc
, corner
);
2205 r1
.right
= r1
.left
+ ROUND_CORNER_SIZE
+ 1;
2206 r1
.bottom
= r1
.top
+ ROUND_CORNER_SIZE
;
2207 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2209 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDTOPRIGHT
);
2211 /* Now erase the bottom corner and draw diagonal edge */
2213 r1
.bottom
= r
.bottom
;
2214 r1
.right
= r1
.left
+ ROUND_CORNER_SIZE
+ 1;
2215 r1
.top
= r1
.bottom
- ROUND_CORNER_SIZE
;
2216 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2218 DrawEdge(hdc
, &r1
, EDGE_SUNKEN
, BF_DIAGONAL_ENDTOPLEFT
);
2221 else /* ! TCS_VERTICAL */
2223 /* These are for adjusting the drawing of a Selected tab */
2224 /* The initial values are for the normal case of non-Selected */
2225 if (iItem
== infoPtr
->iSelected
) {
2226 /* if leftmost draw the line longer */
2227 if(selectedRect
.left
== 0)
2228 fillRect
.left
+= CONTROL_BORDER_SIZEX
;
2229 /* if rightmost draw the line longer */
2230 if(selectedRect
.right
== clRight
)
2231 fillRect
.right
-= CONTROL_BORDER_SIZEX
;
2234 if (infoPtr
->dwStyle
& TCS_BOTTOM
)
2236 /* Adjust both rectangles for topmost row */
2237 if (TAB_GetItem(infoPtr
, iItem
)->rect
.top
== infoPtr
->uNumRows
-1)
2243 TRACE("<bottom> item=%d, fill=(%s), edge=(%s)\n",
2244 iItem
, wine_dbgstr_rect(&fillRect
), wine_dbgstr_rect(&r
));
2246 /* Clear interior */
2247 SetBkColor(hdc
, bkgnd
);
2248 ExtTextOutW(hdc
, 0, 0, 2, &fillRect
, NULL
, 0, 0);
2250 /* Draw rectangular edge around tab */
2251 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_LEFT
|BF_BOTTOM
|BF_RIGHT
);
2253 /* Now erase the righthand corner and draw diagonal edge */
2254 SetBkColor(hdc
, corner
);
2255 r1
.left
= r
.right
- ROUND_CORNER_SIZE
;
2256 r1
.bottom
= r
.bottom
;
2258 r1
.top
= r1
.bottom
- ROUND_CORNER_SIZE
- 1;
2259 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2261 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDBOTTOMLEFT
);
2263 /* Now erase the lefthand corner and draw diagonal edge */
2265 r1
.bottom
= r
.bottom
;
2266 r1
.right
= r1
.left
+ ROUND_CORNER_SIZE
;
2267 r1
.top
= r1
.bottom
- ROUND_CORNER_SIZE
- 1;
2268 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2270 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDTOPLEFT
);
2272 if (iItem
== infoPtr
->iSelected
)
2276 if (selectedRect
.left
== 0)
2281 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_LEFT
);
2288 /* Adjust both rectangles for bottommost row */
2289 if (TAB_GetItem(infoPtr
, iItem
)->rect
.top
== infoPtr
->uNumRows
-1)
2291 fillRect
.bottom
+= 3;
2295 TRACE("<top> item=%d, fill=(%s), edge=(%s)\n",
2296 iItem
, wine_dbgstr_rect(&fillRect
), wine_dbgstr_rect(&r
));
2298 /* Clear interior */
2299 SetBkColor(hdc
, bkgnd
);
2300 ExtTextOutW(hdc
, 0, 0, 2, &fillRect
, NULL
, 0, 0);
2302 /* Draw rectangular edge around tab */
2303 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_LEFT
|BF_TOP
|BF_RIGHT
);
2305 /* Now erase the righthand corner and draw diagonal edge */
2306 SetBkColor(hdc
, corner
);
2307 r1
.left
= r
.right
- ROUND_CORNER_SIZE
;
2310 r1
.bottom
= r1
.top
+ ROUND_CORNER_SIZE
+ 1;
2311 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2313 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDBOTTOMRIGHT
);
2315 /* Now erase the lefthand corner and draw diagonal edge */
2318 r1
.right
= r1
.left
+ ROUND_CORNER_SIZE
;
2319 r1
.bottom
= r1
.top
+ ROUND_CORNER_SIZE
+ 1;
2320 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2322 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDTOPRIGHT
);
2327 TAB_DumpItemInternal(infoPtr
, iItem
);
2329 /* This modifies r to be the text rectangle. */
2330 TAB_DrawItemInterior(infoPtr
, hdc
, iItem
, &r
);
2334 /******************************************************************************
2337 * This method is used to draw the raised border around the tab control
2340 static void TAB_DrawBorder(const TAB_INFO
*infoPtr
, HDC hdc
)
2343 HTHEME theme
= GetWindowTheme (infoPtr
->hwnd
);
2345 GetClientRect (infoPtr
->hwnd
, &rect
);
2348 * Adjust for the style
2351 if (infoPtr
->uNumItem
)
2353 if ((infoPtr
->dwStyle
& TCS_BOTTOM
) && !(infoPtr
->dwStyle
& TCS_VERTICAL
))
2354 rect
.bottom
-= infoPtr
->tabHeight
* infoPtr
->uNumRows
+ CONTROL_BORDER_SIZEX
;
2355 else if((infoPtr
->dwStyle
& TCS_BOTTOM
) && (infoPtr
->dwStyle
& TCS_VERTICAL
))
2356 rect
.right
-= infoPtr
->tabHeight
* infoPtr
->uNumRows
+ CONTROL_BORDER_SIZEX
;
2357 else if(infoPtr
->dwStyle
& TCS_VERTICAL
)
2358 rect
.left
+= infoPtr
->tabHeight
* infoPtr
->uNumRows
+ CONTROL_BORDER_SIZEX
;
2359 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2360 rect
.top
+= infoPtr
->tabHeight
* infoPtr
->uNumRows
+ CONTROL_BORDER_SIZEX
;
2363 TRACE("border=(%s)\n", wine_dbgstr_rect(&rect
));
2366 DrawThemeBackground (theme
, hdc
, TABP_PANE
, 0, &rect
, NULL
);
2368 DrawEdge(hdc
, &rect
, EDGE_RAISED
, BF_SOFT
|BF_RECT
);
2371 /******************************************************************************
2374 * This method repaints the tab control..
2376 static void TAB_Refresh (const TAB_INFO
*infoPtr
, HDC hdc
)
2381 if (!infoPtr
->DoRedraw
)
2384 hOldFont
= SelectObject (hdc
, infoPtr
->hFont
);
2386 if (infoPtr
->dwStyle
& TCS_BUTTONS
)
2388 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
2389 TAB_DrawItem (infoPtr
, hdc
, i
);
2393 /* Draw all the non selected item first */
2394 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
2396 if (i
!= infoPtr
->iSelected
)
2397 TAB_DrawItem (infoPtr
, hdc
, i
);
2400 /* Now, draw the border, draw it before the selected item
2401 * since the selected item overwrites part of the border. */
2402 TAB_DrawBorder (infoPtr
, hdc
);
2404 /* Then, draw the selected item */
2405 TAB_DrawItem (infoPtr
, hdc
, infoPtr
->iSelected
);
2408 SelectObject (hdc
, hOldFont
);
2411 static inline DWORD
TAB_GetRowCount (const TAB_INFO
*infoPtr
)
2413 TRACE("(%p)\n", infoPtr
);
2414 return infoPtr
->uNumRows
;
2417 static inline LRESULT
TAB_SetRedraw (TAB_INFO
*infoPtr
, BOOL doRedraw
)
2419 infoPtr
->DoRedraw
= doRedraw
;
2423 /******************************************************************************
2424 * TAB_EnsureSelectionVisible
2426 * This method will make sure that the current selection is completely
2427 * visible by scrolling until it is.
2429 static void TAB_EnsureSelectionVisible(
2432 INT iSelected
= infoPtr
->iSelected
;
2433 INT iOrigLeftmostVisible
= infoPtr
->leftmostVisible
;
2435 /* set the items row to the bottommost row or topmost row depending on
2437 if ((infoPtr
->uNumRows
> 1) && !(infoPtr
->dwStyle
& TCS_BUTTONS
))
2439 TAB_ITEM
*selected
= TAB_GetItem(infoPtr
, iSelected
);
2443 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
2444 newselected
= selected
->rect
.left
;
2446 newselected
= selected
->rect
.top
;
2448 /* the target row is always (number of rows - 1)
2449 as row 0 is furthest from the clientRect */
2450 iTargetRow
= infoPtr
->uNumRows
- 1;
2452 if (newselected
!= iTargetRow
)
2455 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
2457 for (i
=0; i
< infoPtr
->uNumItem
; i
++)
2459 /* move everything in the row of the selected item to the iTargetRow */
2460 TAB_ITEM
*item
= TAB_GetItem(infoPtr
, i
);
2462 if (item
->rect
.left
== newselected
)
2463 item
->rect
.left
= iTargetRow
;
2466 if (item
->rect
.left
> newselected
)
2473 for (i
=0; i
< infoPtr
->uNumItem
; i
++)
2475 TAB_ITEM
*item
= TAB_GetItem(infoPtr
, i
);
2477 if (item
->rect
.top
== newselected
)
2478 item
->rect
.top
= iTargetRow
;
2481 if (item
->rect
.top
> newselected
)
2486 TAB_RecalcHotTrack(infoPtr
, NULL
, NULL
, NULL
);
2491 * Do the trivial cases first.
2493 if ( (!infoPtr
->needsScrolling
) ||
2494 (infoPtr
->hwndUpDown
==0) || (infoPtr
->dwStyle
& TCS_VERTICAL
))
2497 if (infoPtr
->leftmostVisible
>= iSelected
)
2499 infoPtr
->leftmostVisible
= iSelected
;
2503 TAB_ITEM
*selected
= TAB_GetItem(infoPtr
, iSelected
);
2508 /* Calculate the part of the client area that is visible */
2509 GetClientRect(infoPtr
->hwnd
, &r
);
2512 GetClientRect(infoPtr
->hwndUpDown
, &r
);
2515 if ((selected
->rect
.right
-
2516 selected
->rect
.left
) >= width
)
2518 /* Special case: width of selected item is greater than visible
2521 infoPtr
->leftmostVisible
= iSelected
;
2525 for (i
= infoPtr
->leftmostVisible
; i
< infoPtr
->uNumItem
; i
++)
2527 if ((selected
->rect
.right
- TAB_GetItem(infoPtr
, i
)->rect
.left
) < width
)
2530 infoPtr
->leftmostVisible
= i
;
2534 if (infoPtr
->leftmostVisible
!= iOrigLeftmostVisible
)
2535 TAB_RecalcHotTrack(infoPtr
, NULL
, NULL
, NULL
);
2537 SendMessageW(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
2538 MAKELONG(infoPtr
->leftmostVisible
, 0));
2541 /******************************************************************************
2542 * TAB_InvalidateTabArea
2544 * This method will invalidate the portion of the control that contains the
2545 * tabs. It is called when the state of the control changes and needs
2548 static void TAB_InvalidateTabArea(const TAB_INFO
*infoPtr
)
2550 RECT clientRect
, rInvalidate
, rAdjClient
;
2551 INT lastRow
= infoPtr
->uNumRows
- 1;
2554 if (lastRow
< 0) return;
2556 GetClientRect(infoPtr
->hwnd
, &clientRect
);
2557 rInvalidate
= clientRect
;
2558 rAdjClient
= clientRect
;
2560 TAB_AdjustRect(infoPtr
, 0, &rAdjClient
);
2562 TAB_InternalGetItemRect(infoPtr
, infoPtr
->uNumItem
-1 , &rect
, NULL
);
2563 if ((infoPtr
->dwStyle
& TCS_BOTTOM
) && (infoPtr
->dwStyle
& TCS_VERTICAL
))
2565 rInvalidate
.left
= rAdjClient
.right
;
2566 if (infoPtr
->uNumRows
== 1)
2567 rInvalidate
.bottom
= clientRect
.top
+ rect
.bottom
+ 2 * SELECTED_TAB_OFFSET
;
2569 else if(infoPtr
->dwStyle
& TCS_VERTICAL
)
2571 rInvalidate
.right
= rAdjClient
.left
;
2572 if (infoPtr
->uNumRows
== 1)
2573 rInvalidate
.bottom
= clientRect
.top
+ rect
.bottom
+ 2 * SELECTED_TAB_OFFSET
;
2575 else if (infoPtr
->dwStyle
& TCS_BOTTOM
)
2577 rInvalidate
.top
= rAdjClient
.bottom
;
2578 if (infoPtr
->uNumRows
== 1)
2579 rInvalidate
.right
= clientRect
.left
+ rect
.right
+ 2 * SELECTED_TAB_OFFSET
;
2583 rInvalidate
.bottom
= rAdjClient
.top
;
2584 if (infoPtr
->uNumRows
== 1)
2585 rInvalidate
.right
= clientRect
.left
+ rect
.right
+ 2 * SELECTED_TAB_OFFSET
;
2588 /* Punch out the updown control */
2589 if (infoPtr
->needsScrolling
&& (rInvalidate
.right
> 0)) {
2591 GetClientRect(infoPtr
->hwndUpDown
, &r
);
2592 if (rInvalidate
.right
> clientRect
.right
- r
.left
)
2593 rInvalidate
.right
= rInvalidate
.right
- (r
.right
- r
.left
);
2595 rInvalidate
.right
= clientRect
.right
- r
.left
;
2598 TRACE("invalidate (%s)\n", wine_dbgstr_rect(&rInvalidate
));
2600 InvalidateRect(infoPtr
->hwnd
, &rInvalidate
, TRUE
);
2603 static inline LRESULT
TAB_Paint (TAB_INFO
*infoPtr
, HDC hdcPaint
)
2612 hdc
= BeginPaint (infoPtr
->hwnd
, &ps
);
2613 TRACE("erase %d, rect=(%s)\n", ps
.fErase
, wine_dbgstr_rect(&ps
.rcPaint
));
2616 TAB_Refresh (infoPtr
, hdc
);
2619 EndPaint (infoPtr
->hwnd
, &ps
);
2625 TAB_InsertItemT (TAB_INFO
*infoPtr
, INT iItem
, const TCITEMW
*pti
, BOOL bUnicode
)
2630 GetClientRect (infoPtr
->hwnd
, &rect
);
2631 TRACE("Rect: %p %s\n", infoPtr
->hwnd
, wine_dbgstr_rect(&rect
));
2633 if (iItem
< 0) return -1;
2634 if (iItem
> infoPtr
->uNumItem
)
2635 iItem
= infoPtr
->uNumItem
;
2637 TAB_DumpItemExternalT(pti
, iItem
, bUnicode
);
2640 if (infoPtr
->uNumItem
== 0) {
2641 infoPtr
->items
= Alloc (TAB_ITEM_SIZE(infoPtr
));
2642 infoPtr
->uNumItem
++;
2643 infoPtr
->iSelected
= 0;
2646 LPBYTE oldItems
= (LPBYTE
)infoPtr
->items
;
2648 infoPtr
->uNumItem
++;
2649 infoPtr
->items
= Alloc (TAB_ITEM_SIZE(infoPtr
) * infoPtr
->uNumItem
);
2651 /* pre insert copy */
2653 memcpy (infoPtr
->items
, oldItems
,
2654 iItem
* TAB_ITEM_SIZE(infoPtr
));
2657 /* post insert copy */
2658 if (iItem
< infoPtr
->uNumItem
- 1) {
2659 memcpy (TAB_GetItem(infoPtr
, iItem
+ 1),
2660 oldItems
+ iItem
* TAB_ITEM_SIZE(infoPtr
),
2661 (infoPtr
->uNumItem
- iItem
- 1) * TAB_ITEM_SIZE(infoPtr
));
2665 if (iItem
<= infoPtr
->iSelected
)
2666 infoPtr
->iSelected
++;
2671 item
= TAB_GetItem(infoPtr
, iItem
);
2673 item
->pszText
= NULL
;
2675 if (pti
->mask
& TCIF_TEXT
)
2678 Str_SetPtrW (&item
->pszText
, pti
->pszText
);
2680 Str_SetPtrAtoW (&item
->pszText
, (LPSTR
)pti
->pszText
);
2683 if (pti
->mask
& TCIF_IMAGE
)
2684 item
->iImage
= pti
->iImage
;
2688 if (pti
->mask
& TCIF_PARAM
)
2689 memcpy(item
->extra
, &pti
->lParam
, infoPtr
->cbInfo
);
2691 memset(item
->extra
, 0, infoPtr
->cbInfo
);
2693 TAB_SetItemBounds(infoPtr
);
2694 if (infoPtr
->uNumItem
> 1)
2695 TAB_InvalidateTabArea(infoPtr
);
2697 InvalidateRect(infoPtr
->hwnd
, NULL
, TRUE
);
2699 TRACE("[%p]: added item %d %s\n",
2700 infoPtr
->hwnd
, iItem
, debugstr_w(item
->pszText
));
2702 /* If we haven't set the current focus yet, set it now. */
2703 if (infoPtr
->uFocus
== -1)
2704 TAB_SetCurFocus(infoPtr
, iItem
);
2710 TAB_SetItemSize (TAB_INFO
*infoPtr
, INT cx
, INT cy
)
2713 BOOL bNeedPaint
= FALSE
;
2715 lResult
= MAKELONG(infoPtr
->tabWidth
, infoPtr
->tabHeight
);
2717 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2718 if (infoPtr
->dwStyle
& TCS_FIXEDWIDTH
&& (infoPtr
->tabWidth
!= cx
))
2720 infoPtr
->tabWidth
= cx
;
2724 if (infoPtr
->tabHeight
!= cy
)
2726 if ((infoPtr
->fHeightSet
= (cy
!= 0)))
2727 infoPtr
->tabHeight
= cy
;
2731 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2732 HIWORD(lResult
), LOWORD(lResult
),
2733 infoPtr
->tabHeight
, infoPtr
->tabWidth
);
2737 TAB_SetItemBounds(infoPtr
);
2738 RedrawWindow(infoPtr
->hwnd
, NULL
, NULL
, RDW_ERASE
| RDW_INVALIDATE
| RDW_UPDATENOW
);
2744 static inline LRESULT
TAB_SetMinTabWidth (TAB_INFO
*infoPtr
, INT cx
)
2748 TRACE("(%p,%d)\n", infoPtr
, cx
);
2750 if (infoPtr
->tabMinWidth
< 0)
2751 oldcx
= DEFAULT_MIN_TAB_WIDTH
;
2753 oldcx
= infoPtr
->tabMinWidth
;
2754 infoPtr
->tabMinWidth
= cx
;
2755 TAB_SetItemBounds(infoPtr
);
2759 static inline LRESULT
2760 TAB_HighlightItem (TAB_INFO
*infoPtr
, INT iItem
, BOOL fHighlight
)
2766 TRACE("(%p,%d,%s)\n", infoPtr
, iItem
, fHighlight
? "true" : "false");
2768 if (iItem
< 0 || iItem
>= infoPtr
->uNumItem
)
2771 lpState
= &TAB_GetItem(infoPtr
, iItem
)->dwState
;
2772 oldState
= *lpState
;
2775 *lpState
|= TCIS_HIGHLIGHTED
;
2777 *lpState
&= ~TCIS_HIGHLIGHTED
;
2779 if ((oldState
!= *lpState
) && TAB_InternalGetItemRect (infoPtr
, iItem
, &r
, NULL
))
2780 InvalidateRect (infoPtr
->hwnd
, &r
, TRUE
);
2786 TAB_SetItemT (TAB_INFO
*infoPtr
, INT iItem
, LPTCITEMW tabItem
, BOOL bUnicode
)
2790 TRACE("(%p,%d,%p,%s)\n", infoPtr
, iItem
, tabItem
, bUnicode
? "true" : "false");
2792 if (iItem
< 0 || iItem
>= infoPtr
->uNumItem
)
2795 TAB_DumpItemExternalT(tabItem
, iItem
, bUnicode
);
2797 wineItem
= TAB_GetItem(infoPtr
, iItem
);
2799 if (tabItem
->mask
& TCIF_IMAGE
)
2800 wineItem
->iImage
= tabItem
->iImage
;
2802 if (tabItem
->mask
& TCIF_PARAM
)
2803 memcpy(wineItem
->extra
, &tabItem
->lParam
, infoPtr
->cbInfo
);
2805 if (tabItem
->mask
& TCIF_RTLREADING
)
2806 FIXME("TCIF_RTLREADING\n");
2808 if (tabItem
->mask
& TCIF_STATE
)
2809 wineItem
->dwState
= (wineItem
->dwState
& ~tabItem
->dwStateMask
) |
2810 ( tabItem
->dwState
& tabItem
->dwStateMask
);
2812 if (tabItem
->mask
& TCIF_TEXT
)
2814 Free(wineItem
->pszText
);
2815 wineItem
->pszText
= NULL
;
2817 Str_SetPtrW(&wineItem
->pszText
, tabItem
->pszText
);
2819 Str_SetPtrAtoW(&wineItem
->pszText
, (LPSTR
)tabItem
->pszText
);
2822 /* Update and repaint tabs */
2823 TAB_SetItemBounds(infoPtr
);
2824 TAB_InvalidateTabArea(infoPtr
);
2829 static inline LRESULT
TAB_GetItemCount (const TAB_INFO
*infoPtr
)
2832 return infoPtr
->uNumItem
;
2837 TAB_GetItemT (TAB_INFO
*infoPtr
, INT iItem
, LPTCITEMW tabItem
, BOOL bUnicode
)
2841 TRACE("(%p,%d,%p,%s)\n", infoPtr
, iItem
, tabItem
, bUnicode
? "true" : "false");
2843 if (!tabItem
) return FALSE
;
2845 if (iItem
< 0 || iItem
>= infoPtr
->uNumItem
)
2847 /* init requested fields */
2848 if (tabItem
->mask
& TCIF_IMAGE
) tabItem
->iImage
= 0;
2849 if (tabItem
->mask
& TCIF_PARAM
) tabItem
->lParam
= 0;
2850 if (tabItem
->mask
& TCIF_STATE
) tabItem
->dwState
= 0;
2854 wineItem
= TAB_GetItem(infoPtr
, iItem
);
2856 if (tabItem
->mask
& TCIF_IMAGE
)
2857 tabItem
->iImage
= wineItem
->iImage
;
2859 if (tabItem
->mask
& TCIF_PARAM
)
2860 memcpy(&tabItem
->lParam
, wineItem
->extra
, infoPtr
->cbInfo
);
2862 if (tabItem
->mask
& TCIF_RTLREADING
)
2863 FIXME("TCIF_RTLREADING\n");
2865 if (tabItem
->mask
& TCIF_STATE
)
2866 tabItem
->dwState
= wineItem
->dwState
& tabItem
->dwStateMask
;
2868 if (tabItem
->mask
& TCIF_TEXT
)
2871 Str_GetPtrW (wineItem
->pszText
, tabItem
->pszText
, tabItem
->cchTextMax
);
2873 Str_GetPtrWtoA (wineItem
->pszText
, (LPSTR
)tabItem
->pszText
, tabItem
->cchTextMax
);
2876 TAB_DumpItemExternalT(tabItem
, iItem
, bUnicode
);
2882 static LRESULT
TAB_DeleteItem (TAB_INFO
*infoPtr
, INT iItem
)
2884 BOOL bResult
= FALSE
;
2886 TRACE("(%p, %d)\n", infoPtr
, iItem
);
2888 if ((iItem
>= 0) && (iItem
< infoPtr
->uNumItem
))
2890 TAB_ITEM
*item
= TAB_GetItem(infoPtr
, iItem
);
2891 LPBYTE oldItems
= (LPBYTE
)infoPtr
->items
;
2893 TAB_InvalidateTabArea(infoPtr
);
2894 Free(item
->pszText
);
2895 infoPtr
->uNumItem
--;
2897 if (!infoPtr
->uNumItem
)
2899 infoPtr
->items
= NULL
;
2900 if (infoPtr
->iHotTracked
>= 0)
2902 KillTimer(infoPtr
->hwnd
, TAB_HOTTRACK_TIMER
);
2903 infoPtr
->iHotTracked
= -1;
2908 infoPtr
->items
= Alloc(TAB_ITEM_SIZE(infoPtr
) * infoPtr
->uNumItem
);
2911 memcpy(infoPtr
->items
, oldItems
, iItem
* TAB_ITEM_SIZE(infoPtr
));
2913 if (iItem
< infoPtr
->uNumItem
)
2914 memcpy(TAB_GetItem(infoPtr
, iItem
),
2915 oldItems
+ (iItem
+ 1) * TAB_ITEM_SIZE(infoPtr
),
2916 (infoPtr
->uNumItem
- iItem
) * TAB_ITEM_SIZE(infoPtr
));
2918 if (iItem
<= infoPtr
->iHotTracked
)
2920 /* When tabs move left/up, the hot track item may change */
2921 FIXME("Recalc hot track\n");
2926 /* Readjust the selected index */
2927 if (iItem
== infoPtr
->iSelected
)
2928 infoPtr
->iSelected
= -1;
2929 else if (iItem
< infoPtr
->iSelected
)
2930 infoPtr
->iSelected
--;
2932 if (infoPtr
->uNumItem
== 0)
2933 infoPtr
->iSelected
= -1;
2935 /* Reposition and repaint tabs */
2936 TAB_SetItemBounds(infoPtr
);
2944 static inline LRESULT
TAB_DeleteAllItems (TAB_INFO
*infoPtr
)
2946 TRACE("(%p)\n", infoPtr
);
2947 while (infoPtr
->uNumItem
)
2948 TAB_DeleteItem (infoPtr
, 0);
2953 static inline LRESULT
TAB_GetFont (const TAB_INFO
*infoPtr
)
2955 TRACE("(%p) returning %p\n", infoPtr
, infoPtr
->hFont
);
2956 return (LRESULT
)infoPtr
->hFont
;
2959 static inline LRESULT
TAB_SetFont (TAB_INFO
*infoPtr
, HFONT hNewFont
)
2961 TRACE("(%p,%p)\n", infoPtr
, hNewFont
);
2963 infoPtr
->hFont
= hNewFont
;
2965 TAB_SetItemBounds(infoPtr
);
2967 TAB_InvalidateTabArea(infoPtr
);
2973 static inline LRESULT
TAB_GetImageList (const TAB_INFO
*infoPtr
)
2976 return (LRESULT
)infoPtr
->himl
;
2979 static inline LRESULT
TAB_SetImageList (TAB_INFO
*infoPtr
, HIMAGELIST himlNew
)
2981 HIMAGELIST himlPrev
= infoPtr
->himl
;
2982 TRACE("himl=%p\n", himlNew
);
2983 infoPtr
->himl
= himlNew
;
2984 TAB_SetItemBounds(infoPtr
);
2985 InvalidateRect(infoPtr
->hwnd
, NULL
, TRUE
);
2986 return (LRESULT
)himlPrev
;
2989 static inline LRESULT
TAB_GetUnicodeFormat (const TAB_INFO
*infoPtr
)
2991 TRACE("(%p)\n", infoPtr
);
2992 return infoPtr
->bUnicode
;
2995 static inline LRESULT
TAB_SetUnicodeFormat (TAB_INFO
*infoPtr
, BOOL bUnicode
)
2997 BOOL bTemp
= infoPtr
->bUnicode
;
2999 TRACE("(%p %d)\n", infoPtr
, bUnicode
);
3000 infoPtr
->bUnicode
= bUnicode
;
3005 static inline LRESULT
TAB_Size (TAB_INFO
*infoPtr
)
3007 /* I'm not really sure what the following code was meant to do.
3008 This is what it is doing:
3009 When WM_SIZE is sent with SIZE_RESTORED, the control
3010 gets positioned in the top left corner.
3014 UINT uPosFlags,cx,cy;
3018 parent = GetParent (hwnd);
3019 GetClientRect(parent, &parent_rect);
3022 if (GetWindowLongW(hwnd, GWL_STYLE) & CCS_NORESIZE)
3023 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
3025 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
3026 cx, cy, uPosFlags | SWP_NOZORDER);
3028 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
3031 /* Recompute the size/position of the tabs. */
3032 TAB_SetItemBounds (infoPtr
);
3034 /* Force a repaint of the control. */
3035 InvalidateRect(infoPtr
->hwnd
, NULL
, TRUE
);
3041 static LRESULT
TAB_Create (HWND hwnd
, LPARAM lParam
)
3044 TEXTMETRICW fontMetrics
;
3049 infoPtr
= Alloc (sizeof(TAB_INFO
));
3051 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
3053 infoPtr
->hwnd
= hwnd
;
3054 infoPtr
->hwndNotify
= ((LPCREATESTRUCTW
)lParam
)->hwndParent
;
3055 infoPtr
->uNumItem
= 0;
3056 infoPtr
->uNumRows
= 0;
3057 infoPtr
->uHItemPadding
= 6;
3058 infoPtr
->uVItemPadding
= 3;
3059 infoPtr
->uHItemPadding_s
= 6;
3060 infoPtr
->uVItemPadding_s
= 3;
3063 infoPtr
->hcurArrow
= LoadCursorW (0, (LPWSTR
)IDC_ARROW
);
3064 infoPtr
->iSelected
= -1;
3065 infoPtr
->iHotTracked
= -1;
3066 infoPtr
->uFocus
= -1;
3067 infoPtr
->hwndToolTip
= 0;
3068 infoPtr
->DoRedraw
= TRUE
;
3069 infoPtr
->needsScrolling
= FALSE
;
3070 infoPtr
->hwndUpDown
= 0;
3071 infoPtr
->leftmostVisible
= 0;
3072 infoPtr
->fHeightSet
= FALSE
;
3073 infoPtr
->bUnicode
= IsWindowUnicode (hwnd
);
3074 infoPtr
->cbInfo
= sizeof(LPARAM
);
3076 TRACE("Created tab control, hwnd [%p]\n", hwnd
);
3078 /* The tab control always has the WS_CLIPSIBLINGS style. Even
3079 if you don't specify it in CreateWindow. This is necessary in
3080 order for paint to work correctly. This follows windows behaviour. */
3081 dwStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
3082 SetWindowLongW(hwnd
, GWL_STYLE
, dwStyle
|WS_CLIPSIBLINGS
);
3084 infoPtr
->dwStyle
= dwStyle
| WS_CLIPSIBLINGS
;
3085 infoPtr
->exStyle
= (dwStyle
& TCS_FLATBUTTONS
) ? TCS_EX_FLATSEPARATORS
: 0;
3087 if (infoPtr
->dwStyle
& TCS_TOOLTIPS
) {
3088 /* Create tooltip control */
3089 infoPtr
->hwndToolTip
=
3090 CreateWindowExW (0, TOOLTIPS_CLASSW
, NULL
, WS_POPUP
,
3091 CW_USEDEFAULT
, CW_USEDEFAULT
,
3092 CW_USEDEFAULT
, CW_USEDEFAULT
,
3095 /* Send NM_TOOLTIPSCREATED notification */
3096 if (infoPtr
->hwndToolTip
) {
3097 NMTOOLTIPSCREATED nmttc
;
3099 nmttc
.hdr
.hwndFrom
= hwnd
;
3100 nmttc
.hdr
.idFrom
= GetWindowLongPtrW(hwnd
, GWLP_ID
);
3101 nmttc
.hdr
.code
= NM_TOOLTIPSCREATED
;
3102 nmttc
.hwndToolTips
= infoPtr
->hwndToolTip
;
3104 SendMessageW (infoPtr
->hwndNotify
, WM_NOTIFY
,
3105 GetWindowLongPtrW(hwnd
, GWLP_ID
), (LPARAM
)&nmttc
);
3109 OpenThemeData (infoPtr
->hwnd
, themeClass
);
3112 * We need to get text information so we need a DC and we need to select
3116 hOldFont
= SelectObject (hdc
, GetStockObject (SYSTEM_FONT
));
3118 /* Use the system font to determine the initial height of a tab. */
3119 GetTextMetricsW(hdc
, &fontMetrics
);
3122 * Make sure there is enough space for the letters + growing the
3123 * selected item + extra space for the selected item.
3125 infoPtr
->tabHeight
= fontMetrics
.tmHeight
+ SELECTED_TAB_OFFSET
+
3126 ((infoPtr
->dwStyle
& TCS_BUTTONS
) ? 2 : 1) *
3127 infoPtr
->uVItemPadding
;
3129 /* Initialize the width of a tab. */
3130 if (infoPtr
->dwStyle
& TCS_FIXEDWIDTH
)
3131 infoPtr
->tabWidth
= GetDeviceCaps(hdc
, LOGPIXELSX
);
3133 infoPtr
->tabMinWidth
= -1;
3135 TRACE("tabH=%d, tabW=%d\n", infoPtr
->tabHeight
, infoPtr
->tabWidth
);
3137 SelectObject (hdc
, hOldFont
);
3138 ReleaseDC(hwnd
, hdc
);
3144 TAB_Destroy (TAB_INFO
*infoPtr
)
3148 SetWindowLongPtrW(infoPtr
->hwnd
, 0, 0);
3150 if (infoPtr
->items
) {
3151 for (iItem
= 0; iItem
< infoPtr
->uNumItem
; iItem
++) {
3152 Free (TAB_GetItem(infoPtr
, iItem
)->pszText
);
3154 Free (infoPtr
->items
);
3157 if (infoPtr
->hwndToolTip
)
3158 DestroyWindow (infoPtr
->hwndToolTip
);
3160 if (infoPtr
->hwndUpDown
)
3161 DestroyWindow(infoPtr
->hwndUpDown
);
3163 if (infoPtr
->iHotTracked
>= 0)
3164 KillTimer(infoPtr
->hwnd
, TAB_HOTTRACK_TIMER
);
3166 CloseThemeData (GetWindowTheme (infoPtr
->hwnd
));
3172 /* update theme after a WM_THEMECHANGED message */
3173 static LRESULT
theme_changed(const TAB_INFO
*infoPtr
)
3175 HTHEME theme
= GetWindowTheme (infoPtr
->hwnd
);
3176 CloseThemeData (theme
);
3177 OpenThemeData (infoPtr
->hwnd
, themeClass
);
3181 static LRESULT
TAB_NCCalcSize(WPARAM wParam
)
3185 return WVR_ALIGNTOP
;
3188 static inline LRESULT
3189 TAB_SetItemExtra (TAB_INFO
*infoPtr
, INT cbInfo
)
3191 TRACE("(%p %d)\n", infoPtr
, cbInfo
);
3196 if (infoPtr
->uNumItem
)
3198 /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
3202 infoPtr
->cbInfo
= cbInfo
;
3206 static LRESULT
TAB_RemoveImage (TAB_INFO
*infoPtr
, INT image
)
3208 TRACE("%p %d\n", infoPtr
, image
);
3210 if (ImageList_Remove (infoPtr
->himl
, image
))
3215 /* shift indices, repaint items if needed */
3216 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
3218 idx
= &TAB_GetItem(infoPtr
, i
)->iImage
;
3227 if (TAB_InternalGetItemRect (infoPtr
, i
, &r
, NULL
))
3228 InvalidateRect (infoPtr
->hwnd
, &r
, TRUE
);
3237 TAB_SetExtendedStyle (TAB_INFO
*infoPtr
, DWORD exMask
, DWORD exStyle
)
3239 DWORD prevstyle
= infoPtr
->exStyle
;
3241 /* zero mask means all styles */
3242 if (exMask
== 0) exMask
= ~0;
3244 if (exMask
& TCS_EX_REGISTERDROP
)
3246 FIXME("TCS_EX_REGISTERDROP style unimplemented\n");
3247 exMask
&= ~TCS_EX_REGISTERDROP
;
3248 exStyle
&= ~TCS_EX_REGISTERDROP
;
3251 if (exMask
& TCS_EX_FLATSEPARATORS
)
3253 if ((prevstyle
^ exStyle
) & TCS_EX_FLATSEPARATORS
)
3255 infoPtr
->exStyle
^= TCS_EX_FLATSEPARATORS
;
3256 TAB_InvalidateTabArea(infoPtr
);
3263 static inline LRESULT
3264 TAB_GetExtendedStyle (const TAB_INFO
*infoPtr
)
3266 return infoPtr
->exStyle
;
3270 TAB_DeselectAll (TAB_INFO
*infoPtr
, BOOL excludesel
)
3273 INT i
, selected
= infoPtr
->iSelected
;
3275 TRACE("(%p, %d)\n", infoPtr
, excludesel
);
3277 if (!(infoPtr
->dwStyle
& TCS_BUTTONS
))
3280 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
3282 if ((TAB_GetItem(infoPtr
, i
)->dwState
& TCIS_BUTTONPRESSED
) &&
3285 TAB_GetItem(infoPtr
, i
)->dwState
&= ~TCIS_BUTTONPRESSED
;
3290 if (!excludesel
&& (selected
!= -1))
3292 TAB_GetItem(infoPtr
, selected
)->dwState
&= ~TCIS_BUTTONPRESSED
;
3293 infoPtr
->iSelected
= -1;
3298 TAB_InvalidateTabArea (infoPtr
);
3305 * Processes WM_STYLECHANGED messages.
3308 * [I] infoPtr : valid pointer to the tab data structure
3309 * [I] wStyleType : window style type (normal or extended)
3310 * [I] lpss : window style information
3315 static INT
TAB_StyleChanged(TAB_INFO
*infoPtr
, WPARAM wStyleType
,
3316 const STYLESTRUCT
*lpss
)
3318 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
3319 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
3321 if (wStyleType
!= GWL_STYLE
) return 0;
3323 infoPtr
->dwStyle
= lpss
->styleNew
;
3325 TAB_SetItemBounds (infoPtr
);
3326 InvalidateRect(infoPtr
->hwnd
, NULL
, TRUE
);
3331 static LRESULT WINAPI
3332 TAB_WindowProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
3334 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
3336 TRACE("hwnd=%p msg=%x wParam=%lx lParam=%lx\n", hwnd
, uMsg
, wParam
, lParam
);
3337 if (!infoPtr
&& (uMsg
!= WM_CREATE
))
3338 return DefWindowProcW (hwnd
, uMsg
, wParam
, lParam
);
3342 case TCM_GETIMAGELIST
:
3343 return TAB_GetImageList (infoPtr
);
3345 case TCM_SETIMAGELIST
:
3346 return TAB_SetImageList (infoPtr
, (HIMAGELIST
)lParam
);
3348 case TCM_GETITEMCOUNT
:
3349 return TAB_GetItemCount (infoPtr
);
3353 return TAB_GetItemT (infoPtr
, (INT
)wParam
, (LPTCITEMW
)lParam
, uMsg
== TCM_GETITEMW
);
3357 return TAB_SetItemT (infoPtr
, (INT
)wParam
, (LPTCITEMW
)lParam
, uMsg
== TCM_SETITEMW
);
3359 case TCM_DELETEITEM
:
3360 return TAB_DeleteItem (infoPtr
, (INT
)wParam
);
3362 case TCM_DELETEALLITEMS
:
3363 return TAB_DeleteAllItems (infoPtr
);
3365 case TCM_GETITEMRECT
:
3366 return TAB_GetItemRect (infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
3369 return TAB_GetCurSel (infoPtr
);
3372 return TAB_HitTest (infoPtr
, (LPTCHITTESTINFO
)lParam
);
3375 return TAB_SetCurSel (infoPtr
, (INT
)wParam
);
3377 case TCM_INSERTITEMA
:
3378 case TCM_INSERTITEMW
:
3379 return TAB_InsertItemT (infoPtr
, (INT
)wParam
, (TCITEMW
*)lParam
, uMsg
== TCM_INSERTITEMW
);
3381 case TCM_SETITEMEXTRA
:
3382 return TAB_SetItemExtra (infoPtr
, (INT
)wParam
);
3384 case TCM_ADJUSTRECT
:
3385 return TAB_AdjustRect (infoPtr
, (BOOL
)wParam
, (LPRECT
)lParam
);
3387 case TCM_SETITEMSIZE
:
3388 return TAB_SetItemSize (infoPtr
, (INT
)LOWORD(lParam
), (INT
)HIWORD(lParam
));
3390 case TCM_REMOVEIMAGE
:
3391 return TAB_RemoveImage (infoPtr
, (INT
)wParam
);
3393 case TCM_SETPADDING
:
3394 return TAB_SetPadding (infoPtr
, lParam
);
3396 case TCM_GETROWCOUNT
:
3397 return TAB_GetRowCount(infoPtr
);
3399 case TCM_GETUNICODEFORMAT
:
3400 return TAB_GetUnicodeFormat (infoPtr
);
3402 case TCM_SETUNICODEFORMAT
:
3403 return TAB_SetUnicodeFormat (infoPtr
, (BOOL
)wParam
);
3405 case TCM_HIGHLIGHTITEM
:
3406 return TAB_HighlightItem (infoPtr
, (INT
)wParam
, (BOOL
)LOWORD(lParam
));
3408 case TCM_GETTOOLTIPS
:
3409 return TAB_GetToolTips (infoPtr
);
3411 case TCM_SETTOOLTIPS
:
3412 return TAB_SetToolTips (infoPtr
, (HWND
)wParam
);
3414 case TCM_GETCURFOCUS
:
3415 return TAB_GetCurFocus (infoPtr
);
3417 case TCM_SETCURFOCUS
:
3418 return TAB_SetCurFocus (infoPtr
, (INT
)wParam
);
3420 case TCM_SETMINTABWIDTH
:
3421 return TAB_SetMinTabWidth(infoPtr
, (INT
)lParam
);
3423 case TCM_DESELECTALL
:
3424 return TAB_DeselectAll (infoPtr
, (BOOL
)wParam
);
3426 case TCM_GETEXTENDEDSTYLE
:
3427 return TAB_GetExtendedStyle (infoPtr
);
3429 case TCM_SETEXTENDEDSTYLE
:
3430 return TAB_SetExtendedStyle (infoPtr
, wParam
, lParam
);
3433 return TAB_GetFont (infoPtr
);
3436 return TAB_SetFont (infoPtr
, (HFONT
)wParam
);
3439 return TAB_Create (hwnd
, lParam
);
3442 return TAB_Destroy (infoPtr
);
3445 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
3447 case WM_LBUTTONDOWN
:
3448 return TAB_LButtonDown (infoPtr
, wParam
, lParam
);
3451 return TAB_LButtonUp (infoPtr
);
3454 return SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, wParam
, lParam
);
3456 case WM_RBUTTONDOWN
:
3457 return TAB_RButtonDown (infoPtr
);
3460 return TAB_MouseMove (infoPtr
, wParam
, lParam
);
3462 case WM_PRINTCLIENT
:
3464 return TAB_Paint (infoPtr
, (HDC
)wParam
);
3467 return TAB_Size (infoPtr
);
3470 return TAB_SetRedraw (infoPtr
, (BOOL
)wParam
);
3473 return TAB_OnHScroll(infoPtr
, (int)LOWORD(wParam
), (int)HIWORD(wParam
));
3475 case WM_STYLECHANGED
:
3476 return TAB_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
3478 case WM_SYSCOLORCHANGE
:
3479 COMCTL32_RefreshSysColors();
3482 case WM_THEMECHANGED
:
3483 return theme_changed (infoPtr
);
3486 TAB_KillFocus(infoPtr
);
3488 TAB_FocusChanging(infoPtr
);
3489 break; /* Don't disturb normal focus behavior */
3492 return TAB_KeyDown(infoPtr
, wParam
, lParam
);
3495 return TAB_NCHitTest(infoPtr
, lParam
);
3498 return TAB_NCCalcSize(wParam
);
3501 if (uMsg
>= WM_USER
&& uMsg
< WM_APP
&& !COMCTL32_IsReflectedMessage(uMsg
))
3502 WARN("unknown msg %04x wp=%08lx lp=%08lx\n",
3503 uMsg
, wParam
, lParam
);
3506 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
3515 ZeroMemory (&wndClass
, sizeof(WNDCLASSW
));
3516 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
;
3517 wndClass
.lpfnWndProc
= TAB_WindowProc
;
3518 wndClass
.cbClsExtra
= 0;
3519 wndClass
.cbWndExtra
= sizeof(TAB_INFO
*);
3520 wndClass
.hCursor
= LoadCursorW (0, (LPWSTR
)IDC_ARROW
);
3521 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_BTNFACE
+1);
3522 wndClass
.lpszClassName
= WC_TABCONTROLW
;
3524 RegisterClassW (&wndClass
);
3529 TAB_Unregister (void)
3531 UnregisterClassW (WC_TABCONTROLW
, NULL
);