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 TCM_INSERTITEM always stores at least LPARAM sized data. */
88 #define EXTRA_ITEM_SIZE(infoPtr) (max((infoPtr)->cbInfo, sizeof(LPARAM)))
89 #define TAB_ITEM_SIZE(infoPtr) FIELD_OFFSET(TAB_ITEM, extra[EXTRA_ITEM_SIZE(infoPtr)])
93 HWND hwnd
; /* Tab control window */
94 HWND hwndNotify
; /* notification window (parent) */
95 UINT uNumItem
; /* number of tab items */
96 UINT uNumRows
; /* number of tab rows */
97 INT tabHeight
; /* height of the tab row */
98 INT tabWidth
; /* width of tabs */
99 INT tabMinWidth
; /* minimum width of items */
100 USHORT uHItemPadding
; /* amount of horizontal padding, in pixels */
101 USHORT uVItemPadding
; /* amount of vertical padding, in pixels */
102 USHORT uHItemPadding_s
; /* Set amount of horizontal padding, in pixels */
103 USHORT uVItemPadding_s
; /* Set amount of vertical padding, in pixels */
104 HFONT hFont
; /* handle to the current font */
105 HCURSOR hcurArrow
; /* handle to the current cursor */
106 HIMAGELIST himl
; /* handle to an image list (may be 0) */
107 HWND hwndToolTip
; /* handle to tab's tooltip */
108 INT leftmostVisible
; /* Used for scrolling, this member contains
109 * the index of the first visible item */
110 INT iSelected
; /* the currently selected item */
111 INT iHotTracked
; /* the highlighted item under the mouse */
112 INT uFocus
; /* item which has the focus */
113 TAB_ITEM
* items
; /* pointer to an array of TAB_ITEM's */
114 BOOL DoRedraw
; /* flag for redrawing when tab contents is changed*/
115 BOOL needsScrolling
; /* TRUE if the size of the tabs is greater than
116 * the size of the control */
117 BOOL fHeightSet
; /* was the height of the tabs explicitly set? */
118 BOOL bUnicode
; /* Unicode control? */
119 HWND hwndUpDown
; /* Updown control used for scrolling */
120 INT cbInfo
; /* Number of bytes of caller supplied info per tab */
122 DWORD exStyle
; /* Extended style used, currently:
123 TCS_EX_FLATSEPARATORS, TCS_EX_REGISTERDROP */
124 DWORD dwStyle
; /* the cached window GWL_STYLE */
127 /******************************************************************************
128 * Positioning constants
130 #define SELECTED_TAB_OFFSET 2
131 #define ROUND_CORNER_SIZE 2
132 #define DISPLAY_AREA_PADDINGX 2
133 #define DISPLAY_AREA_PADDINGY 2
134 #define CONTROL_BORDER_SIZEX 2
135 #define CONTROL_BORDER_SIZEY 2
136 #define BUTTON_SPACINGX 3
137 #define BUTTON_SPACINGY 3
138 #define FLAT_BTN_SPACINGX 8
139 #define DEFAULT_MIN_TAB_WIDTH 54
140 #define DEFAULT_PADDING_X 6
141 #define EXTRA_ICON_PADDING 3
143 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongPtrW(hwnd,0))
144 /* Since items are variable sized, cannot directly access them */
145 #define TAB_GetItem(info,i) \
146 ((TAB_ITEM*)((LPBYTE)info->items + (i) * TAB_ITEM_SIZE(info)))
148 #define GET_DEFAULT_MIN_TAB_WIDTH(infoPtr) (DEFAULT_MIN_TAB_WIDTH - (DEFAULT_PADDING_X - (infoPtr)->uHItemPadding) * 2)
150 /******************************************************************************
151 * Hot-tracking timer constants
153 #define TAB_HOTTRACK_TIMER 1
154 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
156 static const WCHAR themeClass
[] = { 'T','a','b',0 };
158 /******************************************************************************
161 static void TAB_InvalidateTabArea(const TAB_INFO
*);
162 static void TAB_EnsureSelectionVisible(TAB_INFO
*);
163 static void TAB_DrawItemInterior(const TAB_INFO
*, HDC
, INT
, RECT
*);
164 static LRESULT
TAB_DeselectAll(TAB_INFO
*, BOOL
);
165 static BOOL
TAB_InternalGetItemRect(const TAB_INFO
*, INT
, RECT
*, RECT
*);
168 TAB_SendSimpleNotify (const TAB_INFO
*infoPtr
, UINT code
)
172 nmhdr
.hwndFrom
= infoPtr
->hwnd
;
173 nmhdr
.idFrom
= GetWindowLongPtrW(infoPtr
->hwnd
, GWLP_ID
);
176 return (BOOL
) SendMessageW (infoPtr
->hwndNotify
, WM_NOTIFY
,
177 nmhdr
.idFrom
, (LPARAM
) &nmhdr
);
181 TAB_RelayEvent (HWND hwndTip
, HWND hwndMsg
, UINT uMsg
,
182 WPARAM wParam
, LPARAM lParam
)
190 msg
.time
= GetMessageTime ();
191 msg
.pt
.x
= (short)LOWORD(GetMessagePos ());
192 msg
.pt
.y
= (short)HIWORD(GetMessagePos ());
194 SendMessageW (hwndTip
, TTM_RELAYEVENT
, 0, (LPARAM
)&msg
);
198 TAB_DumpItemExternalT(const TCITEMW
*pti
, UINT iItem
, BOOL isW
)
201 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
202 iItem
, pti
->mask
, pti
->dwState
, pti
->dwStateMask
, pti
->cchTextMax
);
203 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
204 iItem
, pti
->iImage
, pti
->lParam
, isW
? debugstr_w(pti
->pszText
) : debugstr_a((LPSTR
)pti
->pszText
));
209 TAB_DumpItemInternal(const TAB_INFO
*infoPtr
, UINT iItem
)
214 ti
= TAB_GetItem(infoPtr
, iItem
);
215 TRACE("tab %d, dwState=0x%08x, pszText=%s, iImage=%d\n",
216 iItem
, ti
->dwState
, debugstr_w(ti
->pszText
), ti
->iImage
);
217 TRACE("tab %d, rect.left=%d, rect.top(row)=%d\n",
218 iItem
, ti
->rect
.left
, ti
->rect
.top
);
223 * the index of the selected tab, or -1 if no tab is selected. */
224 static inline LRESULT
TAB_GetCurSel (const TAB_INFO
*infoPtr
)
226 TRACE("(%p)\n", infoPtr
);
227 return infoPtr
->iSelected
;
231 * the index of the tab item that has the focus. */
232 static inline LRESULT
233 TAB_GetCurFocus (const TAB_INFO
*infoPtr
)
235 TRACE("(%p)\n", infoPtr
);
236 return infoPtr
->uFocus
;
239 static inline LRESULT
TAB_GetToolTips (const TAB_INFO
*infoPtr
)
241 TRACE("(%p)\n", infoPtr
);
242 return (LRESULT
)infoPtr
->hwndToolTip
;
245 static inline LRESULT
TAB_SetCurSel (TAB_INFO
*infoPtr
, INT iItem
)
247 INT prevItem
= infoPtr
->iSelected
;
249 TRACE("(%p %d)\n", infoPtr
, iItem
);
252 infoPtr
->iSelected
= -1;
253 else if (iItem
>= infoPtr
->uNumItem
)
256 if (prevItem
!= iItem
) {
258 TAB_GetItem(infoPtr
, prevItem
)->dwState
&= ~TCIS_BUTTONPRESSED
;
259 TAB_GetItem(infoPtr
, iItem
)->dwState
|= TCIS_BUTTONPRESSED
;
261 infoPtr
->iSelected
= iItem
;
262 infoPtr
->uFocus
= iItem
;
263 TAB_EnsureSelectionVisible(infoPtr
);
264 TAB_InvalidateTabArea(infoPtr
);
270 static LRESULT
TAB_SetCurFocus (TAB_INFO
*infoPtr
, INT iItem
)
272 TRACE("(%p %d)\n", infoPtr
, iItem
);
275 infoPtr
->uFocus
= -1;
276 if (infoPtr
->iSelected
!= -1) {
277 infoPtr
->iSelected
= -1;
278 TAB_SendSimpleNotify(infoPtr
, TCN_SELCHANGE
);
279 TAB_InvalidateTabArea(infoPtr
);
282 else if (iItem
< infoPtr
->uNumItem
) {
283 if (infoPtr
->dwStyle
& TCS_BUTTONS
) {
284 /* set focus to new item, leave selection as is */
285 if (infoPtr
->uFocus
!= iItem
) {
286 INT prev_focus
= infoPtr
->uFocus
;
289 infoPtr
->uFocus
= iItem
;
291 if (prev_focus
!= infoPtr
->iSelected
) {
292 if (TAB_InternalGetItemRect(infoPtr
, prev_focus
, &r
, NULL
))
293 InvalidateRect(infoPtr
->hwnd
, &r
, FALSE
);
296 if (TAB_InternalGetItemRect(infoPtr
, iItem
, &r
, NULL
))
297 InvalidateRect(infoPtr
->hwnd
, &r
, FALSE
);
299 TAB_SendSimpleNotify(infoPtr
, TCN_FOCUSCHANGE
);
302 INT oldFocus
= infoPtr
->uFocus
;
303 if (infoPtr
->iSelected
!= iItem
|| oldFocus
== -1 ) {
304 infoPtr
->uFocus
= iItem
;
305 if (oldFocus
!= -1) {
306 if (!TAB_SendSimpleNotify(infoPtr
, TCN_SELCHANGING
)) {
307 infoPtr
->iSelected
= iItem
;
308 TAB_SendSimpleNotify(infoPtr
, TCN_SELCHANGE
);
311 infoPtr
->iSelected
= iItem
;
312 TAB_EnsureSelectionVisible(infoPtr
);
313 TAB_InvalidateTabArea(infoPtr
);
321 static inline LRESULT
322 TAB_SetToolTips (TAB_INFO
*infoPtr
, HWND hwndToolTip
)
324 TRACE("%p %p\n", infoPtr
, hwndToolTip
);
325 infoPtr
->hwndToolTip
= hwndToolTip
;
329 static inline LRESULT
330 TAB_SetPadding (TAB_INFO
*infoPtr
, LPARAM lParam
)
332 TRACE("(%p %d %d)\n", infoPtr
, LOWORD(lParam
), HIWORD(lParam
));
333 infoPtr
->uHItemPadding_s
= LOWORD(lParam
);
334 infoPtr
->uVItemPadding_s
= HIWORD(lParam
);
339 /******************************************************************************
340 * TAB_InternalGetItemRect
342 * This method will calculate the rectangle representing a given tab item in
343 * client coordinates. This method takes scrolling into account.
345 * This method returns TRUE if the item is visible in the window and FALSE
346 * if it is completely outside the client area.
348 static BOOL
TAB_InternalGetItemRect(
349 const TAB_INFO
* infoPtr
,
354 RECT tmpItemRect
,clientRect
;
356 /* Perform a sanity check and a trivial visibility check. */
357 if ( (infoPtr
->uNumItem
<= 0) ||
358 (itemIndex
>= infoPtr
->uNumItem
) ||
359 (!(((infoPtr
->dwStyle
& TCS_MULTILINE
) || (infoPtr
->dwStyle
& TCS_VERTICAL
))) &&
360 (itemIndex
< infoPtr
->leftmostVisible
)))
362 TRACE("Not Visible\n");
363 /* need to initialize these to empty rects */
366 memset(itemRect
,0,sizeof(RECT
));
367 itemRect
->bottom
= infoPtr
->tabHeight
;
370 memset(selectedRect
,0,sizeof(RECT
));
375 * Avoid special cases in this procedure by assigning the "out"
376 * parameters if the caller didn't supply them
378 if (itemRect
== NULL
)
379 itemRect
= &tmpItemRect
;
381 /* Retrieve the unmodified item rect. */
382 *itemRect
= TAB_GetItem(infoPtr
,itemIndex
)->rect
;
384 /* calculate the times bottom and top based on the row */
385 GetClientRect(infoPtr
->hwnd
, &clientRect
);
387 if ((infoPtr
->dwStyle
& TCS_BOTTOM
) && (infoPtr
->dwStyle
& TCS_VERTICAL
))
389 itemRect
->right
= clientRect
.right
- SELECTED_TAB_OFFSET
- itemRect
->left
* infoPtr
->tabHeight
-
390 ((infoPtr
->dwStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGX
: 0);
391 itemRect
->left
= itemRect
->right
- infoPtr
->tabHeight
;
393 else if (infoPtr
->dwStyle
& TCS_VERTICAL
)
395 itemRect
->left
= clientRect
.left
+ SELECTED_TAB_OFFSET
+ itemRect
->left
* infoPtr
->tabHeight
+
396 ((infoPtr
->dwStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGX
: 0);
397 itemRect
->right
= itemRect
->left
+ infoPtr
->tabHeight
;
399 else if (infoPtr
->dwStyle
& TCS_BOTTOM
)
401 itemRect
->bottom
= clientRect
.bottom
- itemRect
->top
* infoPtr
->tabHeight
-
402 ((infoPtr
->dwStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: SELECTED_TAB_OFFSET
);
403 itemRect
->top
= itemRect
->bottom
- infoPtr
->tabHeight
;
405 else /* not TCS_BOTTOM and not TCS_VERTICAL */
407 itemRect
->top
= clientRect
.top
+ itemRect
->top
* infoPtr
->tabHeight
+
408 ((infoPtr
->dwStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: SELECTED_TAB_OFFSET
);
409 itemRect
->bottom
= itemRect
->top
+ infoPtr
->tabHeight
;
413 * "scroll" it to make sure the item at the very left of the
414 * tab control is the leftmost visible tab.
416 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
420 -TAB_GetItem(infoPtr
, infoPtr
->leftmostVisible
)->rect
.top
);
423 * Move the rectangle so the first item is slightly offset from
424 * the bottom of the tab control.
428 SELECTED_TAB_OFFSET
);
433 -TAB_GetItem(infoPtr
, infoPtr
->leftmostVisible
)->rect
.left
,
437 * Move the rectangle so the first item is slightly offset from
438 * the left of the tab control.
444 TRACE("item %d tab h=%d, rect=(%s)\n",
445 itemIndex
, infoPtr
->tabHeight
, wine_dbgstr_rect(itemRect
));
447 /* Now, calculate the position of the item as if it were selected. */
448 if (selectedRect
!=NULL
)
450 CopyRect(selectedRect
, itemRect
);
452 /* The rectangle of a selected item is a bit wider. */
453 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
454 InflateRect(selectedRect
, 0, SELECTED_TAB_OFFSET
);
456 InflateRect(selectedRect
, SELECTED_TAB_OFFSET
, 0);
458 /* If it also a bit higher. */
459 if ((infoPtr
->dwStyle
& TCS_BOTTOM
) && (infoPtr
->dwStyle
& TCS_VERTICAL
))
461 selectedRect
->left
-= 2; /* the border is thicker on the right */
462 selectedRect
->right
+= SELECTED_TAB_OFFSET
;
464 else if (infoPtr
->dwStyle
& TCS_VERTICAL
)
466 selectedRect
->left
-= SELECTED_TAB_OFFSET
;
467 selectedRect
->right
+= 1;
469 else if (infoPtr
->dwStyle
& TCS_BOTTOM
)
471 selectedRect
->bottom
+= SELECTED_TAB_OFFSET
;
473 else /* not TCS_BOTTOM and not TCS_VERTICAL */
475 selectedRect
->top
-= SELECTED_TAB_OFFSET
;
476 selectedRect
->bottom
-= 1;
480 /* Check for visibility */
481 if (infoPtr
->dwStyle
& TCS_VERTICAL
)
482 return (itemRect
->top
< clientRect
.bottom
) && (itemRect
->bottom
> clientRect
.top
);
484 return (itemRect
->left
< clientRect
.right
) && (itemRect
->right
> clientRect
.left
);
488 TAB_GetItemRect(const TAB_INFO
*infoPtr
, INT item
, RECT
*rect
)
490 TRACE("(%p, %d, %p)\n", infoPtr
, item
, rect
);
491 return TAB_InternalGetItemRect(infoPtr
, item
, rect
, NULL
);
494 /******************************************************************************
497 * This method is called to handle keyboard input
499 static LRESULT
TAB_KeyDown(TAB_INFO
* infoPtr
, WPARAM keyCode
, LPARAM lParam
)
504 /* TCN_KEYDOWN notification sent always */
505 nm
.hdr
.hwndFrom
= infoPtr
->hwnd
;
506 nm
.hdr
.idFrom
= GetWindowLongPtrW(infoPtr
->hwnd
, GWLP_ID
);
507 nm
.hdr
.code
= TCN_KEYDOWN
;
510 SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, nm
.hdr
.idFrom
, (LPARAM
)&nm
);
515 newItem
= infoPtr
->uFocus
- 1;
518 newItem
= infoPtr
->uFocus
+ 1;
522 /* If we changed to a valid item, change focused item */
523 if (newItem
>= 0 && newItem
< infoPtr
->uNumItem
&& infoPtr
->uFocus
!= newItem
)
524 TAB_SetCurFocus(infoPtr
, newItem
);
530 * WM_KILLFOCUS handler
532 static void TAB_KillFocus(TAB_INFO
*infoPtr
)
534 /* clear current focused item back to selected for TCS_BUTTONS */
535 if ((infoPtr
->dwStyle
& TCS_BUTTONS
) && (infoPtr
->uFocus
!= infoPtr
->iSelected
))
539 if (TAB_InternalGetItemRect(infoPtr
, infoPtr
->uFocus
, &r
, NULL
))
540 InvalidateRect(infoPtr
->hwnd
, &r
, FALSE
);
542 infoPtr
->uFocus
= infoPtr
->iSelected
;
546 /******************************************************************************
549 * This method is called whenever the focus goes in or out of this control
550 * it is used to update the visual state of the control.
552 static void TAB_FocusChanging(const TAB_INFO
*infoPtr
)
558 * Get the rectangle for the item.
560 isVisible
= TAB_InternalGetItemRect(infoPtr
,
566 * If the rectangle is not completely invisible, invalidate that
567 * portion of the window.
571 TRACE("invalidate (%s)\n", wine_dbgstr_rect(&selectedRect
));
572 InvalidateRect(infoPtr
->hwnd
, &selectedRect
, TRUE
);
576 static INT
TAB_InternalHitTest (const TAB_INFO
*infoPtr
, POINT pt
, UINT
*flags
)
581 for (iCount
= 0; iCount
< infoPtr
->uNumItem
; iCount
++)
583 TAB_InternalGetItemRect(infoPtr
, iCount
, &rect
, NULL
);
585 if (PtInRect(&rect
, pt
))
587 *flags
= TCHT_ONITEM
;
592 *flags
= TCHT_NOWHERE
;
596 static inline LRESULT
597 TAB_HitTest (const TAB_INFO
*infoPtr
, LPTCHITTESTINFO lptest
)
599 TRACE("(%p, %p)\n", infoPtr
, lptest
);
600 return TAB_InternalHitTest (infoPtr
, lptest
->pt
, &lptest
->flags
);
603 /******************************************************************************
606 * Napster v2b5 has a tab control for its main navigation which has a client
607 * area that covers the whole area of the dialog pages.
608 * That's why it receives all msgs for that area and the underlying dialog ctrls
610 * So I decided that we should handle WM_NCHITTEST here and return
611 * HTTRANSPARENT if we don't hit the tab control buttons.
612 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
613 * doesn't do it that way. Maybe depends on tab control styles ?
615 static inline LRESULT
616 TAB_NCHitTest (const TAB_INFO
*infoPtr
, LPARAM lParam
)
621 pt
.x
= (short)LOWORD(lParam
);
622 pt
.y
= (short)HIWORD(lParam
);
623 ScreenToClient(infoPtr
->hwnd
, &pt
);
625 if (TAB_InternalHitTest(infoPtr
, pt
, &dummyflag
) == -1)
626 return HTTRANSPARENT
;
632 TAB_LButtonDown (TAB_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
638 if (infoPtr
->hwndToolTip
)
639 TAB_RelayEvent (infoPtr
->hwndToolTip
, infoPtr
->hwnd
,
640 WM_LBUTTONDOWN
, wParam
, lParam
);
642 if (!(infoPtr
->dwStyle
& TCS_FOCUSNEVER
)) {
643 SetFocus (infoPtr
->hwnd
);
646 if (infoPtr
->hwndToolTip
)
647 TAB_RelayEvent (infoPtr
->hwndToolTip
, infoPtr
->hwnd
,
648 WM_LBUTTONDOWN
, wParam
, lParam
);
650 pt
.x
= (short)LOWORD(lParam
);
651 pt
.y
= (short)HIWORD(lParam
);
653 newItem
= TAB_InternalHitTest (infoPtr
, pt
, &dummy
);
655 TRACE("On Tab, item %d\n", newItem
);
657 if ((newItem
!= -1) && (infoPtr
->iSelected
!= newItem
))
659 if ((infoPtr
->dwStyle
& TCS_BUTTONS
) && (infoPtr
->dwStyle
& TCS_MULTISELECT
) &&
660 (wParam
& MK_CONTROL
))
664 /* toggle multiselection */
665 TAB_GetItem(infoPtr
, newItem
)->dwState
^= TCIS_BUTTONPRESSED
;
666 if (TAB_InternalGetItemRect (infoPtr
, newItem
, &r
, NULL
))
667 InvalidateRect (infoPtr
->hwnd
, &r
, TRUE
);
672 BOOL pressed
= FALSE
;
674 /* any button pressed ? */
675 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
676 if ((TAB_GetItem (infoPtr
, i
)->dwState
& TCIS_BUTTONPRESSED
) &&
677 (infoPtr
->iSelected
!= i
))
683 TAB_SendSimpleNotify(infoPtr
, TCN_SELCHANGING
);
686 TAB_DeselectAll (infoPtr
, FALSE
);
688 TAB_SetCurSel(infoPtr
, newItem
);
690 TAB_SendSimpleNotify(infoPtr
, TCN_SELCHANGE
);
697 static inline LRESULT
698 TAB_LButtonUp (const TAB_INFO
*infoPtr
)
700 TAB_SendSimpleNotify(infoPtr
, NM_CLICK
);
705 static inline LRESULT
706 TAB_RButtonDown (const TAB_INFO
*infoPtr
)
708 TAB_SendSimpleNotify(infoPtr
, NM_RCLICK
);
712 /******************************************************************************
713 * TAB_DrawLoneItemInterior
715 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
716 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
717 * up the device context and font. This routine does the same setup but
718 * only calls TAB_DrawItemInterior for the single specified item.
721 TAB_DrawLoneItemInterior(const TAB_INFO
* infoPtr
, int iItem
)
723 HDC hdc
= GetDC(infoPtr
->hwnd
);
726 /* Clip UpDown control to not draw over it */
727 if (infoPtr
->needsScrolling
)
729 GetWindowRect(infoPtr
->hwnd
, &rC
);
730 GetWindowRect(infoPtr
->hwndUpDown
, &r
);
731 ExcludeClipRect(hdc
, r
.left
- rC
.left
, r
.top
- rC
.top
, r
.right
- rC
.left
, r
.bottom
- rC
.top
);
733 TAB_DrawItemInterior(infoPtr
, hdc
, iItem
, NULL
);
734 ReleaseDC(infoPtr
->hwnd
, hdc
);
737 /* update a tab after hottracking - invalidate it or just redraw the interior,
738 * based on whether theming is used or not */
739 static inline void hottrack_refresh(const TAB_INFO
*infoPtr
, int tabIndex
)
741 if (tabIndex
== -1) return;
743 if (GetWindowTheme (infoPtr
->hwnd
))
746 TAB_InternalGetItemRect(infoPtr
, tabIndex
, &rect
, NULL
);
747 InvalidateRect (infoPtr
->hwnd
, &rect
, FALSE
);
750 TAB_DrawLoneItemInterior(infoPtr
, tabIndex
);
753 /******************************************************************************
754 * TAB_HotTrackTimerProc
756 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
757 * timer is setup so we can check if the mouse is moved out of our window.
758 * (We don't get an event when the mouse leaves, the mouse-move events just
759 * stop being delivered to our window and just start being delivered to
760 * another window.) This function is called when the timer triggers so
761 * we can check if the mouse has left our window. If so, we un-highlight
762 * the hot-tracked tab.
765 TAB_HotTrackTimerProc
767 HWND hwnd
, /* handle of window for timer messages */
768 UINT uMsg
, /* WM_TIMER message */
769 UINT_PTR idEvent
, /* timer identifier */
770 DWORD dwTime
/* current system time */
773 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
775 if (infoPtr
!= NULL
&& infoPtr
->iHotTracked
>= 0)
780 ** If we can't get the cursor position, or if the cursor is outside our
781 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
782 ** "outside" even if it is within our bounding rect if another window
783 ** overlaps. Note also that the case where the cursor stayed within our
784 ** window but has moved off the hot-tracked tab will be handled by the
785 ** WM_MOUSEMOVE event.
787 if (!GetCursorPos(&pt
) || WindowFromPoint(pt
) != hwnd
)
789 /* Redraw iHotTracked to look normal */
790 INT iRedraw
= infoPtr
->iHotTracked
;
791 infoPtr
->iHotTracked
= -1;
792 hottrack_refresh (infoPtr
, iRedraw
);
794 /* Kill this timer */
795 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
800 /******************************************************************************
803 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
804 * should be highlighted. This function determines which tab in a tab control,
805 * if any, is under the mouse and records that information. The caller may
806 * supply output parameters to receive the item number of the tab item which
807 * was highlighted but isn't any longer and of the tab item which is now
808 * highlighted but wasn't previously. The caller can use this information to
809 * selectively redraw those tab items.
811 * If the caller has a mouse position, it can supply it through the pos
812 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
813 * supplies NULL and this function determines the current mouse position
821 int* out_redrawLeave
,
828 if (out_redrawLeave
!= NULL
)
829 *out_redrawLeave
= -1;
830 if (out_redrawEnter
!= NULL
)
831 *out_redrawEnter
= -1;
833 if ((infoPtr
->dwStyle
& TCS_HOTTRACK
) || GetWindowTheme(infoPtr
->hwnd
))
841 ScreenToClient(infoPtr
->hwnd
, &pt
);
845 pt
.x
= (short)LOWORD(*pos
);
846 pt
.y
= (short)HIWORD(*pos
);
849 item
= TAB_InternalHitTest(infoPtr
, pt
, &flags
);
852 if (item
!= infoPtr
->iHotTracked
)
854 if (infoPtr
->iHotTracked
>= 0)
856 /* Mark currently hot-tracked to be redrawn to look normal */
857 if (out_redrawLeave
!= NULL
)
858 *out_redrawLeave
= infoPtr
->iHotTracked
;
862 /* Kill timer which forces recheck of mouse pos */
863 KillTimer(infoPtr
->hwnd
, TAB_HOTTRACK_TIMER
);
868 /* Start timer so we recheck mouse pos */
869 UINT timerID
= SetTimer
873 TAB_HOTTRACK_TIMER_INTERVAL
,
874 TAB_HotTrackTimerProc
878 return; /* Hot tracking not available */
881 infoPtr
->iHotTracked
= item
;
885 /* Mark new hot-tracked to be redrawn to look highlighted */
886 if (out_redrawEnter
!= NULL
)
887 *out_redrawEnter
= item
;
892 /******************************************************************************
895 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
898 TAB_MouseMove (TAB_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
903 if (infoPtr
->hwndToolTip
)
904 TAB_RelayEvent (infoPtr
->hwndToolTip
, infoPtr
->hwnd
,
905 WM_LBUTTONDOWN
, wParam
, lParam
);
907 /* Determine which tab to highlight. Redraw tabs which change highlight
909 TAB_RecalcHotTrack(infoPtr
, &lParam
, &redrawLeave
, &redrawEnter
);
911 hottrack_refresh (infoPtr
, redrawLeave
);
912 hottrack_refresh (infoPtr
, redrawEnter
);
917 /******************************************************************************
920 * Calculates the tab control's display area given the window rectangle or
921 * the window rectangle given the requested display rectangle.
923 static LRESULT
TAB_AdjustRect(const TAB_INFO
*infoPtr
, WPARAM fLarger
, LPRECT prc
)
925 LONG
*iRightBottom
, *iLeftTop
;
927 TRACE ("hwnd=%p fLarger=%ld (%s)\n", infoPtr
->hwnd
, fLarger
,
928 wine_dbgstr_rect(prc
));
932 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
934 iRightBottom
= &(prc
->right
);
935 iLeftTop
= &(prc
->left
);
939 iRightBottom
= &(prc
->bottom
);
940 iLeftTop
= &(prc
->top
);
943 if (fLarger
) /* Go from display rectangle */
945 /* Add the height of the tabs. */
946 if (infoPtr
->dwStyle
& TCS_BOTTOM
)
947 *iRightBottom
+= infoPtr
->tabHeight
* infoPtr
->uNumRows
;
949 *iLeftTop
-= infoPtr
->tabHeight
* infoPtr
->uNumRows
+
950 ((infoPtr
->dwStyle
& TCS_BUTTONS
)? 3 * (infoPtr
->uNumRows
- 1) : 0);
952 /* Inflate the rectangle for the padding */
953 InflateRect(prc
, DISPLAY_AREA_PADDINGX
, DISPLAY_AREA_PADDINGY
);
955 /* Inflate for the border */
956 InflateRect(prc
, CONTROL_BORDER_SIZEX
, CONTROL_BORDER_SIZEY
);
958 else /* Go from window rectangle. */
960 /* Deflate the rectangle for the border */
961 InflateRect(prc
, -CONTROL_BORDER_SIZEX
, -CONTROL_BORDER_SIZEY
);
963 /* Deflate the rectangle for the padding */
964 InflateRect(prc
, -DISPLAY_AREA_PADDINGX
, -DISPLAY_AREA_PADDINGY
);
966 /* Remove the height of the tabs. */
967 if (infoPtr
->dwStyle
& TCS_BOTTOM
)
968 *iRightBottom
-= infoPtr
->tabHeight
* infoPtr
->uNumRows
;
970 *iLeftTop
+= (infoPtr
->tabHeight
) * infoPtr
->uNumRows
+
971 ((infoPtr
->dwStyle
& TCS_BUTTONS
)? 3 * (infoPtr
->uNumRows
- 1) : 0);
977 /******************************************************************************
980 * This method will handle the notification from the scroll control and
981 * perform the scrolling operation on the tab control.
983 static LRESULT
TAB_OnHScroll(TAB_INFO
*infoPtr
, int nScrollCode
, int nPos
)
985 if(nScrollCode
== SB_THUMBPOSITION
&& nPos
!= infoPtr
->leftmostVisible
)
987 if(nPos
< infoPtr
->leftmostVisible
)
988 infoPtr
->leftmostVisible
--;
990 infoPtr
->leftmostVisible
++;
992 TAB_RecalcHotTrack(infoPtr
, NULL
, NULL
, NULL
);
993 TAB_InvalidateTabArea(infoPtr
);
994 SendMessageW(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
995 MAKELONG(infoPtr
->leftmostVisible
, 0));
1001 /******************************************************************************
1002 * TAB_SetupScrolling
1004 * This method will check the current scrolling state and make sure the
1005 * scrolling control is displayed (or not).
1007 static void TAB_SetupScrolling(
1009 const RECT
* clientRect
)
1011 static const WCHAR emptyW
[] = { 0 };
1014 if (infoPtr
->needsScrolling
)
1017 INT vsize
, tabwidth
;
1020 * Calculate the position of the scroll control.
1022 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
1024 controlPos
.right
= clientRect
->right
;
1025 controlPos
.left
= controlPos
.right
- 2 * GetSystemMetrics(SM_CXHSCROLL
);
1027 if (infoPtr
->dwStyle
& TCS_BOTTOM
)
1029 controlPos
.top
= clientRect
->bottom
- infoPtr
->tabHeight
;
1030 controlPos
.bottom
= controlPos
.top
+ GetSystemMetrics(SM_CYHSCROLL
);
1034 controlPos
.bottom
= clientRect
->top
+ infoPtr
->tabHeight
;
1035 controlPos
.top
= controlPos
.bottom
- GetSystemMetrics(SM_CYHSCROLL
);
1040 controlPos
.right
= clientRect
->right
;
1041 controlPos
.left
= controlPos
.right
- 2 * GetSystemMetrics(SM_CXHSCROLL
);
1043 if (infoPtr
->dwStyle
& TCS_BOTTOM
)
1045 controlPos
.top
= clientRect
->bottom
- infoPtr
->tabHeight
;
1046 controlPos
.bottom
= controlPos
.top
+ GetSystemMetrics(SM_CYHSCROLL
);
1050 controlPos
.bottom
= clientRect
->top
+ infoPtr
->tabHeight
;
1051 controlPos
.top
= controlPos
.bottom
- GetSystemMetrics(SM_CYHSCROLL
);
1056 * If we don't have a scroll control yet, we want to create one.
1057 * If we have one, we want to make sure it's positioned properly.
1059 if (infoPtr
->hwndUpDown
==0)
1061 infoPtr
->hwndUpDown
= CreateWindowW(UPDOWN_CLASSW
, emptyW
,
1062 WS_VISIBLE
| WS_CHILD
| UDS_HORZ
,
1063 controlPos
.left
, controlPos
.top
,
1064 controlPos
.right
- controlPos
.left
,
1065 controlPos
.bottom
- controlPos
.top
,
1066 infoPtr
->hwnd
, NULL
, NULL
, NULL
);
1070 SetWindowPos(infoPtr
->hwndUpDown
,
1072 controlPos
.left
, controlPos
.top
,
1073 controlPos
.right
- controlPos
.left
,
1074 controlPos
.bottom
- controlPos
.top
,
1075 SWP_SHOWWINDOW
| SWP_NOZORDER
);
1078 /* Now calculate upper limit of the updown control range.
1079 * We do this by calculating how many tabs will be offscreen when the
1080 * last tab is visible.
1082 if(infoPtr
->uNumItem
)
1084 vsize
= clientRect
->right
- (controlPos
.right
- controlPos
.left
+ 1);
1085 maxRange
= infoPtr
->uNumItem
;
1086 tabwidth
= TAB_GetItem(infoPtr
, infoPtr
->uNumItem
- 1)->rect
.right
;
1088 for(; maxRange
> 0; maxRange
--)
1090 if(tabwidth
- TAB_GetItem(infoPtr
,maxRange
- 1)->rect
.left
> vsize
)
1094 if(maxRange
== infoPtr
->uNumItem
)
1100 /* If we once had a scroll control... hide it */
1101 if (infoPtr
->hwndUpDown
)
1102 ShowWindow(infoPtr
->hwndUpDown
, SW_HIDE
);
1104 if (infoPtr
->hwndUpDown
)
1105 SendMessageW(infoPtr
->hwndUpDown
, UDM_SETRANGE32
, 0, maxRange
);
1108 /******************************************************************************
1111 * This method will calculate the position rectangles of all the items in the
1112 * control. The rectangle calculated starts at 0 for the first item in the
1113 * list and ignores scrolling and selection.
1114 * It also uses the current font to determine the height of the tab row and
1115 * it checks if all the tabs fit in the client area of the window. If they
1116 * don't, a scrolling control is added.
1118 static void TAB_SetItemBounds (TAB_INFO
*infoPtr
)
1120 TEXTMETRICW fontMetrics
;
1123 INT curItemRowCount
;
1124 HFONT hFont
, hOldFont
;
1133 * We need to get text information so we need a DC and we need to select
1136 hdc
= GetDC(infoPtr
->hwnd
);
1138 hFont
= infoPtr
->hFont
? infoPtr
->hFont
: GetStockObject (SYSTEM_FONT
);
1139 hOldFont
= SelectObject (hdc
, hFont
);
1142 * We will base the rectangle calculations on the client rectangle
1145 GetClientRect(infoPtr
->hwnd
, &clientRect
);
1147 /* if TCS_VERTICAL then swap the height and width so this code places the
1148 tabs along the top of the rectangle and we can just rotate them after
1149 rather than duplicate all of the below code */
1150 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
1152 iTemp
= clientRect
.bottom
;
1153 clientRect
.bottom
= clientRect
.right
;
1154 clientRect
.right
= iTemp
;
1157 /* Now use hPadding and vPadding */
1158 infoPtr
->uHItemPadding
= infoPtr
->uHItemPadding_s
;
1159 infoPtr
->uVItemPadding
= infoPtr
->uVItemPadding_s
;
1161 /* The leftmost item will be "0" aligned */
1163 curItemRowCount
= infoPtr
->uNumItem
? 1 : 0;
1165 if (!(infoPtr
->fHeightSet
))
1168 INT icon_height
= 0, cx
;
1170 /* Use the current font to determine the height of a tab. */
1171 GetTextMetricsW(hdc
, &fontMetrics
);
1173 /* Get the icon height */
1175 ImageList_GetIconSize(infoPtr
->himl
, &cx
, &icon_height
);
1177 /* Take the highest between font or icon */
1178 if (fontMetrics
.tmHeight
> icon_height
)
1179 item_height
= fontMetrics
.tmHeight
+ 2;
1181 item_height
= icon_height
;
1184 * Make sure there is enough space for the letters + icon + growing the
1185 * selected item + extra space for the selected item.
1187 infoPtr
->tabHeight
= item_height
+
1188 ((infoPtr
->dwStyle
& TCS_BUTTONS
) ? 2 : 1) *
1189 infoPtr
->uVItemPadding
;
1191 TRACE("tabH=%d, tmH=%d, iconh=%d\n",
1192 infoPtr
->tabHeight
, fontMetrics
.tmHeight
, icon_height
);
1195 TRACE("client right=%d\n", clientRect
.right
);
1197 /* Get the icon width */
1202 ImageList_GetIconSize(infoPtr
->himl
, &icon_width
, &cy
);
1204 if (infoPtr
->dwStyle
& TCS_FIXEDWIDTH
)
1207 /* Add padding if icon is present */
1208 icon_width
+= infoPtr
->uHItemPadding
;
1211 for (curItem
= 0; curItem
< infoPtr
->uNumItem
; curItem
++)
1213 TAB_ITEM
*curr
= TAB_GetItem(infoPtr
, curItem
);
1215 /* Set the leftmost position of the tab. */
1216 curr
->rect
.left
= curItemLeftPos
;
1218 if (infoPtr
->dwStyle
& TCS_FIXEDWIDTH
)
1220 curr
->rect
.right
= curr
->rect
.left
+
1221 max(infoPtr
->tabWidth
, icon_width
);
1223 else if (!curr
->pszText
)
1225 /* If no text use minimum tab width including padding. */
1226 if (infoPtr
->tabMinWidth
< 0)
1227 curr
->rect
.right
= curr
->rect
.left
+ GET_DEFAULT_MIN_TAB_WIDTH(infoPtr
);
1230 curr
->rect
.right
= curr
->rect
.left
+ infoPtr
->tabMinWidth
;
1232 /* Add extra padding if icon is present */
1233 if (infoPtr
->himl
&& infoPtr
->tabMinWidth
> 0 && infoPtr
->tabMinWidth
< DEFAULT_MIN_TAB_WIDTH
1234 && infoPtr
->uHItemPadding
> 1)
1235 curr
->rect
.right
+= EXTRA_ICON_PADDING
* (infoPtr
->uHItemPadding
-1);
1242 /* Calculate how wide the tab is depending on the text it contains */
1243 GetTextExtentPoint32W(hdc
, curr
->pszText
,
1244 lstrlenW(curr
->pszText
), &size
);
1246 tabwidth
= size
.cx
+ icon_width
+ 2 * infoPtr
->uHItemPadding
;
1248 if (infoPtr
->tabMinWidth
< 0)
1249 tabwidth
= max(tabwidth
, GET_DEFAULT_MIN_TAB_WIDTH(infoPtr
));
1251 tabwidth
= max(tabwidth
, infoPtr
->tabMinWidth
);
1253 curr
->rect
.right
= curr
->rect
.left
+ tabwidth
;
1254 TRACE("for <%s>, l,r=%d,%d\n",
1255 debugstr_w(curr
->pszText
), curr
->rect
.left
, curr
->rect
.right
);
1259 * Check if this is a multiline tab control and if so
1260 * check to see if we should wrap the tabs
1262 * Wrap all these tabs. We will arrange them evenly later.
1266 if (((infoPtr
->dwStyle
& TCS_MULTILINE
) || (infoPtr
->dwStyle
& TCS_VERTICAL
)) &&
1268 (clientRect
.right
- CONTROL_BORDER_SIZEX
- DISPLAY_AREA_PADDINGX
)))
1270 curr
->rect
.right
-= curr
->rect
.left
;
1272 curr
->rect
.left
= 0;
1274 TRACE("wrapping <%s>, l,r=%d,%d\n", debugstr_w(curr
->pszText
),
1275 curr
->rect
.left
, curr
->rect
.right
);
1278 curr
->rect
.bottom
= 0;
1279 curr
->rect
.top
= curItemRowCount
- 1;
1281 TRACE("Rect: %s\n", wine_dbgstr_rect(&curr
->rect
));
1284 * The leftmost position of the next item is the rightmost position
1287 if (infoPtr
->dwStyle
& TCS_BUTTONS
)
1289 curItemLeftPos
= curr
->rect
.right
+ BUTTON_SPACINGX
;
1290 if (infoPtr
->dwStyle
& TCS_FLATBUTTONS
)
1291 curItemLeftPos
+= FLAT_BTN_SPACINGX
;
1294 curItemLeftPos
= curr
->rect
.right
;
1297 if (!((infoPtr
->dwStyle
& TCS_MULTILINE
) || (infoPtr
->dwStyle
& TCS_VERTICAL
)))
1300 * Check if we need a scrolling control.
1302 infoPtr
->needsScrolling
= (curItemLeftPos
+ (2 * SELECTED_TAB_OFFSET
) >
1305 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1306 if(!infoPtr
->needsScrolling
)
1307 infoPtr
->leftmostVisible
= 0;
1312 * No scrolling in Multiline or Vertical styles.
1314 infoPtr
->needsScrolling
= FALSE
;
1315 infoPtr
->leftmostVisible
= 0;
1317 TAB_SetupScrolling(infoPtr
, &clientRect
);
1319 /* Set the number of rows */
1320 infoPtr
->uNumRows
= curItemRowCount
;
1322 /* Arrange all tabs evenly if style says so */
1323 if (!(infoPtr
->dwStyle
& TCS_RAGGEDRIGHT
) &&
1324 ((infoPtr
->dwStyle
& TCS_MULTILINE
) || (infoPtr
->dwStyle
& TCS_VERTICAL
)) &&
1325 (infoPtr
->uNumItem
> 0) &&
1326 (infoPtr
->uNumRows
> 1))
1328 INT tabPerRow
,remTab
,iRow
;
1333 * Ok windows tries to even out the rows. place the same
1334 * number of tabs in each row. So lets give that a shot
1337 tabPerRow
= infoPtr
->uNumItem
/ (infoPtr
->uNumRows
);
1338 remTab
= infoPtr
->uNumItem
% (infoPtr
->uNumRows
);
1340 for (iItm
=0,iRow
=0,iCount
=0,curItemLeftPos
=0;
1341 iItm
<infoPtr
->uNumItem
;
1344 /* normalize the current rect */
1345 TAB_ITEM
*curr
= TAB_GetItem(infoPtr
, iItm
);
1347 /* shift the item to the left side of the clientRect */
1348 curr
->rect
.right
-= curr
->rect
.left
;
1349 curr
->rect
.left
= 0;
1351 TRACE("r=%d, cl=%d, cl.r=%d, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1352 curr
->rect
.right
, curItemLeftPos
, clientRect
.right
,
1353 iCount
, iRow
, infoPtr
->uNumRows
, remTab
, tabPerRow
);
1355 /* if we have reached the maximum number of tabs on this row */
1356 /* move to the next row, reset our current item left position and */
1357 /* the count of items on this row */
1359 if (infoPtr
->dwStyle
& TCS_VERTICAL
) {
1360 /* Vert: Add the remaining tabs in the *last* remainder rows */
1361 if (iCount
>= ((iRow
>=(INT
)infoPtr
->uNumRows
- remTab
)?tabPerRow
+ 1:tabPerRow
)) {
1367 /* Horz: Add the remaining tabs in the *first* remainder rows */
1368 if (iCount
>= ((iRow
<remTab
)?tabPerRow
+ 1:tabPerRow
)) {
1375 /* shift the item to the right to place it as the next item in this row */
1376 curr
->rect
.left
+= curItemLeftPos
;
1377 curr
->rect
.right
+= curItemLeftPos
;
1378 curr
->rect
.top
= iRow
;
1379 if (infoPtr
->dwStyle
& TCS_BUTTONS
)
1381 curItemLeftPos
= curr
->rect
.right
+ 1;
1382 if (infoPtr
->dwStyle
& TCS_FLATBUTTONS
)
1383 curItemLeftPos
+= FLAT_BTN_SPACINGX
;
1386 curItemLeftPos
= curr
->rect
.right
;
1388 TRACE("arranging <%s>, l,r=%d,%d, row=%d\n",
1389 debugstr_w(curr
->pszText
), curr
->rect
.left
,
1390 curr
->rect
.right
, curr
->rect
.top
);
1397 INT widthDiff
, iIndexStart
=0, iIndexEnd
=0;
1401 while(iIndexStart
< infoPtr
->uNumItem
)
1403 TAB_ITEM
*start
= TAB_GetItem(infoPtr
, iIndexStart
);
1406 * find the index of the row
1408 /* find the first item on the next row */
1409 for (iIndexEnd
=iIndexStart
;
1410 (iIndexEnd
< infoPtr
->uNumItem
) &&
1411 (TAB_GetItem(infoPtr
, iIndexEnd
)->rect
.top
==
1414 /* intentionally blank */;
1417 * we need to justify these tabs so they fill the whole given
1421 /* find the amount of space remaining on this row */
1422 widthDiff
= clientRect
.right
- (2 * SELECTED_TAB_OFFSET
) -
1423 TAB_GetItem(infoPtr
, iIndexEnd
- 1)->rect
.right
;
1425 /* iCount is the number of tab items on this row */
1426 iCount
= iIndexEnd
- iIndexStart
;
1430 remainder
= widthDiff
% iCount
;
1431 widthDiff
= widthDiff
/ iCount
;
1432 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1433 for (iIndex
=iIndexStart
, iCount
=0; iIndex
< iIndexEnd
; iIndex
++, iCount
++)
1435 TAB_ITEM
*item
= TAB_GetItem(infoPtr
, iIndex
);
1437 item
->rect
.left
+= iCount
* widthDiff
;
1438 item
->rect
.right
+= (iCount
+ 1) * widthDiff
;
1440 TRACE("adjusting 1 <%s>, l,r=%d,%d\n",
1441 debugstr_w(item
->pszText
),
1442 item
->rect
.left
, item
->rect
.right
);
1445 TAB_GetItem(infoPtr
, iIndex
- 1)->rect
.right
+= remainder
;
1447 else /* we have only one item on this row, make it take up the entire row */
1449 start
->rect
.left
= clientRect
.left
;
1450 start
->rect
.right
= clientRect
.right
- 4;
1452 TRACE("adjusting 2 <%s>, l,r=%d,%d\n",
1453 debugstr_w(start
->pszText
),
1454 start
->rect
.left
, start
->rect
.right
);
1459 iIndexStart
= iIndexEnd
;
1464 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1465 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
1468 for(iIndex
= 0; iIndex
< infoPtr
->uNumItem
; iIndex
++)
1470 rcItem
= &TAB_GetItem(infoPtr
, iIndex
)->rect
;
1472 rcOriginal
= *rcItem
;
1474 /* this is rotating the items by 90 degrees clockwise around the center of the control */
1475 rcItem
->top
= (rcOriginal
.left
- clientRect
.left
);
1476 rcItem
->bottom
= rcItem
->top
+ (rcOriginal
.right
- rcOriginal
.left
);
1477 rcItem
->left
= rcOriginal
.top
;
1478 rcItem
->right
= rcOriginal
.bottom
;
1482 TAB_EnsureSelectionVisible(infoPtr
);
1483 TAB_RecalcHotTrack(infoPtr
, NULL
, NULL
, NULL
);
1486 SelectObject (hdc
, hOldFont
);
1487 ReleaseDC (infoPtr
->hwnd
, hdc
);
1492 TAB_EraseTabInterior(const TAB_INFO
*infoPtr
, HDC hdc
, INT iItem
, const RECT
*drawRect
)
1494 HBRUSH hbr
= CreateSolidBrush (comctl32_color
.clrBtnFace
);
1495 BOOL deleteBrush
= TRUE
;
1496 RECT rTemp
= *drawRect
;
1498 if (infoPtr
->dwStyle
& TCS_BUTTONS
)
1500 if (iItem
== infoPtr
->iSelected
)
1502 /* Background color */
1503 if (!(infoPtr
->dwStyle
& TCS_OWNERDRAWFIXED
))
1506 hbr
= GetSysColorBrush(COLOR_SCROLLBAR
);
1508 SetTextColor(hdc
, comctl32_color
.clr3dFace
);
1509 SetBkColor(hdc
, comctl32_color
.clr3dHilight
);
1511 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1512 * we better use 0x55aa bitmap brush to make scrollbar's background
1513 * look different from the window background.
1515 if (comctl32_color
.clr3dHilight
== comctl32_color
.clrWindow
)
1516 hbr
= COMCTL32_hPattern55AABrush
;
1518 deleteBrush
= FALSE
;
1520 FillRect(hdc
, &rTemp
, hbr
);
1522 else /* ! selected */
1524 if (infoPtr
->dwStyle
& TCS_FLATBUTTONS
)
1526 InflateRect(&rTemp
, 2, 2);
1527 FillRect(hdc
, &rTemp
, hbr
);
1528 if (iItem
== infoPtr
->iHotTracked
||
1529 (iItem
!= infoPtr
->iSelected
&& iItem
== infoPtr
->uFocus
))
1530 DrawEdge(hdc
, &rTemp
, BDR_RAISEDINNER
, BF_RECT
);
1533 FillRect(hdc
, &rTemp
, hbr
);
1537 else /* !TCS_BUTTONS */
1539 InflateRect(&rTemp
, -2, -2);
1540 if (!GetWindowTheme (infoPtr
->hwnd
))
1541 FillRect(hdc
, &rTemp
, hbr
);
1544 /* highlighting is drawn on top of previous fills */
1545 if (TAB_GetItem(infoPtr
, iItem
)->dwState
& TCIS_HIGHLIGHTED
)
1550 deleteBrush
= FALSE
;
1552 hbr
= GetSysColorBrush(COLOR_HIGHLIGHT
);
1553 FillRect(hdc
, &rTemp
, hbr
);
1557 if (deleteBrush
) DeleteObject(hbr
);
1560 /******************************************************************************
1561 * TAB_DrawItemInterior
1563 * This method is used to draw the interior (text and icon) of a single tab
1564 * into the tab control.
1567 TAB_DrawItemInterior(const TAB_INFO
*infoPtr
, HDC hdc
, INT iItem
, RECT
*drawRect
)
1576 /* if (drawRect == NULL) */
1583 * Get the rectangle for the item.
1585 isVisible
= TAB_InternalGetItemRect(infoPtr
, iItem
, &itemRect
, &selectedRect
);
1590 * Make sure drawRect points to something valid; simplifies code.
1592 drawRect
= &localRect
;
1595 * This logic copied from the part of TAB_DrawItem which draws
1596 * the tab background. It's important to keep it in sync. I
1597 * would have liked to avoid code duplication, but couldn't figure
1598 * out how without making spaghetti of TAB_DrawItem.
1600 if (iItem
== infoPtr
->iSelected
)
1601 *drawRect
= selectedRect
;
1603 *drawRect
= itemRect
;
1605 if (infoPtr
->dwStyle
& TCS_BUTTONS
)
1607 if (iItem
== infoPtr
->iSelected
)
1609 drawRect
->left
+= 4;
1611 drawRect
->right
-= 4;
1613 if (infoPtr
->dwStyle
& TCS_VERTICAL
)
1615 if (!(infoPtr
->dwStyle
& TCS_BOTTOM
)) drawRect
->right
+= 1;
1616 drawRect
->bottom
-= 4;
1620 if (infoPtr
->dwStyle
& TCS_BOTTOM
)
1623 drawRect
->bottom
-= 4;
1626 drawRect
->bottom
-= 1;
1631 drawRect
->left
+= 2;
1633 drawRect
->right
-= 2;
1634 drawRect
->bottom
-= 2;
1639 if ((infoPtr
->dwStyle
& TCS_VERTICAL
) && (infoPtr
->dwStyle
& TCS_BOTTOM
))
1641 if (iItem
!= infoPtr
->iSelected
)
1643 drawRect
->left
+= 2;
1645 drawRect
->bottom
-= 2;
1648 else if (infoPtr
->dwStyle
& TCS_VERTICAL
)
1650 if (iItem
== infoPtr
->iSelected
)
1652 drawRect
->right
+= 1;
1657 drawRect
->right
-= 2;
1658 drawRect
->bottom
-= 2;
1661 else if (infoPtr
->dwStyle
& TCS_BOTTOM
)
1663 if (iItem
== infoPtr
->iSelected
)
1669 InflateRect(drawRect
, -2, -2);
1670 drawRect
->bottom
+= 2;
1675 if (iItem
== infoPtr
->iSelected
)
1677 drawRect
->bottom
+= 3;
1681 drawRect
->bottom
-= 2;
1682 InflateRect(drawRect
, -2, 0);
1687 TRACE("drawRect=(%s)\n", wine_dbgstr_rect(drawRect
));
1689 /* Clear interior */
1690 TAB_EraseTabInterior (infoPtr
, hdc
, iItem
, drawRect
);
1692 /* Draw the focus rectangle */
1693 if (!(infoPtr
->dwStyle
& TCS_FOCUSNEVER
) &&
1694 (GetFocus() == infoPtr
->hwnd
) &&
1695 (iItem
== infoPtr
->uFocus
) )
1697 RECT rFocus
= *drawRect
;
1699 if (!(infoPtr
->dwStyle
& TCS_BUTTONS
)) InflateRect(&rFocus
, -3, -3);
1700 if (infoPtr
->dwStyle
& TCS_BOTTOM
&& !(infoPtr
->dwStyle
& TCS_VERTICAL
))
1703 /* focus should stay on selected item for TCS_BUTTONS style */
1704 if (!((infoPtr
->dwStyle
& TCS_BUTTONS
) && (infoPtr
->iSelected
!= iItem
)))
1705 DrawFocusRect(hdc
, &rFocus
);
1711 htextPen
= CreatePen( PS_SOLID
, 1, comctl32_color
.clrBtnText
);
1712 holdPen
= SelectObject(hdc
, htextPen
);
1713 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
1716 * Setup for text output
1718 oldBkMode
= SetBkMode(hdc
, TRANSPARENT
);
1719 if (!GetWindowTheme (infoPtr
->hwnd
) || (infoPtr
->dwStyle
& TCS_BUTTONS
))
1721 if ((infoPtr
->dwStyle
& TCS_HOTTRACK
) && (iItem
== infoPtr
->iHotTracked
) &&
1722 !(infoPtr
->dwStyle
& TCS_FLATBUTTONS
))
1723 SetTextColor(hdc
, comctl32_color
.clrHighlight
);
1724 else if (TAB_GetItem(infoPtr
, iItem
)->dwState
& TCIS_HIGHLIGHTED
)
1725 SetTextColor(hdc
, comctl32_color
.clrHighlightText
);
1727 SetTextColor(hdc
, comctl32_color
.clrBtnText
);
1731 * if owner draw, tell the owner to draw
1733 if ((infoPtr
->dwStyle
& TCS_OWNERDRAWFIXED
) && IsWindow(infoPtr
->hwndNotify
))
1739 drawRect
->right
-= 1;
1740 if ( iItem
== infoPtr
->iSelected
)
1742 drawRect
->right
-= 1;
1743 drawRect
->left
+= 1;
1746 id
= (UINT
)GetWindowLongPtrW( infoPtr
->hwnd
, GWLP_ID
);
1748 /* fill DRAWITEMSTRUCT */
1749 dis
.CtlType
= ODT_TAB
;
1752 dis
.itemAction
= ODA_DRAWENTIRE
;
1754 if ( iItem
== infoPtr
->iSelected
)
1755 dis
.itemState
|= ODS_SELECTED
;
1756 if (infoPtr
->uFocus
== iItem
)
1757 dis
.itemState
|= ODS_FOCUS
;
1758 dis
.hwndItem
= infoPtr
->hwnd
;
1760 CopyRect(&dis
.rcItem
,drawRect
);
1762 /* when extra data fits ULONG_PTR, store it directly */
1763 if (infoPtr
->cbInfo
> sizeof(LPARAM
))
1764 dis
.itemData
= (ULONG_PTR
) TAB_GetItem(infoPtr
, iItem
)->extra
;
1767 /* this could be considered broken on 64 bit, but that's how it works -
1768 only first 4 bytes are copied */
1769 memcpy(&dis
.itemData
, (ULONG_PTR
*)TAB_GetItem(infoPtr
, iItem
)->extra
, 4);
1772 /* draw notification */
1773 SendMessageW( infoPtr
->hwndNotify
, WM_DRAWITEM
, id
, (LPARAM
)&dis
);
1777 TAB_ITEM
*item
= TAB_GetItem(infoPtr
, iItem
);
1781 /* used to center the icon and text in the tab */
1783 INT center_offset_h
, center_offset_v
;
1785 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1786 rcImage
= *drawRect
;
1790 rcText
.left
= rcText
.top
= rcText
.right
= rcText
.bottom
= 0;
1792 /* get the rectangle that the text fits in */
1795 DrawTextW(hdc
, item
->pszText
, -1, &rcText
, DT_CALCRECT
);
1798 * If not owner draw, then do the drawing ourselves.
1802 if (infoPtr
->himl
&& item
->iImage
!= -1)
1807 ImageList_GetIconSize(infoPtr
->himl
, &cx
, &cy
);
1809 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
1811 center_offset_h
= ((drawRect
->bottom
- drawRect
->top
) - (cy
+ infoPtr
->uHItemPadding
+ (rcText
.right
- rcText
.left
))) / 2;
1812 center_offset_v
= ((drawRect
->right
- drawRect
->left
) - cx
) / 2;
1816 center_offset_h
= ((drawRect
->right
- drawRect
->left
) - (cx
+ infoPtr
->uHItemPadding
+ (rcText
.right
- rcText
.left
))) / 2;
1817 center_offset_v
= ((drawRect
->bottom
- drawRect
->top
) - cy
) / 2;
1820 /* if an item is selected, the icon is shifted up instead of down */
1821 if (iItem
== infoPtr
->iSelected
)
1822 center_offset_v
-= infoPtr
->uVItemPadding
/ 2;
1824 center_offset_v
+= infoPtr
->uVItemPadding
/ 2;
1826 if (infoPtr
->dwStyle
& TCS_FIXEDWIDTH
&& infoPtr
->dwStyle
& (TCS_FORCELABELLEFT
| TCS_FORCEICONLEFT
))
1827 center_offset_h
= infoPtr
->uHItemPadding
;
1829 if (center_offset_h
< 2)
1830 center_offset_h
= 2;
1832 if (center_offset_v
< 0)
1833 center_offset_v
= 0;
1835 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%s), textlen=%d\n",
1836 debugstr_w(item
->pszText
), center_offset_h
, center_offset_v
,
1837 wine_dbgstr_rect(drawRect
), (rcText
.right
-rcText
.left
));
1839 if((infoPtr
->dwStyle
& TCS_VERTICAL
) && (infoPtr
->dwStyle
& TCS_BOTTOM
))
1841 rcImage
.top
= drawRect
->top
+ center_offset_h
;
1842 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1843 /* right side of the tab, but the image still uses the left as its x position */
1844 /* this keeps the image always drawn off of the same side of the tab */
1845 rcImage
.left
= drawRect
->right
- cx
- center_offset_v
;
1846 drawRect
->top
+= cy
+ infoPtr
->uHItemPadding
;
1848 else if(infoPtr
->dwStyle
& TCS_VERTICAL
)
1850 rcImage
.top
= drawRect
->bottom
- cy
- center_offset_h
;
1851 rcImage
.left
= drawRect
->left
+ center_offset_v
;
1852 drawRect
->bottom
-= cy
+ infoPtr
->uHItemPadding
;
1854 else /* normal style, whether TCS_BOTTOM or not */
1856 rcImage
.left
= drawRect
->left
+ center_offset_h
;
1857 rcImage
.top
= drawRect
->top
+ center_offset_v
;
1858 drawRect
->left
+= cx
+ infoPtr
->uHItemPadding
;
1861 TRACE("drawing image=%d, left=%d, top=%d\n",
1862 item
->iImage
, rcImage
.left
, rcImage
.top
-1);
1874 /* Now position text */
1875 if (infoPtr
->dwStyle
& TCS_FIXEDWIDTH
&& infoPtr
->dwStyle
& TCS_FORCELABELLEFT
)
1876 center_offset_h
= infoPtr
->uHItemPadding
;
1878 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
1879 center_offset_h
= ((drawRect
->bottom
- drawRect
->top
) - (rcText
.right
- rcText
.left
)) / 2;
1881 center_offset_h
= ((drawRect
->right
- drawRect
->left
) - (rcText
.right
- rcText
.left
)) / 2;
1883 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
1885 if(infoPtr
->dwStyle
& TCS_BOTTOM
)
1886 drawRect
->top
+=center_offset_h
;
1888 drawRect
->bottom
-=center_offset_h
;
1890 center_offset_v
= ((drawRect
->right
- drawRect
->left
) - (rcText
.bottom
- rcText
.top
)) / 2;
1894 drawRect
->left
+= center_offset_h
;
1895 center_offset_v
= ((drawRect
->bottom
- drawRect
->top
) - (rcText
.bottom
- rcText
.top
)) / 2;
1898 /* if an item is selected, the text is shifted up instead of down */
1899 if (iItem
== infoPtr
->iSelected
)
1900 center_offset_v
-= infoPtr
->uVItemPadding
/ 2;
1902 center_offset_v
+= infoPtr
->uVItemPadding
/ 2;
1904 if (center_offset_v
< 0)
1905 center_offset_v
= 0;
1907 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
1908 drawRect
->left
+= center_offset_v
;
1910 drawRect
->top
+= center_offset_v
;
1913 if(infoPtr
->dwStyle
& TCS_VERTICAL
) /* if we are vertical rotate the text and each character */
1915 static const WCHAR ArialW
[] = { 'A','r','i','a','l',0 };
1918 INT nEscapement
= 900;
1919 INT nOrientation
= 900;
1921 if(infoPtr
->dwStyle
& TCS_BOTTOM
)
1924 nOrientation
= -900;
1927 /* to get a font with the escapement and orientation we are looking for, we need to */
1928 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1929 if (!GetObjectW((infoPtr
->hFont
) ?
1930 infoPtr
->hFont
: GetStockObject(SYSTEM_FONT
),
1931 sizeof(LOGFONTW
),&logfont
))
1935 lstrcpyW(logfont
.lfFaceName
, ArialW
);
1936 logfont
.lfHeight
= -MulDiv(iPointSize
, GetDeviceCaps(hdc
, LOGPIXELSY
),
1938 logfont
.lfWeight
= FW_NORMAL
;
1939 logfont
.lfItalic
= 0;
1940 logfont
.lfUnderline
= 0;
1941 logfont
.lfStrikeOut
= 0;
1944 logfont
.lfEscapement
= nEscapement
;
1945 logfont
.lfOrientation
= nOrientation
;
1946 hFont
= CreateFontIndirectW(&logfont
);
1947 SelectObject(hdc
, hFont
);
1952 (infoPtr
->dwStyle
& TCS_BOTTOM
) ? drawRect
->right
: drawRect
->left
,
1953 (!(infoPtr
->dwStyle
& TCS_BOTTOM
)) ? drawRect
->bottom
: drawRect
->top
,
1957 lstrlenW(item
->pszText
),
1961 DeleteObject(hFont
);
1965 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%s), textlen=%d\n",
1966 debugstr_w(item
->pszText
), center_offset_h
, center_offset_v
,
1967 wine_dbgstr_rect(drawRect
), (rcText
.right
-rcText
.left
));
1974 lstrlenW(item
->pszText
),
1976 DT_LEFT
| DT_SINGLELINE
1981 *drawRect
= rcTemp
; /* restore drawRect */
1987 SelectObject(hdc
, hOldFont
);
1988 SetBkMode(hdc
, oldBkMode
);
1989 SelectObject(hdc
, holdPen
);
1990 DeleteObject( htextPen
);
1993 /******************************************************************************
1996 * This method is used to draw a single tab into the tab control.
1998 static void TAB_DrawItem(const TAB_INFO
*infoPtr
, HDC hdc
, INT iItem
)
2003 RECT r
, fillRect
, r1
;
2006 COLORREF bkgnd
, corner
;
2010 * Get the rectangle for the item.
2012 isVisible
= TAB_InternalGetItemRect(infoPtr
,
2021 /* Clip UpDown control to not draw over it */
2022 if (infoPtr
->needsScrolling
)
2024 GetWindowRect(infoPtr
->hwnd
, &rC
);
2025 GetWindowRect(infoPtr
->hwndUpDown
, &rUD
);
2026 ExcludeClipRect(hdc
, rUD
.left
- rC
.left
, rUD
.top
- rC
.top
, rUD
.right
- rC
.left
, rUD
.bottom
- rC
.top
);
2029 /* If you need to see what the control is doing,
2030 * then override these variables. They will change what
2031 * fill colors are used for filling the tabs, and the
2032 * corners when drawing the edge.
2034 bkgnd
= comctl32_color
.clrBtnFace
;
2035 corner
= comctl32_color
.clrBtnFace
;
2037 if (infoPtr
->dwStyle
& TCS_BUTTONS
)
2039 /* Get item rectangle */
2042 /* Separators between flat buttons */
2043 if ((infoPtr
->dwStyle
& TCS_FLATBUTTONS
) && (infoPtr
->exStyle
& TCS_EX_FLATSEPARATORS
))
2046 r1
.right
+= (FLAT_BTN_SPACINGX
-2);
2047 DrawEdge(hdc
, &r1
, EDGE_ETCHED
, BF_RIGHT
);
2050 if (iItem
== infoPtr
->iSelected
)
2052 DrawEdge(hdc
, &r
, EDGE_SUNKEN
, BF_SOFT
|BF_RECT
);
2054 OffsetRect(&r
, 1, 1);
2056 else /* ! selected */
2058 DWORD state
= TAB_GetItem(infoPtr
, iItem
)->dwState
;
2060 if ((state
& TCIS_BUTTONPRESSED
) || (iItem
== infoPtr
->uFocus
))
2061 DrawEdge(hdc
, &r
, EDGE_SUNKEN
, BF_SOFT
|BF_RECT
);
2063 if (!(infoPtr
->dwStyle
& TCS_FLATBUTTONS
))
2064 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_RECT
);
2067 else /* !TCS_BUTTONS */
2069 /* We draw a rectangle of different sizes depending on the selection
2071 if (iItem
== infoPtr
->iSelected
) {
2073 GetClientRect (infoPtr
->hwnd
, &rect
);
2074 clRight
= rect
.right
;
2075 clBottom
= rect
.bottom
;
2082 * Erase the background. (Delay it but setup rectangle.)
2083 * This is necessary when drawing the selected item since it is larger
2084 * than the others, it might overlap with stuff already drawn by the
2089 /* Draw themed tabs - but only if they are at the top.
2090 * Windows draws even side or bottom tabs themed, with wacky results.
2091 * However, since in Wine apps may get themed that did not opt in via
2092 * a manifest avoid theming when we know the result will be wrong */
2093 if ((theme
= GetWindowTheme (infoPtr
->hwnd
))
2094 && ((infoPtr
->dwStyle
& (TCS_VERTICAL
| TCS_BOTTOM
)) == 0))
2096 static const int partIds
[8] = {
2099 TABP_TABITEMLEFTEDGE
,
2100 TABP_TABITEMRIGHTEDGE
,
2101 TABP_TABITEMBOTHEDGE
,
2104 TABP_TOPTABITEMLEFTEDGE
,
2105 TABP_TOPTABITEMRIGHTEDGE
,
2106 TABP_TOPTABITEMBOTHEDGE
,
2109 int stateId
= TIS_NORMAL
;
2111 /* selected and unselected tabs have different parts */
2112 if (iItem
== infoPtr
->iSelected
)
2114 /* The part also differs on the position of a tab on a line.
2115 * "Visually" determining the position works well enough. */
2116 if(selectedRect
.left
== 0)
2118 if(selectedRect
.right
== clRight
)
2121 if (iItem
== infoPtr
->iSelected
)
2122 stateId
= TIS_SELECTED
;
2123 else if (iItem
== infoPtr
->iHotTracked
)
2125 else if (iItem
== infoPtr
->uFocus
)
2126 stateId
= TIS_FOCUSED
;
2128 /* Adjust rectangle for bottommost row */
2129 if (TAB_GetItem(infoPtr
, iItem
)->rect
.top
== infoPtr
->uNumRows
-1)
2132 DrawThemeBackground (theme
, hdc
, partIds
[partIndex
], stateId
, &r
, NULL
);
2133 GetThemeBackgroundContentRect (theme
, hdc
, partIds
[partIndex
], stateId
, &r
, &r
);
2135 else if(infoPtr
->dwStyle
& TCS_VERTICAL
)
2137 /* These are for adjusting the drawing of a Selected tab */
2138 /* The initial values are for the normal case of non-Selected */
2139 int ZZ
= 1; /* Do not stretch if selected */
2140 if (iItem
== infoPtr
->iSelected
) {
2143 /* if leftmost draw the line longer */
2144 if(selectedRect
.top
== 0)
2145 fillRect
.top
+= CONTROL_BORDER_SIZEY
;
2146 /* if rightmost draw the line longer */
2147 if(selectedRect
.bottom
== clBottom
)
2148 fillRect
.bottom
-= CONTROL_BORDER_SIZEY
;
2151 if (infoPtr
->dwStyle
& TCS_BOTTOM
)
2153 /* Adjust both rectangles to match native */
2156 TRACE("<right> item=%d, fill=(%s), edge=(%s)\n",
2157 iItem
, wine_dbgstr_rect(&fillRect
), wine_dbgstr_rect(&r
));
2159 /* Clear interior */
2160 SetBkColor(hdc
, bkgnd
);
2161 ExtTextOutW(hdc
, 0, 0, 2, &fillRect
, NULL
, 0, 0);
2163 /* Draw rectangular edge around tab */
2164 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_RIGHT
|BF_TOP
|BF_BOTTOM
);
2166 /* Now erase the top corner and draw diagonal edge */
2167 SetBkColor(hdc
, corner
);
2168 r1
.left
= r
.right
- ROUND_CORNER_SIZE
- 1;
2171 r1
.bottom
= r1
.top
+ ROUND_CORNER_SIZE
;
2172 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2174 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDTOPLEFT
);
2176 /* Now erase the bottom corner and draw diagonal edge */
2177 r1
.left
= r
.right
- ROUND_CORNER_SIZE
- 1;
2178 r1
.bottom
= r
.bottom
;
2180 r1
.top
= r1
.bottom
- ROUND_CORNER_SIZE
;
2181 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2183 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDBOTTOMLEFT
);
2185 if ((iItem
== infoPtr
->iSelected
) && (selectedRect
.top
== 0)) {
2189 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_TOP
);
2195 TRACE("<left> item=%d, fill=(%s), edge=(%s)\n",
2196 iItem
, wine_dbgstr_rect(&fillRect
), wine_dbgstr_rect(&r
));
2198 /* Clear interior */
2199 SetBkColor(hdc
, bkgnd
);
2200 ExtTextOutW(hdc
, 0, 0, 2, &fillRect
, NULL
, 0, 0);
2202 /* Draw rectangular edge around tab */
2203 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_LEFT
|BF_TOP
|BF_BOTTOM
);
2205 /* Now erase the top corner and draw diagonal edge */
2206 SetBkColor(hdc
, corner
);
2209 r1
.right
= r1
.left
+ ROUND_CORNER_SIZE
+ 1;
2210 r1
.bottom
= r1
.top
+ ROUND_CORNER_SIZE
;
2211 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2213 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDTOPRIGHT
);
2215 /* Now erase the bottom corner and draw diagonal edge */
2217 r1
.bottom
= r
.bottom
;
2218 r1
.right
= r1
.left
+ ROUND_CORNER_SIZE
+ 1;
2219 r1
.top
= r1
.bottom
- ROUND_CORNER_SIZE
;
2220 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2222 DrawEdge(hdc
, &r1
, EDGE_SUNKEN
, BF_DIAGONAL_ENDTOPLEFT
);
2225 else /* ! TCS_VERTICAL */
2227 /* These are for adjusting the drawing of a Selected tab */
2228 /* The initial values are for the normal case of non-Selected */
2229 if (iItem
== infoPtr
->iSelected
) {
2230 /* if leftmost draw the line longer */
2231 if(selectedRect
.left
== 0)
2232 fillRect
.left
+= CONTROL_BORDER_SIZEX
;
2233 /* if rightmost draw the line longer */
2234 if(selectedRect
.right
== clRight
)
2235 fillRect
.right
-= CONTROL_BORDER_SIZEX
;
2238 if (infoPtr
->dwStyle
& TCS_BOTTOM
)
2240 /* Adjust both rectangles for topmost row */
2241 if (TAB_GetItem(infoPtr
, iItem
)->rect
.top
== infoPtr
->uNumRows
-1)
2247 TRACE("<bottom> item=%d, fill=(%s), edge=(%s)\n",
2248 iItem
, wine_dbgstr_rect(&fillRect
), wine_dbgstr_rect(&r
));
2250 /* Clear interior */
2251 SetBkColor(hdc
, bkgnd
);
2252 ExtTextOutW(hdc
, 0, 0, 2, &fillRect
, NULL
, 0, 0);
2254 /* Draw rectangular edge around tab */
2255 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_LEFT
|BF_BOTTOM
|BF_RIGHT
);
2257 /* Now erase the righthand corner and draw diagonal edge */
2258 SetBkColor(hdc
, corner
);
2259 r1
.left
= r
.right
- ROUND_CORNER_SIZE
;
2260 r1
.bottom
= r
.bottom
;
2262 r1
.top
= r1
.bottom
- ROUND_CORNER_SIZE
- 1;
2263 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2265 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDBOTTOMLEFT
);
2267 /* Now erase the lefthand corner and draw diagonal edge */
2269 r1
.bottom
= r
.bottom
;
2270 r1
.right
= r1
.left
+ ROUND_CORNER_SIZE
;
2271 r1
.top
= r1
.bottom
- ROUND_CORNER_SIZE
- 1;
2272 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2274 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDTOPLEFT
);
2276 if (iItem
== infoPtr
->iSelected
)
2280 if (selectedRect
.left
== 0)
2285 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_LEFT
);
2292 /* Adjust both rectangles for bottommost row */
2293 if (TAB_GetItem(infoPtr
, iItem
)->rect
.top
== infoPtr
->uNumRows
-1)
2295 fillRect
.bottom
+= 3;
2299 TRACE("<top> item=%d, fill=(%s), edge=(%s)\n",
2300 iItem
, wine_dbgstr_rect(&fillRect
), wine_dbgstr_rect(&r
));
2302 /* Clear interior */
2303 SetBkColor(hdc
, bkgnd
);
2304 ExtTextOutW(hdc
, 0, 0, 2, &fillRect
, NULL
, 0, 0);
2306 /* Draw rectangular edge around tab */
2307 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_LEFT
|BF_TOP
|BF_RIGHT
);
2309 /* Now erase the righthand corner and draw diagonal edge */
2310 SetBkColor(hdc
, corner
);
2311 r1
.left
= r
.right
- ROUND_CORNER_SIZE
;
2314 r1
.bottom
= r1
.top
+ ROUND_CORNER_SIZE
+ 1;
2315 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2317 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDBOTTOMRIGHT
);
2319 /* Now erase the lefthand corner and draw diagonal edge */
2322 r1
.right
= r1
.left
+ ROUND_CORNER_SIZE
;
2323 r1
.bottom
= r1
.top
+ ROUND_CORNER_SIZE
+ 1;
2324 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2326 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDTOPRIGHT
);
2331 TAB_DumpItemInternal(infoPtr
, iItem
);
2333 /* This modifies r to be the text rectangle. */
2334 TAB_DrawItemInterior(infoPtr
, hdc
, iItem
, &r
);
2338 /******************************************************************************
2341 * This method is used to draw the raised border around the tab control
2344 static void TAB_DrawBorder(const TAB_INFO
*infoPtr
, HDC hdc
)
2347 HTHEME theme
= GetWindowTheme (infoPtr
->hwnd
);
2349 GetClientRect (infoPtr
->hwnd
, &rect
);
2352 * Adjust for the style
2355 if (infoPtr
->uNumItem
)
2357 if ((infoPtr
->dwStyle
& TCS_BOTTOM
) && !(infoPtr
->dwStyle
& TCS_VERTICAL
))
2358 rect
.bottom
-= infoPtr
->tabHeight
* infoPtr
->uNumRows
+ CONTROL_BORDER_SIZEX
;
2359 else if((infoPtr
->dwStyle
& TCS_BOTTOM
) && (infoPtr
->dwStyle
& TCS_VERTICAL
))
2360 rect
.right
-= infoPtr
->tabHeight
* infoPtr
->uNumRows
+ CONTROL_BORDER_SIZEX
;
2361 else if(infoPtr
->dwStyle
& TCS_VERTICAL
)
2362 rect
.left
+= infoPtr
->tabHeight
* infoPtr
->uNumRows
+ CONTROL_BORDER_SIZEX
;
2363 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2364 rect
.top
+= infoPtr
->tabHeight
* infoPtr
->uNumRows
+ CONTROL_BORDER_SIZEX
;
2367 TRACE("border=(%s)\n", wine_dbgstr_rect(&rect
));
2370 DrawThemeBackground (theme
, hdc
, TABP_PANE
, 0, &rect
, NULL
);
2372 DrawEdge(hdc
, &rect
, EDGE_RAISED
, BF_SOFT
|BF_RECT
);
2375 /******************************************************************************
2378 * This method repaints the tab control..
2380 static void TAB_Refresh (const TAB_INFO
*infoPtr
, HDC hdc
)
2385 if (!infoPtr
->DoRedraw
)
2388 hOldFont
= SelectObject (hdc
, infoPtr
->hFont
);
2390 if (infoPtr
->dwStyle
& TCS_BUTTONS
)
2392 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
2393 TAB_DrawItem (infoPtr
, hdc
, i
);
2397 /* Draw all the non selected item first */
2398 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
2400 if (i
!= infoPtr
->iSelected
)
2401 TAB_DrawItem (infoPtr
, hdc
, i
);
2404 /* Now, draw the border, draw it before the selected item
2405 * since the selected item overwrites part of the border. */
2406 TAB_DrawBorder (infoPtr
, hdc
);
2408 /* Then, draw the selected item */
2409 TAB_DrawItem (infoPtr
, hdc
, infoPtr
->iSelected
);
2412 SelectObject (hdc
, hOldFont
);
2415 static inline DWORD
TAB_GetRowCount (const TAB_INFO
*infoPtr
)
2417 TRACE("(%p)\n", infoPtr
);
2418 return infoPtr
->uNumRows
;
2421 static inline LRESULT
TAB_SetRedraw (TAB_INFO
*infoPtr
, BOOL doRedraw
)
2423 infoPtr
->DoRedraw
= doRedraw
;
2427 /******************************************************************************
2428 * TAB_EnsureSelectionVisible
2430 * This method will make sure that the current selection is completely
2431 * visible by scrolling until it is.
2433 static void TAB_EnsureSelectionVisible(
2436 INT iSelected
= infoPtr
->iSelected
;
2437 INT iOrigLeftmostVisible
= infoPtr
->leftmostVisible
;
2439 /* set the items row to the bottommost row or topmost row depending on
2441 if ((infoPtr
->uNumRows
> 1) && !(infoPtr
->dwStyle
& TCS_BUTTONS
))
2443 TAB_ITEM
*selected
= TAB_GetItem(infoPtr
, iSelected
);
2447 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
2448 newselected
= selected
->rect
.left
;
2450 newselected
= selected
->rect
.top
;
2452 /* the target row is always (number of rows - 1)
2453 as row 0 is furthest from the clientRect */
2454 iTargetRow
= infoPtr
->uNumRows
- 1;
2456 if (newselected
!= iTargetRow
)
2459 if(infoPtr
->dwStyle
& TCS_VERTICAL
)
2461 for (i
=0; i
< infoPtr
->uNumItem
; i
++)
2463 /* move everything in the row of the selected item to the iTargetRow */
2464 TAB_ITEM
*item
= TAB_GetItem(infoPtr
, i
);
2466 if (item
->rect
.left
== newselected
)
2467 item
->rect
.left
= iTargetRow
;
2470 if (item
->rect
.left
> newselected
)
2477 for (i
=0; i
< infoPtr
->uNumItem
; i
++)
2479 TAB_ITEM
*item
= TAB_GetItem(infoPtr
, i
);
2481 if (item
->rect
.top
== newselected
)
2482 item
->rect
.top
= iTargetRow
;
2485 if (item
->rect
.top
> newselected
)
2490 TAB_RecalcHotTrack(infoPtr
, NULL
, NULL
, NULL
);
2495 * Do the trivial cases first.
2497 if ( (!infoPtr
->needsScrolling
) ||
2498 (infoPtr
->hwndUpDown
==0) || (infoPtr
->dwStyle
& TCS_VERTICAL
))
2501 if (infoPtr
->leftmostVisible
>= iSelected
)
2503 infoPtr
->leftmostVisible
= iSelected
;
2507 TAB_ITEM
*selected
= TAB_GetItem(infoPtr
, iSelected
);
2512 /* Calculate the part of the client area that is visible */
2513 GetClientRect(infoPtr
->hwnd
, &r
);
2516 GetClientRect(infoPtr
->hwndUpDown
, &r
);
2519 if ((selected
->rect
.right
-
2520 selected
->rect
.left
) >= width
)
2522 /* Special case: width of selected item is greater than visible
2525 infoPtr
->leftmostVisible
= iSelected
;
2529 for (i
= infoPtr
->leftmostVisible
; i
< infoPtr
->uNumItem
; i
++)
2531 if ((selected
->rect
.right
- TAB_GetItem(infoPtr
, i
)->rect
.left
) < width
)
2534 infoPtr
->leftmostVisible
= i
;
2538 if (infoPtr
->leftmostVisible
!= iOrigLeftmostVisible
)
2539 TAB_RecalcHotTrack(infoPtr
, NULL
, NULL
, NULL
);
2541 SendMessageW(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
2542 MAKELONG(infoPtr
->leftmostVisible
, 0));
2545 /******************************************************************************
2546 * TAB_InvalidateTabArea
2548 * This method will invalidate the portion of the control that contains the
2549 * tabs. It is called when the state of the control changes and needs
2552 static void TAB_InvalidateTabArea(const TAB_INFO
*infoPtr
)
2554 RECT clientRect
, rInvalidate
, rAdjClient
;
2555 INT lastRow
= infoPtr
->uNumRows
- 1;
2558 if (lastRow
< 0) return;
2560 GetClientRect(infoPtr
->hwnd
, &clientRect
);
2561 rInvalidate
= clientRect
;
2562 rAdjClient
= clientRect
;
2564 TAB_AdjustRect(infoPtr
, 0, &rAdjClient
);
2566 TAB_InternalGetItemRect(infoPtr
, infoPtr
->uNumItem
-1 , &rect
, NULL
);
2567 if ((infoPtr
->dwStyle
& TCS_BOTTOM
) && (infoPtr
->dwStyle
& TCS_VERTICAL
))
2569 rInvalidate
.left
= rAdjClient
.right
;
2570 if (infoPtr
->uNumRows
== 1)
2571 rInvalidate
.bottom
= clientRect
.top
+ rect
.bottom
+ 2 * SELECTED_TAB_OFFSET
;
2573 else if(infoPtr
->dwStyle
& TCS_VERTICAL
)
2575 rInvalidate
.right
= rAdjClient
.left
;
2576 if (infoPtr
->uNumRows
== 1)
2577 rInvalidate
.bottom
= clientRect
.top
+ rect
.bottom
+ 2 * SELECTED_TAB_OFFSET
;
2579 else if (infoPtr
->dwStyle
& TCS_BOTTOM
)
2581 rInvalidate
.top
= rAdjClient
.bottom
;
2582 if (infoPtr
->uNumRows
== 1)
2583 rInvalidate
.right
= clientRect
.left
+ rect
.right
+ 2 * SELECTED_TAB_OFFSET
;
2587 rInvalidate
.bottom
= rAdjClient
.top
;
2588 if (infoPtr
->uNumRows
== 1)
2589 rInvalidate
.right
= clientRect
.left
+ rect
.right
+ 2 * SELECTED_TAB_OFFSET
;
2592 /* Punch out the updown control */
2593 if (infoPtr
->needsScrolling
&& (rInvalidate
.right
> 0)) {
2595 GetClientRect(infoPtr
->hwndUpDown
, &r
);
2596 if (rInvalidate
.right
> clientRect
.right
- r
.left
)
2597 rInvalidate
.right
= rInvalidate
.right
- (r
.right
- r
.left
);
2599 rInvalidate
.right
= clientRect
.right
- r
.left
;
2602 TRACE("invalidate (%s)\n", wine_dbgstr_rect(&rInvalidate
));
2604 InvalidateRect(infoPtr
->hwnd
, &rInvalidate
, TRUE
);
2607 static inline LRESULT
TAB_Paint (TAB_INFO
*infoPtr
, HDC hdcPaint
)
2616 hdc
= BeginPaint (infoPtr
->hwnd
, &ps
);
2617 TRACE("erase %d, rect=(%s)\n", ps
.fErase
, wine_dbgstr_rect(&ps
.rcPaint
));
2620 TAB_Refresh (infoPtr
, hdc
);
2623 EndPaint (infoPtr
->hwnd
, &ps
);
2629 TAB_InsertItemT (TAB_INFO
*infoPtr
, INT iItem
, const TCITEMW
*pti
, BOOL bUnicode
)
2634 GetClientRect (infoPtr
->hwnd
, &rect
);
2635 TRACE("Rect: %p %s\n", infoPtr
->hwnd
, wine_dbgstr_rect(&rect
));
2637 if (iItem
< 0) return -1;
2638 if (iItem
> infoPtr
->uNumItem
)
2639 iItem
= infoPtr
->uNumItem
;
2641 TAB_DumpItemExternalT(pti
, iItem
, bUnicode
);
2644 if (infoPtr
->uNumItem
== 0) {
2645 infoPtr
->items
= Alloc (TAB_ITEM_SIZE(infoPtr
));
2646 infoPtr
->uNumItem
++;
2647 infoPtr
->iSelected
= 0;
2650 LPBYTE oldItems
= (LPBYTE
)infoPtr
->items
;
2652 infoPtr
->uNumItem
++;
2653 infoPtr
->items
= Alloc (TAB_ITEM_SIZE(infoPtr
) * infoPtr
->uNumItem
);
2655 /* pre insert copy */
2657 memcpy (infoPtr
->items
, oldItems
,
2658 iItem
* TAB_ITEM_SIZE(infoPtr
));
2661 /* post insert copy */
2662 if (iItem
< infoPtr
->uNumItem
- 1) {
2663 memcpy (TAB_GetItem(infoPtr
, iItem
+ 1),
2664 oldItems
+ iItem
* TAB_ITEM_SIZE(infoPtr
),
2665 (infoPtr
->uNumItem
- iItem
- 1) * TAB_ITEM_SIZE(infoPtr
));
2669 if (iItem
<= infoPtr
->iSelected
)
2670 infoPtr
->iSelected
++;
2675 item
= TAB_GetItem(infoPtr
, iItem
);
2677 item
->pszText
= NULL
;
2679 if (pti
->mask
& TCIF_TEXT
)
2682 Str_SetPtrW (&item
->pszText
, pti
->pszText
);
2684 Str_SetPtrAtoW (&item
->pszText
, (LPSTR
)pti
->pszText
);
2687 if (pti
->mask
& TCIF_IMAGE
)
2688 item
->iImage
= pti
->iImage
;
2692 if (pti
->mask
& TCIF_PARAM
)
2693 memcpy(item
->extra
, &pti
->lParam
, EXTRA_ITEM_SIZE(infoPtr
));
2695 memset(item
->extra
, 0, EXTRA_ITEM_SIZE(infoPtr
));
2697 TAB_SetItemBounds(infoPtr
);
2698 if (infoPtr
->uNumItem
> 1)
2699 TAB_InvalidateTabArea(infoPtr
);
2701 InvalidateRect(infoPtr
->hwnd
, NULL
, TRUE
);
2703 TRACE("[%p]: added item %d %s\n",
2704 infoPtr
->hwnd
, iItem
, debugstr_w(item
->pszText
));
2706 /* If we haven't set the current focus yet, set it now. */
2707 if (infoPtr
->uFocus
== -1)
2708 TAB_SetCurFocus(infoPtr
, iItem
);
2714 TAB_SetItemSize (TAB_INFO
*infoPtr
, INT cx
, INT cy
)
2717 BOOL bNeedPaint
= FALSE
;
2719 lResult
= MAKELONG(infoPtr
->tabWidth
, infoPtr
->tabHeight
);
2721 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2722 if (infoPtr
->dwStyle
& TCS_FIXEDWIDTH
&& (infoPtr
->tabWidth
!= cx
))
2724 infoPtr
->tabWidth
= cx
;
2728 if (infoPtr
->tabHeight
!= cy
)
2730 if ((infoPtr
->fHeightSet
= (cy
!= 0)))
2731 infoPtr
->tabHeight
= cy
;
2735 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2736 HIWORD(lResult
), LOWORD(lResult
),
2737 infoPtr
->tabHeight
, infoPtr
->tabWidth
);
2741 TAB_SetItemBounds(infoPtr
);
2742 RedrawWindow(infoPtr
->hwnd
, NULL
, NULL
, RDW_ERASE
| RDW_INVALIDATE
| RDW_UPDATENOW
);
2748 static inline LRESULT
TAB_SetMinTabWidth (TAB_INFO
*infoPtr
, INT cx
)
2752 TRACE("(%p,%d)\n", infoPtr
, cx
);
2754 if (infoPtr
->tabMinWidth
< 0)
2755 oldcx
= DEFAULT_MIN_TAB_WIDTH
;
2757 oldcx
= infoPtr
->tabMinWidth
;
2758 infoPtr
->tabMinWidth
= cx
;
2759 TAB_SetItemBounds(infoPtr
);
2763 static inline LRESULT
2764 TAB_HighlightItem (TAB_INFO
*infoPtr
, INT iItem
, BOOL fHighlight
)
2770 TRACE("(%p,%d,%s)\n", infoPtr
, iItem
, fHighlight
? "true" : "false");
2772 if (iItem
< 0 || iItem
>= infoPtr
->uNumItem
)
2775 lpState
= &TAB_GetItem(infoPtr
, iItem
)->dwState
;
2776 oldState
= *lpState
;
2779 *lpState
|= TCIS_HIGHLIGHTED
;
2781 *lpState
&= ~TCIS_HIGHLIGHTED
;
2783 if ((oldState
!= *lpState
) && TAB_InternalGetItemRect (infoPtr
, iItem
, &r
, NULL
))
2784 InvalidateRect (infoPtr
->hwnd
, &r
, TRUE
);
2790 TAB_SetItemT (TAB_INFO
*infoPtr
, INT iItem
, LPTCITEMW tabItem
, BOOL bUnicode
)
2794 TRACE("(%p,%d,%p,%s)\n", infoPtr
, iItem
, tabItem
, bUnicode
? "true" : "false");
2796 if (iItem
< 0 || iItem
>= infoPtr
->uNumItem
)
2799 TAB_DumpItemExternalT(tabItem
, iItem
, bUnicode
);
2801 wineItem
= TAB_GetItem(infoPtr
, iItem
);
2803 if (tabItem
->mask
& TCIF_IMAGE
)
2804 wineItem
->iImage
= tabItem
->iImage
;
2806 if (tabItem
->mask
& TCIF_PARAM
)
2807 memcpy(wineItem
->extra
, &tabItem
->lParam
, infoPtr
->cbInfo
);
2809 if (tabItem
->mask
& TCIF_RTLREADING
)
2810 FIXME("TCIF_RTLREADING\n");
2812 if (tabItem
->mask
& TCIF_STATE
)
2813 wineItem
->dwState
= (wineItem
->dwState
& ~tabItem
->dwStateMask
) |
2814 ( tabItem
->dwState
& tabItem
->dwStateMask
);
2816 if (tabItem
->mask
& TCIF_TEXT
)
2818 Free(wineItem
->pszText
);
2819 wineItem
->pszText
= NULL
;
2821 Str_SetPtrW(&wineItem
->pszText
, tabItem
->pszText
);
2823 Str_SetPtrAtoW(&wineItem
->pszText
, (LPSTR
)tabItem
->pszText
);
2826 /* Update and repaint tabs */
2827 TAB_SetItemBounds(infoPtr
);
2828 TAB_InvalidateTabArea(infoPtr
);
2833 static inline LRESULT
TAB_GetItemCount (const TAB_INFO
*infoPtr
)
2836 return infoPtr
->uNumItem
;
2841 TAB_GetItemT (TAB_INFO
*infoPtr
, INT iItem
, LPTCITEMW tabItem
, BOOL bUnicode
)
2845 TRACE("(%p,%d,%p,%s)\n", infoPtr
, iItem
, tabItem
, bUnicode
? "true" : "false");
2847 if (!tabItem
) return FALSE
;
2849 if (iItem
< 0 || iItem
>= infoPtr
->uNumItem
)
2851 /* init requested fields */
2852 if (tabItem
->mask
& TCIF_IMAGE
) tabItem
->iImage
= 0;
2853 if (tabItem
->mask
& TCIF_PARAM
) tabItem
->lParam
= 0;
2854 if (tabItem
->mask
& TCIF_STATE
) tabItem
->dwState
= 0;
2858 wineItem
= TAB_GetItem(infoPtr
, iItem
);
2860 if (tabItem
->mask
& TCIF_IMAGE
)
2861 tabItem
->iImage
= wineItem
->iImage
;
2863 if (tabItem
->mask
& TCIF_PARAM
)
2864 memcpy(&tabItem
->lParam
, wineItem
->extra
, infoPtr
->cbInfo
);
2866 if (tabItem
->mask
& TCIF_RTLREADING
)
2867 FIXME("TCIF_RTLREADING\n");
2869 if (tabItem
->mask
& TCIF_STATE
)
2870 tabItem
->dwState
= wineItem
->dwState
& tabItem
->dwStateMask
;
2872 if (tabItem
->mask
& TCIF_TEXT
)
2875 Str_GetPtrW (wineItem
->pszText
, tabItem
->pszText
, tabItem
->cchTextMax
);
2877 Str_GetPtrWtoA (wineItem
->pszText
, (LPSTR
)tabItem
->pszText
, tabItem
->cchTextMax
);
2880 TAB_DumpItemExternalT(tabItem
, iItem
, bUnicode
);
2886 static LRESULT
TAB_DeleteItem (TAB_INFO
*infoPtr
, INT iItem
)
2888 BOOL bResult
= FALSE
;
2890 TRACE("(%p, %d)\n", infoPtr
, iItem
);
2892 if ((iItem
>= 0) && (iItem
< infoPtr
->uNumItem
))
2894 TAB_ITEM
*item
= TAB_GetItem(infoPtr
, iItem
);
2895 LPBYTE oldItems
= (LPBYTE
)infoPtr
->items
;
2897 TAB_InvalidateTabArea(infoPtr
);
2898 Free(item
->pszText
);
2899 infoPtr
->uNumItem
--;
2901 if (!infoPtr
->uNumItem
)
2903 infoPtr
->items
= NULL
;
2904 if (infoPtr
->iHotTracked
>= 0)
2906 KillTimer(infoPtr
->hwnd
, TAB_HOTTRACK_TIMER
);
2907 infoPtr
->iHotTracked
= -1;
2912 infoPtr
->items
= Alloc(TAB_ITEM_SIZE(infoPtr
) * infoPtr
->uNumItem
);
2915 memcpy(infoPtr
->items
, oldItems
, iItem
* TAB_ITEM_SIZE(infoPtr
));
2917 if (iItem
< infoPtr
->uNumItem
)
2918 memcpy(TAB_GetItem(infoPtr
, iItem
),
2919 oldItems
+ (iItem
+ 1) * TAB_ITEM_SIZE(infoPtr
),
2920 (infoPtr
->uNumItem
- iItem
) * TAB_ITEM_SIZE(infoPtr
));
2922 if (iItem
<= infoPtr
->iHotTracked
)
2924 /* When tabs move left/up, the hot track item may change */
2925 FIXME("Recalc hot track\n");
2930 /* Readjust the selected index */
2931 if (iItem
== infoPtr
->iSelected
)
2932 infoPtr
->iSelected
= -1;
2933 else if (iItem
< infoPtr
->iSelected
)
2934 infoPtr
->iSelected
--;
2936 if (infoPtr
->uNumItem
== 0)
2937 infoPtr
->iSelected
= -1;
2939 /* Reposition and repaint tabs */
2940 TAB_SetItemBounds(infoPtr
);
2948 static inline LRESULT
TAB_DeleteAllItems (TAB_INFO
*infoPtr
)
2950 TRACE("(%p)\n", infoPtr
);
2951 while (infoPtr
->uNumItem
)
2952 TAB_DeleteItem (infoPtr
, 0);
2957 static inline LRESULT
TAB_GetFont (const TAB_INFO
*infoPtr
)
2959 TRACE("(%p) returning %p\n", infoPtr
, infoPtr
->hFont
);
2960 return (LRESULT
)infoPtr
->hFont
;
2963 static inline LRESULT
TAB_SetFont (TAB_INFO
*infoPtr
, HFONT hNewFont
)
2965 TRACE("(%p,%p)\n", infoPtr
, hNewFont
);
2967 infoPtr
->hFont
= hNewFont
;
2969 TAB_SetItemBounds(infoPtr
);
2971 TAB_InvalidateTabArea(infoPtr
);
2977 static inline LRESULT
TAB_GetImageList (const TAB_INFO
*infoPtr
)
2980 return (LRESULT
)infoPtr
->himl
;
2983 static inline LRESULT
TAB_SetImageList (TAB_INFO
*infoPtr
, HIMAGELIST himlNew
)
2985 HIMAGELIST himlPrev
= infoPtr
->himl
;
2986 TRACE("himl=%p\n", himlNew
);
2987 infoPtr
->himl
= himlNew
;
2988 TAB_SetItemBounds(infoPtr
);
2989 InvalidateRect(infoPtr
->hwnd
, NULL
, TRUE
);
2990 return (LRESULT
)himlPrev
;
2993 static inline LRESULT
TAB_GetUnicodeFormat (const TAB_INFO
*infoPtr
)
2995 TRACE("(%p)\n", infoPtr
);
2996 return infoPtr
->bUnicode
;
2999 static inline LRESULT
TAB_SetUnicodeFormat (TAB_INFO
*infoPtr
, BOOL bUnicode
)
3001 BOOL bTemp
= infoPtr
->bUnicode
;
3003 TRACE("(%p %d)\n", infoPtr
, bUnicode
);
3004 infoPtr
->bUnicode
= bUnicode
;
3009 static inline LRESULT
TAB_Size (TAB_INFO
*infoPtr
)
3011 /* I'm not really sure what the following code was meant to do.
3012 This is what it is doing:
3013 When WM_SIZE is sent with SIZE_RESTORED, the control
3014 gets positioned in the top left corner.
3018 UINT uPosFlags,cx,cy;
3022 parent = GetParent (hwnd);
3023 GetClientRect(parent, &parent_rect);
3026 if (GetWindowLongW(hwnd, GWL_STYLE) & CCS_NORESIZE)
3027 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
3029 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
3030 cx, cy, uPosFlags | SWP_NOZORDER);
3032 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
3035 /* Recompute the size/position of the tabs. */
3036 TAB_SetItemBounds (infoPtr
);
3038 /* Force a repaint of the control. */
3039 InvalidateRect(infoPtr
->hwnd
, NULL
, TRUE
);
3045 static LRESULT
TAB_Create (HWND hwnd
, LPARAM lParam
)
3048 TEXTMETRICW fontMetrics
;
3053 infoPtr
= Alloc (sizeof(TAB_INFO
));
3055 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
3057 infoPtr
->hwnd
= hwnd
;
3058 infoPtr
->hwndNotify
= ((LPCREATESTRUCTW
)lParam
)->hwndParent
;
3059 infoPtr
->uNumItem
= 0;
3060 infoPtr
->uNumRows
= 0;
3061 infoPtr
->uHItemPadding
= 6;
3062 infoPtr
->uVItemPadding
= 3;
3063 infoPtr
->uHItemPadding_s
= 6;
3064 infoPtr
->uVItemPadding_s
= 3;
3067 infoPtr
->hcurArrow
= LoadCursorW (0, (LPWSTR
)IDC_ARROW
);
3068 infoPtr
->iSelected
= -1;
3069 infoPtr
->iHotTracked
= -1;
3070 infoPtr
->uFocus
= -1;
3071 infoPtr
->hwndToolTip
= 0;
3072 infoPtr
->DoRedraw
= TRUE
;
3073 infoPtr
->needsScrolling
= FALSE
;
3074 infoPtr
->hwndUpDown
= 0;
3075 infoPtr
->leftmostVisible
= 0;
3076 infoPtr
->fHeightSet
= FALSE
;
3077 infoPtr
->bUnicode
= IsWindowUnicode (hwnd
);
3078 infoPtr
->cbInfo
= sizeof(LPARAM
);
3080 TRACE("Created tab control, hwnd [%p]\n", hwnd
);
3082 /* The tab control always has the WS_CLIPSIBLINGS style. Even
3083 if you don't specify it in CreateWindow. This is necessary in
3084 order for paint to work correctly. This follows windows behaviour. */
3085 dwStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
3086 SetWindowLongW(hwnd
, GWL_STYLE
, dwStyle
|WS_CLIPSIBLINGS
);
3088 infoPtr
->dwStyle
= dwStyle
| WS_CLIPSIBLINGS
;
3089 infoPtr
->exStyle
= (dwStyle
& TCS_FLATBUTTONS
) ? TCS_EX_FLATSEPARATORS
: 0;
3091 if (infoPtr
->dwStyle
& TCS_TOOLTIPS
) {
3092 /* Create tooltip control */
3093 infoPtr
->hwndToolTip
=
3094 CreateWindowExW (0, TOOLTIPS_CLASSW
, NULL
, WS_POPUP
,
3095 CW_USEDEFAULT
, CW_USEDEFAULT
,
3096 CW_USEDEFAULT
, CW_USEDEFAULT
,
3099 /* Send NM_TOOLTIPSCREATED notification */
3100 if (infoPtr
->hwndToolTip
) {
3101 NMTOOLTIPSCREATED nmttc
;
3103 nmttc
.hdr
.hwndFrom
= hwnd
;
3104 nmttc
.hdr
.idFrom
= GetWindowLongPtrW(hwnd
, GWLP_ID
);
3105 nmttc
.hdr
.code
= NM_TOOLTIPSCREATED
;
3106 nmttc
.hwndToolTips
= infoPtr
->hwndToolTip
;
3108 SendMessageW (infoPtr
->hwndNotify
, WM_NOTIFY
,
3109 GetWindowLongPtrW(hwnd
, GWLP_ID
), (LPARAM
)&nmttc
);
3113 OpenThemeData (infoPtr
->hwnd
, themeClass
);
3116 * We need to get text information so we need a DC and we need to select
3120 hOldFont
= SelectObject (hdc
, GetStockObject (SYSTEM_FONT
));
3122 /* Use the system font to determine the initial height of a tab. */
3123 GetTextMetricsW(hdc
, &fontMetrics
);
3126 * Make sure there is enough space for the letters + growing the
3127 * selected item + extra space for the selected item.
3129 infoPtr
->tabHeight
= fontMetrics
.tmHeight
+ SELECTED_TAB_OFFSET
+
3130 ((infoPtr
->dwStyle
& TCS_BUTTONS
) ? 2 : 1) *
3131 infoPtr
->uVItemPadding
;
3133 /* Initialize the width of a tab. */
3134 if (infoPtr
->dwStyle
& TCS_FIXEDWIDTH
)
3135 infoPtr
->tabWidth
= GetDeviceCaps(hdc
, LOGPIXELSX
);
3137 infoPtr
->tabMinWidth
= -1;
3139 TRACE("tabH=%d, tabW=%d\n", infoPtr
->tabHeight
, infoPtr
->tabWidth
);
3141 SelectObject (hdc
, hOldFont
);
3142 ReleaseDC(hwnd
, hdc
);
3148 TAB_Destroy (TAB_INFO
*infoPtr
)
3152 SetWindowLongPtrW(infoPtr
->hwnd
, 0, 0);
3154 if (infoPtr
->items
) {
3155 for (iItem
= 0; iItem
< infoPtr
->uNumItem
; iItem
++) {
3156 Free (TAB_GetItem(infoPtr
, iItem
)->pszText
);
3158 Free (infoPtr
->items
);
3161 if (infoPtr
->hwndToolTip
)
3162 DestroyWindow (infoPtr
->hwndToolTip
);
3164 if (infoPtr
->hwndUpDown
)
3165 DestroyWindow(infoPtr
->hwndUpDown
);
3167 if (infoPtr
->iHotTracked
>= 0)
3168 KillTimer(infoPtr
->hwnd
, TAB_HOTTRACK_TIMER
);
3170 CloseThemeData (GetWindowTheme (infoPtr
->hwnd
));
3176 /* update theme after a WM_THEMECHANGED message */
3177 static LRESULT
theme_changed(const TAB_INFO
*infoPtr
)
3179 HTHEME theme
= GetWindowTheme (infoPtr
->hwnd
);
3180 CloseThemeData (theme
);
3181 OpenThemeData (infoPtr
->hwnd
, themeClass
);
3185 static LRESULT
TAB_NCCalcSize(WPARAM wParam
)
3189 return WVR_ALIGNTOP
;
3192 static inline LRESULT
3193 TAB_SetItemExtra (TAB_INFO
*infoPtr
, INT cbInfo
)
3195 TRACE("(%p %d)\n", infoPtr
, cbInfo
);
3197 if (cbInfo
< 0 || infoPtr
->uNumItem
) return FALSE
;
3199 infoPtr
->cbInfo
= cbInfo
;
3203 static LRESULT
TAB_RemoveImage (TAB_INFO
*infoPtr
, INT image
)
3205 TRACE("%p %d\n", infoPtr
, image
);
3207 if (ImageList_Remove (infoPtr
->himl
, image
))
3212 /* shift indices, repaint items if needed */
3213 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
3215 idx
= &TAB_GetItem(infoPtr
, i
)->iImage
;
3224 if (TAB_InternalGetItemRect (infoPtr
, i
, &r
, NULL
))
3225 InvalidateRect (infoPtr
->hwnd
, &r
, TRUE
);
3234 TAB_SetExtendedStyle (TAB_INFO
*infoPtr
, DWORD exMask
, DWORD exStyle
)
3236 DWORD prevstyle
= infoPtr
->exStyle
;
3238 /* zero mask means all styles */
3239 if (exMask
== 0) exMask
= ~0;
3241 if (exMask
& TCS_EX_REGISTERDROP
)
3243 FIXME("TCS_EX_REGISTERDROP style unimplemented\n");
3244 exMask
&= ~TCS_EX_REGISTERDROP
;
3245 exStyle
&= ~TCS_EX_REGISTERDROP
;
3248 if (exMask
& TCS_EX_FLATSEPARATORS
)
3250 if ((prevstyle
^ exStyle
) & TCS_EX_FLATSEPARATORS
)
3252 infoPtr
->exStyle
^= TCS_EX_FLATSEPARATORS
;
3253 TAB_InvalidateTabArea(infoPtr
);
3260 static inline LRESULT
3261 TAB_GetExtendedStyle (const TAB_INFO
*infoPtr
)
3263 return infoPtr
->exStyle
;
3267 TAB_DeselectAll (TAB_INFO
*infoPtr
, BOOL excludesel
)
3270 INT i
, selected
= infoPtr
->iSelected
;
3272 TRACE("(%p, %d)\n", infoPtr
, excludesel
);
3274 if (!(infoPtr
->dwStyle
& TCS_BUTTONS
))
3277 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
3279 if ((TAB_GetItem(infoPtr
, i
)->dwState
& TCIS_BUTTONPRESSED
) &&
3282 TAB_GetItem(infoPtr
, i
)->dwState
&= ~TCIS_BUTTONPRESSED
;
3287 if (!excludesel
&& (selected
!= -1))
3289 TAB_GetItem(infoPtr
, selected
)->dwState
&= ~TCIS_BUTTONPRESSED
;
3290 infoPtr
->iSelected
= -1;
3295 TAB_InvalidateTabArea (infoPtr
);
3302 * Processes WM_STYLECHANGED messages.
3305 * [I] infoPtr : valid pointer to the tab data structure
3306 * [I] wStyleType : window style type (normal or extended)
3307 * [I] lpss : window style information
3312 static INT
TAB_StyleChanged(TAB_INFO
*infoPtr
, WPARAM wStyleType
,
3313 const STYLESTRUCT
*lpss
)
3315 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
3316 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
3318 if (wStyleType
!= GWL_STYLE
) return 0;
3320 infoPtr
->dwStyle
= lpss
->styleNew
;
3322 TAB_SetItemBounds (infoPtr
);
3323 InvalidateRect(infoPtr
->hwnd
, NULL
, TRUE
);
3328 static LRESULT WINAPI
3329 TAB_WindowProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
3331 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
3333 TRACE("hwnd=%p msg=%x wParam=%lx lParam=%lx\n", hwnd
, uMsg
, wParam
, lParam
);
3334 if (!infoPtr
&& (uMsg
!= WM_CREATE
))
3335 return DefWindowProcW (hwnd
, uMsg
, wParam
, lParam
);
3339 case TCM_GETIMAGELIST
:
3340 return TAB_GetImageList (infoPtr
);
3342 case TCM_SETIMAGELIST
:
3343 return TAB_SetImageList (infoPtr
, (HIMAGELIST
)lParam
);
3345 case TCM_GETITEMCOUNT
:
3346 return TAB_GetItemCount (infoPtr
);
3350 return TAB_GetItemT (infoPtr
, (INT
)wParam
, (LPTCITEMW
)lParam
, uMsg
== TCM_GETITEMW
);
3354 return TAB_SetItemT (infoPtr
, (INT
)wParam
, (LPTCITEMW
)lParam
, uMsg
== TCM_SETITEMW
);
3356 case TCM_DELETEITEM
:
3357 return TAB_DeleteItem (infoPtr
, (INT
)wParam
);
3359 case TCM_DELETEALLITEMS
:
3360 return TAB_DeleteAllItems (infoPtr
);
3362 case TCM_GETITEMRECT
:
3363 return TAB_GetItemRect (infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
3366 return TAB_GetCurSel (infoPtr
);
3369 return TAB_HitTest (infoPtr
, (LPTCHITTESTINFO
)lParam
);
3372 return TAB_SetCurSel (infoPtr
, (INT
)wParam
);
3374 case TCM_INSERTITEMA
:
3375 case TCM_INSERTITEMW
:
3376 return TAB_InsertItemT (infoPtr
, (INT
)wParam
, (TCITEMW
*)lParam
, uMsg
== TCM_INSERTITEMW
);
3378 case TCM_SETITEMEXTRA
:
3379 return TAB_SetItemExtra (infoPtr
, (INT
)wParam
);
3381 case TCM_ADJUSTRECT
:
3382 return TAB_AdjustRect (infoPtr
, (BOOL
)wParam
, (LPRECT
)lParam
);
3384 case TCM_SETITEMSIZE
:
3385 return TAB_SetItemSize (infoPtr
, (INT
)LOWORD(lParam
), (INT
)HIWORD(lParam
));
3387 case TCM_REMOVEIMAGE
:
3388 return TAB_RemoveImage (infoPtr
, (INT
)wParam
);
3390 case TCM_SETPADDING
:
3391 return TAB_SetPadding (infoPtr
, lParam
);
3393 case TCM_GETROWCOUNT
:
3394 return TAB_GetRowCount(infoPtr
);
3396 case TCM_GETUNICODEFORMAT
:
3397 return TAB_GetUnicodeFormat (infoPtr
);
3399 case TCM_SETUNICODEFORMAT
:
3400 return TAB_SetUnicodeFormat (infoPtr
, (BOOL
)wParam
);
3402 case TCM_HIGHLIGHTITEM
:
3403 return TAB_HighlightItem (infoPtr
, (INT
)wParam
, (BOOL
)LOWORD(lParam
));
3405 case TCM_GETTOOLTIPS
:
3406 return TAB_GetToolTips (infoPtr
);
3408 case TCM_SETTOOLTIPS
:
3409 return TAB_SetToolTips (infoPtr
, (HWND
)wParam
);
3411 case TCM_GETCURFOCUS
:
3412 return TAB_GetCurFocus (infoPtr
);
3414 case TCM_SETCURFOCUS
:
3415 return TAB_SetCurFocus (infoPtr
, (INT
)wParam
);
3417 case TCM_SETMINTABWIDTH
:
3418 return TAB_SetMinTabWidth(infoPtr
, (INT
)lParam
);
3420 case TCM_DESELECTALL
:
3421 return TAB_DeselectAll (infoPtr
, (BOOL
)wParam
);
3423 case TCM_GETEXTENDEDSTYLE
:
3424 return TAB_GetExtendedStyle (infoPtr
);
3426 case TCM_SETEXTENDEDSTYLE
:
3427 return TAB_SetExtendedStyle (infoPtr
, wParam
, lParam
);
3430 return TAB_GetFont (infoPtr
);
3433 return TAB_SetFont (infoPtr
, (HFONT
)wParam
);
3436 return TAB_Create (hwnd
, lParam
);
3439 return TAB_Destroy (infoPtr
);
3442 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
3444 case WM_LBUTTONDOWN
:
3445 return TAB_LButtonDown (infoPtr
, wParam
, lParam
);
3448 return TAB_LButtonUp (infoPtr
);
3451 return SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, wParam
, lParam
);
3453 case WM_RBUTTONDOWN
:
3454 return TAB_RButtonDown (infoPtr
);
3457 return TAB_MouseMove (infoPtr
, wParam
, lParam
);
3459 case WM_PRINTCLIENT
:
3461 return TAB_Paint (infoPtr
, (HDC
)wParam
);
3464 return TAB_Size (infoPtr
);
3467 return TAB_SetRedraw (infoPtr
, (BOOL
)wParam
);
3470 return TAB_OnHScroll(infoPtr
, (int)LOWORD(wParam
), (int)HIWORD(wParam
));
3472 case WM_STYLECHANGED
:
3473 return TAB_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
3475 case WM_SYSCOLORCHANGE
:
3476 COMCTL32_RefreshSysColors();
3479 case WM_THEMECHANGED
:
3480 return theme_changed (infoPtr
);
3483 TAB_KillFocus(infoPtr
);
3485 TAB_FocusChanging(infoPtr
);
3486 break; /* Don't disturb normal focus behavior */
3489 return TAB_KeyDown(infoPtr
, wParam
, lParam
);
3492 return TAB_NCHitTest(infoPtr
, lParam
);
3495 return TAB_NCCalcSize(wParam
);
3498 if (uMsg
>= WM_USER
&& uMsg
< WM_APP
&& !COMCTL32_IsReflectedMessage(uMsg
))
3499 WARN("unknown msg %04x wp=%08lx lp=%08lx\n",
3500 uMsg
, wParam
, lParam
);
3503 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
3512 ZeroMemory (&wndClass
, sizeof(WNDCLASSW
));
3513 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
;
3514 wndClass
.lpfnWndProc
= TAB_WindowProc
;
3515 wndClass
.cbClsExtra
= 0;
3516 wndClass
.cbWndExtra
= sizeof(TAB_INFO
*);
3517 wndClass
.hCursor
= LoadCursorW (0, (LPWSTR
)IDC_ARROW
);
3518 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_BTNFACE
+1);
3519 wndClass
.lpszClassName
= WC_TABCONTROLW
;
3521 RegisterClassW (&wndClass
);
3526 TAB_Unregister (void)
3528 UnregisterClassW (WC_TABCONTROLW
, NULL
);