4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
7 * Copyright 2003 Vitaliy Margolen
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 * TCM_GETEXTENDEDSTYLE
32 * TCM_SETEXTENDEDSTYLE
46 #include "wine/debug.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(tab
);
57 RECT rect
; /* bounding rectangle of the item relative to the
58 * leftmost item (the leftmost item, 0, would have a
59 * "left" member of 0 in this rectangle)
61 * additionally the top member holds the row number
62 * and bottom is unused and should be 0 */
63 BYTE extra
[1]; /* Space for caller supplied info, variable size */
66 /* The size of a tab item depends on how much extra data is requested */
67 #define TAB_ITEM_SIZE(infoPtr) (sizeof(TAB_ITEM) - sizeof(BYTE) + infoPtr->cbInfo)
71 HWND hwnd
; /* Tab control window */
72 HWND hwndNotify
; /* notification window (parent) */
73 UINT uNumItem
; /* number of tab items */
74 UINT uNumRows
; /* number of tab rows */
75 INT tabHeight
; /* height of the tab row */
76 INT tabWidth
; /* width of tabs */
77 INT tabMinWidth
; /* minimum width of items */
78 USHORT uHItemPadding
; /* amount of horizontal padding, in pixels */
79 USHORT uVItemPadding
; /* amount of vertical padding, in pixels */
80 USHORT uHItemPadding_s
; /* Set amount of horizontal padding, in pixels */
81 USHORT uVItemPadding_s
; /* Set amount of vertical padding, in pixels */
82 HFONT hFont
; /* handle to the current font */
83 HCURSOR hcurArrow
; /* handle to the current cursor */
84 HIMAGELIST himl
; /* handle to an image list (may be 0) */
85 HWND hwndToolTip
; /* handle to tab's tooltip */
86 INT leftmostVisible
; /* Used for scrolling, this member contains
87 * the index of the first visible item */
88 INT iSelected
; /* the currently selected item */
89 INT iHotTracked
; /* the highlighted item under the mouse */
90 INT uFocus
; /* item which has the focus */
91 TAB_ITEM
* items
; /* pointer to an array of TAB_ITEM's */
92 BOOL DoRedraw
; /* flag for redrawing when tab contents is changed*/
93 BOOL needsScrolling
; /* TRUE if the size of the tabs is greater than
94 * the size of the control */
95 BOOL fHeightSet
; /* was the height of the tabs explicitly set? */
96 BOOL bUnicode
; /* Unicode control? */
97 HWND hwndUpDown
; /* Updown control used for scrolling */
98 INT cbInfo
; /* Number of bytes of caller supplied info per tab */
101 /******************************************************************************
102 * Positioning constants
104 #define SELECTED_TAB_OFFSET 2
105 #define ROUND_CORNER_SIZE 2
106 #define DISPLAY_AREA_PADDINGX 2
107 #define DISPLAY_AREA_PADDINGY 2
108 #define CONTROL_BORDER_SIZEX 2
109 #define CONTROL_BORDER_SIZEY 2
110 #define BUTTON_SPACINGX 3
111 #define BUTTON_SPACINGY 3
112 #define FLAT_BTN_SPACINGX 8
113 #define DEFAULT_TAB_WIDTH 96
115 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongPtrW(hwnd,0))
116 /* Since items are variable sized, cannot directly access them */
117 #define TAB_GetItem(info,i) \
118 ((TAB_ITEM*)((LPBYTE)info->items + (i) * TAB_ITEM_SIZE(info)))
120 /******************************************************************************
121 * Hot-tracking timer constants
123 #define TAB_HOTTRACK_TIMER 1
124 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
126 /******************************************************************************
129 static void TAB_InvalidateTabArea(TAB_INFO
*);
130 static void TAB_EnsureSelectionVisible(TAB_INFO
*);
131 static void TAB_DrawItemInterior(TAB_INFO
*, HDC
, INT
, RECT
*);
134 TAB_SendSimpleNotify (const TAB_INFO
*infoPtr
, UINT code
)
138 nmhdr
.hwndFrom
= infoPtr
->hwnd
;
139 nmhdr
.idFrom
= GetWindowLongPtrW(infoPtr
->hwnd
, GWLP_ID
);
142 return (BOOL
) SendMessageW (infoPtr
->hwndNotify
, WM_NOTIFY
,
143 (WPARAM
) nmhdr
.idFrom
, (LPARAM
) &nmhdr
);
147 TAB_RelayEvent (HWND hwndTip
, HWND hwndMsg
, UINT uMsg
,
148 WPARAM wParam
, LPARAM lParam
)
156 msg
.time
= GetMessageTime ();
157 msg
.pt
.x
= LOWORD(GetMessagePos ());
158 msg
.pt
.y
= HIWORD(GetMessagePos ());
160 SendMessageW (hwndTip
, TTM_RELAYEVENT
, 0, (LPARAM
)&msg
);
164 TAB_DumpItemExternalT(TCITEMW
*pti
, UINT iItem
, BOOL isW
)
167 TRACE("external tab %d, mask=0x%08x, dwState=0x%08lx, dwStateMask=0x%08lx, cchTextMax=0x%08x\n",
168 iItem
, pti
->mask
, pti
->dwState
, pti
->dwStateMask
, pti
->cchTextMax
);
169 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
170 iItem
, pti
->iImage
, pti
->lParam
, isW
? debugstr_w(pti
->pszText
) : debugstr_a((LPSTR
)pti
->pszText
));
175 TAB_DumpItemInternal(TAB_INFO
*infoPtr
, UINT iItem
)
180 ti
= TAB_GetItem(infoPtr
, iItem
);
181 TRACE("tab %d, mask=0x%08x, dwState=0x%08lx, pszText=%s, iImage=%d\n",
182 iItem
, ti
->mask
, ti
->dwState
, debugstr_w(ti
->pszText
),
184 TRACE("tab %d, rect.left=%ld, rect.top(row)=%ld\n",
185 iItem
, ti
->rect
.left
, ti
->rect
.top
);
190 * the index of the selected tab, or -1 if no tab is selected. */
191 static inline LRESULT
TAB_GetCurSel (const TAB_INFO
*infoPtr
)
193 return infoPtr
->iSelected
;
197 * the index of the tab item that has the focus
199 * we have not to return negative value
201 * test for windows */
202 static inline LRESULT
203 TAB_GetCurFocus (const TAB_INFO
*infoPtr
)
205 if (infoPtr
->uFocus
<0)
207 FIXME("we have not to return negative value");
210 return infoPtr
->uFocus
;
213 static inline LRESULT
TAB_GetToolTips (const TAB_INFO
*infoPtr
)
215 if (infoPtr
== NULL
) return 0;
216 return (LRESULT
)infoPtr
->hwndToolTip
;
219 static inline LRESULT
TAB_SetCurSel (TAB_INFO
*infoPtr
, INT iItem
)
223 if (iItem
>= 0 && iItem
< infoPtr
->uNumItem
) {
224 prevItem
=infoPtr
->iSelected
;
225 infoPtr
->iSelected
=iItem
;
226 TAB_EnsureSelectionVisible(infoPtr
);
227 TAB_InvalidateTabArea(infoPtr
);
232 static LRESULT
TAB_SetCurFocus (TAB_INFO
*infoPtr
, INT iItem
)
234 if (iItem
< 0 || iItem
>= infoPtr
->uNumItem
) return 0;
236 if (GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
) & TCS_BUTTONS
) {
237 FIXME("Should set input focus\n");
239 int oldFocus
= infoPtr
->uFocus
;
240 if (infoPtr
->iSelected
!= iItem
|| oldFocus
== -1 ) {
241 infoPtr
->uFocus
= iItem
;
242 if (oldFocus
!= -1) {
243 if (!TAB_SendSimpleNotify(infoPtr
, TCN_SELCHANGING
)) {
244 infoPtr
->iSelected
= iItem
;
245 TAB_SendSimpleNotify(infoPtr
, TCN_SELCHANGE
);
248 infoPtr
->iSelected
= iItem
;
249 TAB_EnsureSelectionVisible(infoPtr
);
250 TAB_InvalidateTabArea(infoPtr
);
257 static inline LRESULT
258 TAB_SetToolTips (TAB_INFO
*infoPtr
, HWND hwndToolTip
)
261 infoPtr
->hwndToolTip
= hwndToolTip
;
265 static inline LRESULT
266 TAB_SetPadding (TAB_INFO
*infoPtr
, LPARAM lParam
)
270 infoPtr
->uHItemPadding_s
=LOWORD(lParam
);
271 infoPtr
->uVItemPadding_s
=HIWORD(lParam
);
276 /******************************************************************************
277 * TAB_InternalGetItemRect
279 * This method will calculate the rectangle representing a given tab item in
280 * client coordinates. This method takes scrolling into account.
282 * This method returns TRUE if the item is visible in the window and FALSE
283 * if it is completely outside the client area.
285 static BOOL
TAB_InternalGetItemRect(
286 const TAB_INFO
* infoPtr
,
291 RECT tmpItemRect
,clientRect
;
292 LONG lStyle
= GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
);
294 /* Perform a sanity check and a trivial visibility check. */
295 if ( (infoPtr
->uNumItem
<= 0) ||
296 (itemIndex
>= infoPtr
->uNumItem
) ||
297 (!((lStyle
& TCS_MULTILINE
) || (lStyle
& TCS_VERTICAL
)) && (itemIndex
< infoPtr
->leftmostVisible
)) )
301 * Avoid special cases in this procedure by assigning the "out"
302 * parameters if the caller didn't supply them
304 if (itemRect
== NULL
)
305 itemRect
= &tmpItemRect
;
307 /* Retrieve the unmodified item rect. */
308 *itemRect
= TAB_GetItem(infoPtr
,itemIndex
)->rect
;
310 /* calculate the times bottom and top based on the row */
311 GetClientRect(infoPtr
->hwnd
, &clientRect
);
313 if ((lStyle
& TCS_BOTTOM
) && (lStyle
& TCS_VERTICAL
))
315 itemRect
->right
= clientRect
.right
- SELECTED_TAB_OFFSET
- itemRect
->left
* infoPtr
->tabHeight
-
316 ((lStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGX
: 0);
317 itemRect
->left
= itemRect
->right
- infoPtr
->tabHeight
;
319 else if (lStyle
& TCS_VERTICAL
)
321 itemRect
->left
= clientRect
.left
+ SELECTED_TAB_OFFSET
+ itemRect
->left
* infoPtr
->tabHeight
+
322 ((lStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGX
: 0);
323 itemRect
->right
= itemRect
->left
+ infoPtr
->tabHeight
;
325 else if (lStyle
& TCS_BOTTOM
)
327 itemRect
->bottom
= clientRect
.bottom
- itemRect
->top
* infoPtr
->tabHeight
-
328 ((lStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: SELECTED_TAB_OFFSET
);
329 itemRect
->top
= itemRect
->bottom
- infoPtr
->tabHeight
;
331 else /* not TCS_BOTTOM and not TCS_VERTICAL */
333 itemRect
->top
= clientRect
.top
+ itemRect
->top
* infoPtr
->tabHeight
+
334 ((lStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: SELECTED_TAB_OFFSET
);
335 itemRect
->bottom
= itemRect
->top
+ infoPtr
->tabHeight
;
339 * "scroll" it to make sure the item at the very left of the
340 * tab control is the leftmost visible tab.
342 if(lStyle
& TCS_VERTICAL
)
346 -TAB_GetItem(infoPtr
, infoPtr
->leftmostVisible
)->rect
.top
);
349 * Move the rectangle so the first item is slightly offset from
350 * the bottom of the tab control.
354 SELECTED_TAB_OFFSET
);
359 -TAB_GetItem(infoPtr
, infoPtr
->leftmostVisible
)->rect
.left
,
363 * Move the rectangle so the first item is slightly offset from
364 * the left of the tab control.
370 TRACE("item %d tab h=%d, rect=(%ld,%ld)-(%ld,%ld)\n",
371 itemIndex
, infoPtr
->tabHeight
,
372 itemRect
->left
, itemRect
->top
, itemRect
->right
, itemRect
->bottom
);
374 /* Now, calculate the position of the item as if it were selected. */
375 if (selectedRect
!=NULL
)
377 CopyRect(selectedRect
, itemRect
);
379 /* The rectangle of a selected item is a bit wider. */
380 if(lStyle
& TCS_VERTICAL
)
381 InflateRect(selectedRect
, 0, SELECTED_TAB_OFFSET
);
383 InflateRect(selectedRect
, SELECTED_TAB_OFFSET
, 0);
385 /* If it also a bit higher. */
386 if ((lStyle
& TCS_BOTTOM
) && (lStyle
& TCS_VERTICAL
))
388 selectedRect
->left
-= 2; /* the border is thicker on the right */
389 selectedRect
->right
+= SELECTED_TAB_OFFSET
;
391 else if (lStyle
& TCS_VERTICAL
)
393 selectedRect
->left
-= SELECTED_TAB_OFFSET
;
394 selectedRect
->right
+= 1;
396 else if (lStyle
& TCS_BOTTOM
)
398 selectedRect
->bottom
+= SELECTED_TAB_OFFSET
;
400 else /* not TCS_BOTTOM and not TCS_VERTICAL */
402 selectedRect
->top
-= SELECTED_TAB_OFFSET
;
403 selectedRect
->bottom
-= 1;
407 /* Check for visibility */
408 if (lStyle
& TCS_VERTICAL
)
409 return (itemRect
->top
< clientRect
.bottom
) && (itemRect
->bottom
> clientRect
.top
);
411 return (itemRect
->left
< clientRect
.right
) && (itemRect
->right
> clientRect
.left
);
415 TAB_GetItemRect(TAB_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
417 return TAB_InternalGetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
, (LPRECT
)NULL
);
420 /******************************************************************************
423 * This method is called to handle keyboard input
425 static LRESULT
TAB_KeyUp(TAB_INFO
* infoPtr
, WPARAM keyCode
)
432 newItem
= infoPtr
->uFocus
- 1;
435 newItem
= infoPtr
->uFocus
+ 1;
440 * If we changed to a valid item, change the selection
443 newItem
< infoPtr
->uNumItem
&&
444 infoPtr
->uFocus
!= newItem
)
446 if (!TAB_SendSimpleNotify(infoPtr
, TCN_SELCHANGING
))
448 infoPtr
->iSelected
= newItem
;
449 infoPtr
->uFocus
= newItem
;
450 TAB_SendSimpleNotify(infoPtr
, TCN_SELCHANGE
);
452 TAB_EnsureSelectionVisible(infoPtr
);
453 TAB_InvalidateTabArea(infoPtr
);
460 /******************************************************************************
463 * This method is called whenever the focus goes in or out of this control
464 * it is used to update the visual state of the control.
466 static void TAB_FocusChanging(const TAB_INFO
*infoPtr
)
472 * Get the rectangle for the item.
474 isVisible
= TAB_InternalGetItemRect(infoPtr
,
480 * If the rectangle is not completely invisible, invalidate that
481 * portion of the window.
485 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
486 selectedRect
.left
,selectedRect
.top
,
487 selectedRect
.right
,selectedRect
.bottom
);
488 InvalidateRect(infoPtr
->hwnd
, &selectedRect
, TRUE
);
492 static INT
TAB_InternalHitTest (
501 for (iCount
= 0; iCount
< infoPtr
->uNumItem
; iCount
++)
503 TAB_InternalGetItemRect(infoPtr
, iCount
, &rect
, NULL
);
505 if (PtInRect(&rect
, pt
))
507 *flags
= TCHT_ONITEM
;
512 *flags
= TCHT_NOWHERE
;
516 static inline LRESULT
517 TAB_HitTest (TAB_INFO
*infoPtr
, LPTCHITTESTINFO lptest
)
519 return TAB_InternalHitTest (infoPtr
, lptest
->pt
, &lptest
->flags
);
522 /******************************************************************************
525 * Napster v2b5 has a tab control for its main navigation which has a client
526 * area that covers the whole area of the dialog pages.
527 * That's why it receives all msgs for that area and the underlying dialog ctrls
529 * So I decided that we should handle WM_NCHITTEST here and return
530 * HTTRANSPARENT if we don't hit the tab control buttons.
531 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
532 * doesn't do it that way. Maybe depends on tab control styles ?
534 static inline LRESULT
535 TAB_NCHitTest (TAB_INFO
*infoPtr
, LPARAM lParam
)
540 pt
.x
= LOWORD(lParam
);
541 pt
.y
= HIWORD(lParam
);
542 ScreenToClient(infoPtr
->hwnd
, &pt
);
544 if (TAB_InternalHitTest(infoPtr
, pt
, &dummyflag
) == -1)
545 return HTTRANSPARENT
;
551 TAB_LButtonDown (TAB_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
556 if (infoPtr
->hwndToolTip
)
557 TAB_RelayEvent (infoPtr
->hwndToolTip
, infoPtr
->hwnd
,
558 WM_LBUTTONDOWN
, wParam
, lParam
);
560 if (GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
) & TCS_FOCUSONBUTTONDOWN
) {
561 SetFocus (infoPtr
->hwnd
);
564 if (infoPtr
->hwndToolTip
)
565 TAB_RelayEvent (infoPtr
->hwndToolTip
, infoPtr
->hwnd
,
566 WM_LBUTTONDOWN
, wParam
, lParam
);
568 pt
.x
= (INT
)LOWORD(lParam
);
569 pt
.y
= (INT
)HIWORD(lParam
);
571 newItem
= TAB_InternalHitTest (infoPtr
, pt
, &dummy
);
573 TRACE("On Tab, item %d\n", newItem
);
575 if (newItem
!= -1 && infoPtr
->iSelected
!= newItem
)
577 if (!TAB_SendSimpleNotify(infoPtr
, TCN_SELCHANGING
))
579 infoPtr
->iSelected
= newItem
;
580 infoPtr
->uFocus
= newItem
;
581 TAB_SendSimpleNotify(infoPtr
, TCN_SELCHANGE
);
583 TAB_EnsureSelectionVisible(infoPtr
);
585 TAB_InvalidateTabArea(infoPtr
);
591 static inline LRESULT
592 TAB_LButtonUp (const TAB_INFO
*infoPtr
)
594 TAB_SendSimpleNotify(infoPtr
, NM_CLICK
);
599 static inline LRESULT
600 TAB_RButtonDown (const TAB_INFO
*infoPtr
)
602 TAB_SendSimpleNotify(infoPtr
, NM_RCLICK
);
606 /******************************************************************************
607 * TAB_DrawLoneItemInterior
609 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
610 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
611 * up the device context and font. This routine does the same setup but
612 * only calls TAB_DrawItemInterior for the single specified item.
615 TAB_DrawLoneItemInterior(TAB_INFO
* infoPtr
, int iItem
)
617 HDC hdc
= GetDC(infoPtr
->hwnd
);
620 /* Clip UpDown control to not draw over it */
621 if (infoPtr
->needsScrolling
)
623 GetWindowRect(infoPtr
->hwnd
, &rC
);
624 GetWindowRect(infoPtr
->hwndUpDown
, &r
);
625 ExcludeClipRect(hdc
, r
.left
- rC
.left
, r
.top
- rC
.top
, r
.right
- rC
.left
, r
.bottom
- rC
.top
);
627 TAB_DrawItemInterior(infoPtr
, hdc
, iItem
, NULL
);
628 ReleaseDC(infoPtr
->hwnd
, hdc
);
631 /******************************************************************************
632 * TAB_HotTrackTimerProc
634 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
635 * timer is setup so we can check if the mouse is moved out of our window.
636 * (We don't get an event when the mouse leaves, the mouse-move events just
637 * stop being delivered to our window and just start being delivered to
638 * another window.) This function is called when the timer triggers so
639 * we can check if the mouse has left our window. If so, we un-highlight
640 * the hot-tracked tab.
643 TAB_HotTrackTimerProc
645 HWND hwnd
, /* handle of window for timer messages */
646 UINT uMsg
, /* WM_TIMER message */
647 UINT idEvent
, /* timer identifier */
648 DWORD dwTime
/* current system time */
651 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
653 if (infoPtr
!= NULL
&& infoPtr
->iHotTracked
>= 0)
658 ** If we can't get the cursor position, or if the cursor is outside our
659 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
660 ** "outside" even if it is within our bounding rect if another window
661 ** overlaps. Note also that the case where the cursor stayed within our
662 ** window but has moved off the hot-tracked tab will be handled by the
663 ** WM_MOUSEMOVE event.
665 if (!GetCursorPos(&pt
) || WindowFromPoint(pt
) != hwnd
)
667 /* Redraw iHotTracked to look normal */
668 INT iRedraw
= infoPtr
->iHotTracked
;
669 infoPtr
->iHotTracked
= -1;
670 TAB_DrawLoneItemInterior(infoPtr
, iRedraw
);
672 /* Kill this timer */
673 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
678 /******************************************************************************
681 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
682 * should be highlighted. This function determines which tab in a tab control,
683 * if any, is under the mouse and records that information. The caller may
684 * supply output parameters to receive the item number of the tab item which
685 * was highlighted but isn't any longer and of the tab item which is now
686 * highlighted but wasn't previously. The caller can use this information to
687 * selectively redraw those tab items.
689 * If the caller has a mouse position, it can supply it through the pos
690 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
691 * supplies NULL and this function determines the current mouse position
699 int* out_redrawLeave
,
706 if (out_redrawLeave
!= NULL
)
707 *out_redrawLeave
= -1;
708 if (out_redrawEnter
!= NULL
)
709 *out_redrawEnter
= -1;
711 if (GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
) & TCS_HOTTRACK
)
719 ScreenToClient(infoPtr
->hwnd
, &pt
);
727 item
= TAB_InternalHitTest(infoPtr
, pt
, &flags
);
730 if (item
!= infoPtr
->iHotTracked
)
732 if (infoPtr
->iHotTracked
>= 0)
734 /* Mark currently hot-tracked to be redrawn to look normal */
735 if (out_redrawLeave
!= NULL
)
736 *out_redrawLeave
= infoPtr
->iHotTracked
;
740 /* Kill timer which forces recheck of mouse pos */
741 KillTimer(infoPtr
->hwnd
, TAB_HOTTRACK_TIMER
);
746 /* Start timer so we recheck mouse pos */
747 UINT timerID
= SetTimer
751 TAB_HOTTRACK_TIMER_INTERVAL
,
752 TAB_HotTrackTimerProc
756 return; /* Hot tracking not available */
759 infoPtr
->iHotTracked
= item
;
763 /* Mark new hot-tracked to be redrawn to look highlighted */
764 if (out_redrawEnter
!= NULL
)
765 *out_redrawEnter
= item
;
770 /******************************************************************************
773 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
776 TAB_MouseMove (TAB_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
781 if (infoPtr
->hwndToolTip
)
782 TAB_RelayEvent (infoPtr
->hwndToolTip
, infoPtr
->hwnd
,
783 WM_LBUTTONDOWN
, wParam
, lParam
);
785 /* Determine which tab to highlight. Redraw tabs which change highlight
787 TAB_RecalcHotTrack(infoPtr
, &lParam
, &redrawLeave
, &redrawEnter
);
789 if (redrawLeave
!= -1)
790 TAB_DrawLoneItemInterior(infoPtr
, redrawLeave
);
791 if (redrawEnter
!= -1)
792 TAB_DrawLoneItemInterior(infoPtr
, redrawEnter
);
797 /******************************************************************************
800 * Calculates the tab control's display area given the window rectangle or
801 * the window rectangle given the requested display rectangle.
803 static LRESULT
TAB_AdjustRect(
808 DWORD lStyle
= GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
);
809 LONG
*iRightBottom
, *iLeftTop
;
811 TRACE ("hwnd=%p fLarger=%d (%ld,%ld)-(%ld,%ld)\n", infoPtr
->hwnd
, fLarger
, prc
->left
, prc
->top
, prc
->right
, prc
->bottom
);
813 if(lStyle
& TCS_VERTICAL
)
815 iRightBottom
= &(prc
->right
);
816 iLeftTop
= &(prc
->left
);
820 iRightBottom
= &(prc
->bottom
);
821 iLeftTop
= &(prc
->top
);
824 if (fLarger
) /* Go from display rectangle */
826 /* Add the height of the tabs. */
827 if (lStyle
& TCS_BOTTOM
)
828 *iRightBottom
+= infoPtr
->tabHeight
* infoPtr
->uNumRows
;
830 *iLeftTop
-= infoPtr
->tabHeight
* infoPtr
->uNumRows
+
831 ((lStyle
& TCS_BUTTONS
)? 3 * (infoPtr
->uNumRows
- 1) : 0);
833 /* Inflate the rectangle for the padding */
834 InflateRect(prc
, DISPLAY_AREA_PADDINGX
, DISPLAY_AREA_PADDINGY
);
836 /* Inflate for the border */
837 InflateRect(prc
, CONTROL_BORDER_SIZEX
, CONTROL_BORDER_SIZEY
);
839 else /* Go from window rectangle. */
841 /* Deflate the rectangle for the border */
842 InflateRect(prc
, -CONTROL_BORDER_SIZEX
, -CONTROL_BORDER_SIZEY
);
844 /* Deflate the rectangle for the padding */
845 InflateRect(prc
, -DISPLAY_AREA_PADDINGX
, -DISPLAY_AREA_PADDINGY
);
847 /* Remove the height of the tabs. */
848 if (lStyle
& TCS_BOTTOM
)
849 *iRightBottom
-= infoPtr
->tabHeight
* infoPtr
->uNumRows
;
851 *iLeftTop
+= (infoPtr
->tabHeight
) * infoPtr
->uNumRows
+
852 ((lStyle
& TCS_BUTTONS
)? 3 * (infoPtr
->uNumRows
- 1) : 0);
858 /******************************************************************************
861 * This method will handle the notification from the scroll control and
862 * perform the scrolling operation on the tab control.
864 static LRESULT
TAB_OnHScroll(
870 if(nScrollCode
== SB_THUMBPOSITION
&& nPos
!= infoPtr
->leftmostVisible
)
872 if(nPos
< infoPtr
->leftmostVisible
)
873 infoPtr
->leftmostVisible
--;
875 infoPtr
->leftmostVisible
++;
877 TAB_RecalcHotTrack(infoPtr
, NULL
, NULL
, NULL
);
878 TAB_InvalidateTabArea(infoPtr
);
879 SendMessageW(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
880 MAKELONG(infoPtr
->leftmostVisible
, 0));
886 /******************************************************************************
889 * This method will check the current scrolling state and make sure the
890 * scrolling control is displayed (or not).
892 static void TAB_SetupScrolling(
895 const RECT
* clientRect
)
897 static const WCHAR msctls_updown32W
[] = { 'm','s','c','t','l','s','_','u','p','d','o','w','n','3','2',0 };
898 static const WCHAR emptyW
[] = { 0 };
900 DWORD lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
902 if (infoPtr
->needsScrolling
)
908 * Calculate the position of the scroll control.
910 if(lStyle
& TCS_VERTICAL
)
912 controlPos
.right
= clientRect
->right
;
913 controlPos
.left
= controlPos
.right
- 2 * GetSystemMetrics(SM_CXHSCROLL
);
915 if (lStyle
& TCS_BOTTOM
)
917 controlPos
.top
= clientRect
->bottom
- infoPtr
->tabHeight
;
918 controlPos
.bottom
= controlPos
.top
+ GetSystemMetrics(SM_CYHSCROLL
);
922 controlPos
.bottom
= clientRect
->top
+ infoPtr
->tabHeight
;
923 controlPos
.top
= controlPos
.bottom
- GetSystemMetrics(SM_CYHSCROLL
);
928 controlPos
.right
= clientRect
->right
;
929 controlPos
.left
= controlPos
.right
- 2 * GetSystemMetrics(SM_CXHSCROLL
);
931 if (lStyle
& TCS_BOTTOM
)
933 controlPos
.top
= clientRect
->bottom
- infoPtr
->tabHeight
;
934 controlPos
.bottom
= controlPos
.top
+ GetSystemMetrics(SM_CYHSCROLL
);
938 controlPos
.bottom
= clientRect
->top
+ infoPtr
->tabHeight
;
939 controlPos
.top
= controlPos
.bottom
- GetSystemMetrics(SM_CYHSCROLL
);
944 * If we don't have a scroll control yet, we want to create one.
945 * If we have one, we want to make sure it's positioned properly.
947 if (infoPtr
->hwndUpDown
==0)
949 infoPtr
->hwndUpDown
= CreateWindowW(msctls_updown32W
, emptyW
,
950 WS_VISIBLE
| WS_CHILD
| UDS_HORZ
,
951 controlPos
.left
, controlPos
.top
,
952 controlPos
.right
- controlPos
.left
,
953 controlPos
.bottom
- controlPos
.top
,
954 hwnd
, NULL
, NULL
, NULL
);
958 SetWindowPos(infoPtr
->hwndUpDown
,
960 controlPos
.left
, controlPos
.top
,
961 controlPos
.right
- controlPos
.left
,
962 controlPos
.bottom
- controlPos
.top
,
963 SWP_SHOWWINDOW
| SWP_NOZORDER
);
966 /* Now calculate upper limit of the updown control range.
967 * We do this by calculating how many tabs will be offscreen when the
968 * last tab is visible.
970 if(infoPtr
->uNumItem
)
972 vsize
= clientRect
->right
- (controlPos
.right
- controlPos
.left
+ 1);
973 maxRange
= infoPtr
->uNumItem
;
974 tabwidth
= TAB_GetItem(infoPtr
, infoPtr
->uNumItem
- 1)->rect
.right
;
976 for(; maxRange
> 0; maxRange
--)
978 if(tabwidth
- TAB_GetItem(infoPtr
,maxRange
- 1)->rect
.left
> vsize
)
982 if(maxRange
== infoPtr
->uNumItem
)
988 /* If we once had a scroll control... hide it */
989 if (infoPtr
->hwndUpDown
!=0)
990 ShowWindow(infoPtr
->hwndUpDown
, SW_HIDE
);
992 if (infoPtr
->hwndUpDown
)
993 SendMessageW(infoPtr
->hwndUpDown
, UDM_SETRANGE32
, 0, maxRange
);
996 /******************************************************************************
999 * This method will calculate the position rectangles of all the items in the
1000 * control. The rectangle calculated starts at 0 for the first item in the
1001 * list and ignores scrolling and selection.
1002 * It also uses the current font to determine the height of the tab row and
1003 * it checks if all the tabs fit in the client area of the window. If they
1004 * don't, a scrolling control is added.
1006 static void TAB_SetItemBounds (TAB_INFO
*infoPtr
)
1008 LONG lStyle
= GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
);
1009 TEXTMETRICW fontMetrics
;
1012 INT curItemRowCount
;
1013 HFONT hFont
, hOldFont
;
1023 * We need to get text information so we need a DC and we need to select
1026 hdc
= GetDC(infoPtr
->hwnd
);
1028 hFont
= infoPtr
->hFont
? infoPtr
->hFont
: GetStockObject (SYSTEM_FONT
);
1029 hOldFont
= SelectObject (hdc
, hFont
);
1032 * We will base the rectangle calculations on the client rectangle
1035 GetClientRect(infoPtr
->hwnd
, &clientRect
);
1037 /* if TCS_VERTICAL then swap the height and width so this code places the
1038 tabs along the top of the rectangle and we can just rotate them after
1039 rather than duplicate all of the below code */
1040 if(lStyle
& TCS_VERTICAL
)
1042 iTemp
= clientRect
.bottom
;
1043 clientRect
.bottom
= clientRect
.right
;
1044 clientRect
.right
= iTemp
;
1047 /* Now use hPadding and vPadding */
1048 infoPtr
->uHItemPadding
= infoPtr
->uHItemPadding_s
;
1049 infoPtr
->uVItemPadding
= infoPtr
->uVItemPadding_s
;
1051 /* The leftmost item will be "0" aligned */
1053 curItemRowCount
= infoPtr
->uNumItem
? 1 : 0;
1055 if (!(infoPtr
->fHeightSet
))
1058 int icon_height
= 0;
1060 /* Use the current font to determine the height of a tab. */
1061 GetTextMetricsW(hdc
, &fontMetrics
);
1063 /* Get the icon height */
1065 ImageList_GetIconSize(infoPtr
->himl
, 0, &icon_height
);
1067 /* Take the highest between font or icon */
1068 if (fontMetrics
.tmHeight
> icon_height
)
1069 item_height
= fontMetrics
.tmHeight
+ 2;
1071 item_height
= icon_height
;
1074 * Make sure there is enough space for the letters + icon + growing the
1075 * selected item + extra space for the selected item.
1077 infoPtr
->tabHeight
= item_height
+
1078 ((lStyle
& TCS_BUTTONS
) ? 2 : 1) *
1079 infoPtr
->uVItemPadding
;
1081 TRACE("tabH=%d, tmH=%ld, iconh=%d\n",
1082 infoPtr
->tabHeight
, fontMetrics
.tmHeight
, icon_height
);
1085 TRACE("client right=%ld\n", clientRect
.right
);
1087 /* Get the icon width */
1090 ImageList_GetIconSize(infoPtr
->himl
, &icon_width
, 0);
1092 if (lStyle
& TCS_FIXEDWIDTH
)
1095 /* Add padding if icon is present */
1096 icon_width
+= infoPtr
->uHItemPadding
;
1099 for (curItem
= 0; curItem
< infoPtr
->uNumItem
; curItem
++)
1101 TAB_ITEM
*curr
= TAB_GetItem(infoPtr
, curItem
);
1103 /* Set the leftmost position of the tab. */
1104 curr
->rect
.left
= curItemLeftPos
;
1106 if ((lStyle
& TCS_FIXEDWIDTH
) || !curr
->pszText
)
1108 curr
->rect
.right
= curr
->rect
.left
+
1109 max(infoPtr
->tabWidth
, icon_width
);
1115 /* Calculate how wide the tab is depending on the text it contains */
1116 GetTextExtentPoint32W(hdc
, curr
->pszText
,
1117 lstrlenW(curr
->pszText
), &size
);
1119 curr
->rect
.right
= curr
->rect
.left
+ size
.cx
+ icon_width
+
1120 num
* infoPtr
->uHItemPadding
;
1121 TRACE("for <%s>, l,r=%ld,%ld, num=%d\n",
1122 debugstr_w(curr
->pszText
), curr
->rect
.left
, curr
->rect
.right
, num
);
1126 * Check if this is a multiline tab control and if so
1127 * check to see if we should wrap the tabs
1129 * Wrap all these tabs. We will arrange them evenly later.
1133 if (((lStyle
& TCS_MULTILINE
) || (lStyle
& TCS_VERTICAL
)) &&
1135 (clientRect
.right
- CONTROL_BORDER_SIZEX
- DISPLAY_AREA_PADDINGX
)))
1137 curr
->rect
.right
-= curr
->rect
.left
;
1139 curr
->rect
.left
= 0;
1141 TRACE("wrapping <%s>, l,r=%ld,%ld\n", debugstr_w(curr
->pszText
),
1142 curr
->rect
.left
, curr
->rect
.right
);
1145 curr
->rect
.bottom
= 0;
1146 curr
->rect
.top
= curItemRowCount
- 1;
1148 TRACE("TextSize: %li\n", size
.cx
);
1149 TRACE("Rect: T %li, L %li, B %li, R %li\n", curr
->rect
.top
,
1150 curr
->rect
.left
, curr
->rect
.bottom
, curr
->rect
.right
);
1153 * The leftmost position of the next item is the rightmost position
1156 if (lStyle
& TCS_BUTTONS
)
1158 curItemLeftPos
= curr
->rect
.right
+ BUTTON_SPACINGX
;
1159 if (lStyle
& TCS_FLATBUTTONS
)
1160 curItemLeftPos
+= FLAT_BTN_SPACINGX
;
1163 curItemLeftPos
= curr
->rect
.right
;
1166 if (!((lStyle
& TCS_MULTILINE
) || (lStyle
& TCS_VERTICAL
)))
1169 * Check if we need a scrolling control.
1171 infoPtr
->needsScrolling
= (curItemLeftPos
+ (2 * SELECTED_TAB_OFFSET
) >
1174 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1175 if(!infoPtr
->needsScrolling
)
1176 infoPtr
->leftmostVisible
= 0;
1181 * No scrolling in Multiline or Vertical styles.
1183 infoPtr
->needsScrolling
= FALSE
;
1184 infoPtr
->leftmostVisible
= 0;
1186 TAB_SetupScrolling(infoPtr
->hwnd
, infoPtr
, &clientRect
);
1188 /* Set the number of rows */
1189 infoPtr
->uNumRows
= curItemRowCount
;
1191 /* Arrange all tabs evenly if style says so */
1192 if (!(lStyle
& TCS_RAGGEDRIGHT
) && ((lStyle
& TCS_MULTILINE
) || (lStyle
& TCS_VERTICAL
)) && (infoPtr
->uNumItem
> 0))
1194 INT tabPerRow
,remTab
,iRow
;
1199 * Ok windows tries to even out the rows. place the same
1200 * number of tabs in each row. So lets give that a shot
1203 tabPerRow
= infoPtr
->uNumItem
/ (infoPtr
->uNumRows
);
1204 remTab
= infoPtr
->uNumItem
% (infoPtr
->uNumRows
);
1206 for (iItm
=0,iRow
=0,iCount
=0,curItemLeftPos
=0;
1207 iItm
<infoPtr
->uNumItem
;
1210 /* normalize the current rect */
1211 TAB_ITEM
*curr
= TAB_GetItem(infoPtr
, iItm
);
1213 /* shift the item to the left side of the clientRect */
1214 curr
->rect
.right
-= curr
->rect
.left
;
1215 curr
->rect
.left
= 0;
1217 TRACE("r=%ld, cl=%d, cl.r=%ld, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1218 curr
->rect
.right
, curItemLeftPos
, clientRect
.right
,
1219 iCount
, iRow
, infoPtr
->uNumRows
, remTab
, tabPerRow
);
1221 /* if we have reached the maximum number of tabs on this row */
1222 /* move to the next row, reset our current item left position and */
1223 /* the count of items on this row */
1225 if (lStyle
& TCS_VERTICAL
) {
1226 /* Vert: Add the remaining tabs in the *last* remainder rows */
1227 if (iCount
>= ((iRow
>=(INT
)infoPtr
->uNumRows
- remTab
)?tabPerRow
+ 1:tabPerRow
)) {
1233 /* Horz: Add the remaining tabs in the *first* remainder rows */
1234 if (iCount
>= ((iRow
<remTab
)?tabPerRow
+ 1:tabPerRow
)) {
1241 /* shift the item to the right to place it as the next item in this row */
1242 curr
->rect
.left
+= curItemLeftPos
;
1243 curr
->rect
.right
+= curItemLeftPos
;
1244 curr
->rect
.top
= iRow
;
1245 if (lStyle
& TCS_BUTTONS
)
1247 curItemLeftPos
= curr
->rect
.right
+ 1;
1248 if (lStyle
& TCS_FLATBUTTONS
)
1249 curItemLeftPos
+= FLAT_BTN_SPACINGX
;
1252 curItemLeftPos
= curr
->rect
.right
;
1254 TRACE("arranging <%s>, l,r=%ld,%ld, row=%ld\n",
1255 debugstr_w(curr
->pszText
), curr
->rect
.left
,
1256 curr
->rect
.right
, curr
->rect
.top
);
1263 INT widthDiff
, iIndexStart
=0, iIndexEnd
=0;
1267 while(iIndexStart
< infoPtr
->uNumItem
)
1269 TAB_ITEM
*start
= TAB_GetItem(infoPtr
, iIndexStart
);
1272 * find the index of the row
1274 /* find the first item on the next row */
1275 for (iIndexEnd
=iIndexStart
;
1276 (iIndexEnd
< infoPtr
->uNumItem
) &&
1277 (TAB_GetItem(infoPtr
, iIndexEnd
)->rect
.top
==
1280 /* intentionally blank */;
1283 * we need to justify these tabs so they fill the whole given
1287 /* find the amount of space remaining on this row */
1288 widthDiff
= clientRect
.right
- (2 * SELECTED_TAB_OFFSET
) -
1289 TAB_GetItem(infoPtr
, iIndexEnd
- 1)->rect
.right
;
1291 /* iCount is the number of tab items on this row */
1292 iCount
= iIndexEnd
- iIndexStart
;
1296 remainder
= widthDiff
% iCount
;
1297 widthDiff
= widthDiff
/ iCount
;
1298 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1299 for (iIndex
=iIndexStart
, iCount
=0; iIndex
< iIndexEnd
; iIndex
++, iCount
++)
1301 TAB_ITEM
*item
= TAB_GetItem(infoPtr
, iIndex
);
1303 item
->rect
.left
+= iCount
* widthDiff
;
1304 item
->rect
.right
+= (iCount
+ 1) * widthDiff
;
1306 TRACE("adjusting 1 <%s>, l,r=%ld,%ld\n",
1307 debugstr_w(item
->pszText
),
1308 item
->rect
.left
, item
->rect
.right
);
1311 TAB_GetItem(infoPtr
, iIndex
- 1)->rect
.right
+= remainder
;
1313 else /* we have only one item on this row, make it take up the entire row */
1315 start
->rect
.left
= clientRect
.left
;
1316 start
->rect
.right
= clientRect
.right
- 4;
1318 TRACE("adjusting 2 <%s>, l,r=%ld,%ld\n",
1319 debugstr_w(start
->pszText
),
1320 start
->rect
.left
, start
->rect
.right
);
1325 iIndexStart
= iIndexEnd
;
1330 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1331 if(lStyle
& TCS_VERTICAL
)
1334 for(iIndex
= 0; iIndex
< infoPtr
->uNumItem
; iIndex
++)
1336 rcItem
= &TAB_GetItem(infoPtr
, iIndex
)->rect
;
1338 rcOriginal
= *rcItem
;
1340 /* this is rotating the items by 90 degrees clockwise around the center of the control */
1341 rcItem
->top
= (rcOriginal
.left
- clientRect
.left
);
1342 rcItem
->bottom
= rcItem
->top
+ (rcOriginal
.right
- rcOriginal
.left
);
1343 rcItem
->left
= rcOriginal
.top
;
1344 rcItem
->right
= rcOriginal
.bottom
;
1348 TAB_EnsureSelectionVisible(infoPtr
);
1349 TAB_RecalcHotTrack(infoPtr
, NULL
, NULL
, NULL
);
1352 SelectObject (hdc
, hOldFont
);
1353 ReleaseDC (infoPtr
->hwnd
, hdc
);
1358 TAB_EraseTabInterior
1366 LONG lStyle
= GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
);
1367 HBRUSH hbr
= CreateSolidBrush (comctl32_color
.clrBtnFace
);
1368 BOOL deleteBrush
= TRUE
;
1369 RECT rTemp
= *drawRect
;
1371 InflateRect(&rTemp
, -2, -2);
1372 if (lStyle
& TCS_BUTTONS
)
1374 if (iItem
== infoPtr
->iSelected
)
1376 /* Background color */
1377 if (!(lStyle
& TCS_OWNERDRAWFIXED
))
1380 hbr
= GetSysColorBrush(COLOR_SCROLLBAR
);
1382 SetTextColor(hdc
, comctl32_color
.clr3dFace
);
1383 SetBkColor(hdc
, comctl32_color
.clr3dHilight
);
1385 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1386 * we better use 0x55aa bitmap brush to make scrollbar's background
1387 * look different from the window background.
1389 if (comctl32_color
.clr3dHilight
== comctl32_color
.clrWindow
)
1390 hbr
= COMCTL32_hPattern55AABrush
;
1392 deleteBrush
= FALSE
;
1394 FillRect(hdc
, &rTemp
, hbr
);
1396 else /* ! selected */
1398 if (lStyle
& TCS_FLATBUTTONS
)
1400 FillRect(hdc
, drawRect
, hbr
);
1401 if (iItem
== infoPtr
->iHotTracked
)
1402 DrawEdge(hdc
, drawRect
, EDGE_RAISED
, BF_SOFT
|BF_RECT
);
1405 FillRect(hdc
, &rTemp
, hbr
);
1409 else /* !TCS_BUTTONS */
1411 FillRect(hdc
, &rTemp
, hbr
);
1415 if (deleteBrush
) DeleteObject(hbr
);
1418 /******************************************************************************
1419 * TAB_DrawItemInterior
1421 * This method is used to draw the interior (text and icon) of a single tab
1422 * into the tab control.
1425 TAB_DrawItemInterior
1433 LONG lStyle
= GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
);
1442 /* if (drawRect == NULL) */
1449 * Get the rectangle for the item.
1451 isVisible
= TAB_InternalGetItemRect(infoPtr
, iItem
, &itemRect
, &selectedRect
);
1456 * Make sure drawRect points to something valid; simplifies code.
1458 drawRect
= &localRect
;
1461 * This logic copied from the part of TAB_DrawItem which draws
1462 * the tab background. It's important to keep it in sync. I
1463 * would have liked to avoid code duplication, but couldn't figure
1464 * out how without making spaghetti of TAB_DrawItem.
1466 if (iItem
== infoPtr
->iSelected
)
1467 *drawRect
= selectedRect
;
1469 *drawRect
= itemRect
;
1471 if (lStyle
& TCS_BUTTONS
)
1473 if (iItem
== infoPtr
->iSelected
)
1475 drawRect
->left
+= 4;
1477 drawRect
->right
-= 4;
1478 drawRect
->bottom
-= 1;
1482 drawRect
->left
+= 2;
1484 drawRect
->right
-= 2;
1485 drawRect
->bottom
-= 2;
1490 if ((lStyle
& TCS_VERTICAL
) && (lStyle
& TCS_BOTTOM
))
1492 if (iItem
!= infoPtr
->iSelected
)
1494 drawRect
->left
+= 2;
1496 drawRect
->bottom
-= 2;
1499 else if (lStyle
& TCS_VERTICAL
)
1501 if (iItem
== infoPtr
->iSelected
)
1503 drawRect
->right
+= 1;
1508 drawRect
->right
-= 2;
1509 drawRect
->bottom
-= 2;
1512 else if (lStyle
& TCS_BOTTOM
)
1514 if (iItem
== infoPtr
->iSelected
)
1520 InflateRect(drawRect
, -2, -2);
1521 drawRect
->bottom
+= 2;
1526 if (iItem
== infoPtr
->iSelected
)
1528 drawRect
->bottom
+= 3;
1532 drawRect
->bottom
-= 2;
1533 InflateRect(drawRect
, -2, 0);
1538 TRACE("drawRect=(%ld,%ld)-(%ld,%ld)\n",
1539 drawRect
->left
, drawRect
->top
, drawRect
->right
, drawRect
->bottom
);
1541 /* Clear interior */
1542 TAB_EraseTabInterior (infoPtr
, hdc
, iItem
, drawRect
);
1544 /* Draw the focus rectangle */
1545 if (!(lStyle
& TCS_FOCUSNEVER
) &&
1546 (GetFocus() == infoPtr
->hwnd
) &&
1547 (iItem
== infoPtr
->uFocus
) )
1549 RECT rFocus
= *drawRect
;
1550 InflateRect(&rFocus
, -3, -3);
1551 if (lStyle
& TCS_BOTTOM
&& !(lStyle
& TCS_VERTICAL
))
1553 if (lStyle
& TCS_BUTTONS
)
1559 DrawFocusRect(hdc
, &rFocus
);
1565 htextPen
= CreatePen( PS_SOLID
, 1, GetSysColor(COLOR_BTNTEXT
) );
1566 holdPen
= SelectObject(hdc
, htextPen
);
1567 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
1570 * Setup for text output
1572 oldBkMode
= SetBkMode(hdc
, TRANSPARENT
);
1573 SetTextColor(hdc
, (((iItem
== infoPtr
->iHotTracked
) && !(lStyle
& TCS_FLATBUTTONS
)) |
1574 (TAB_GetItem(infoPtr
, iItem
)->dwState
& TCIS_HIGHLIGHTED
)) ?
1575 comctl32_color
.clrHighlight
: comctl32_color
.clrBtnText
);
1578 * if owner draw, tell the owner to draw
1580 if ((lStyle
& TCS_OWNERDRAWFIXED
) && GetParent(infoPtr
->hwnd
))
1586 drawRect
->right
-= 1;
1587 if ( iItem
== infoPtr
->iSelected
)
1589 drawRect
->right
-= 1;
1590 drawRect
->left
+= 1;
1594 * get the control id
1596 id
= (UINT
)GetWindowLongPtrW( infoPtr
->hwnd
, GWLP_ID
);
1599 * put together the DRAWITEMSTRUCT
1601 dis
.CtlType
= ODT_TAB
;
1604 dis
.itemAction
= ODA_DRAWENTIRE
;
1606 if ( iItem
== infoPtr
->iSelected
)
1607 dis
.itemState
|= ODS_SELECTED
;
1608 if (infoPtr
->uFocus
== iItem
)
1609 dis
.itemState
|= ODS_FOCUS
;
1610 dis
.hwndItem
= infoPtr
->hwnd
;
1612 CopyRect(&dis
.rcItem
,drawRect
);
1613 dis
.itemData
= (ULONG_PTR
)TAB_GetItem(infoPtr
, iItem
)->extra
;
1616 * send the draw message
1618 SendMessageW( infoPtr
->hwndNotify
, WM_DRAWITEM
, (WPARAM
)id
, (LPARAM
)&dis
);
1622 TAB_ITEM
*item
= TAB_GetItem(infoPtr
, iItem
);
1626 /* used to center the icon and text in the tab */
1628 INT center_offset_h
, center_offset_v
;
1630 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1631 rcImage
= *drawRect
;
1635 rcText
.left
= rcText
.top
= rcText
.right
= rcText
.bottom
= 0;
1637 /* get the rectangle that the text fits in */
1640 DrawTextW(hdc
, item
->pszText
, -1, &rcText
, DT_CALCRECT
);
1643 * If not owner draw, then do the drawing ourselves.
1647 if (infoPtr
->himl
&& (item
->mask
& TCIF_IMAGE
))
1652 ImageList_GetIconSize(infoPtr
->himl
, &cx
, &cy
);
1654 if(lStyle
& TCS_VERTICAL
)
1656 center_offset_h
= ((drawRect
->bottom
- drawRect
->top
) - (cy
+ infoPtr
->uHItemPadding
+ (rcText
.right
- rcText
.left
))) / 2;
1657 center_offset_v
= ((drawRect
->right
- drawRect
->left
) - (cx
+ infoPtr
->uVItemPadding
)) / 2;
1661 center_offset_h
= ((drawRect
->right
- drawRect
->left
) - (cx
+ infoPtr
->uHItemPadding
+ (rcText
.right
- rcText
.left
))) / 2;
1662 center_offset_v
= ((drawRect
->bottom
- drawRect
->top
) - (cy
+ infoPtr
->uVItemPadding
)) / 2;
1665 if (lStyle
& TCS_FIXEDWIDTH
&& lStyle
& (TCS_FORCELABELLEFT
| TCS_FORCEICONLEFT
))
1666 center_offset_h
= infoPtr
->uHItemPadding
;
1668 if (center_offset_h
< 2)
1669 center_offset_h
= 2;
1671 if (center_offset_v
< 0)
1672 center_offset_v
= 0;
1674 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1675 debugstr_w(item
->pszText
), center_offset_h
, center_offset_v
,
1676 drawRect
->left
, drawRect
->top
, drawRect
->right
, drawRect
->bottom
,
1677 (rcText
.right
-rcText
.left
));
1679 if((lStyle
& TCS_VERTICAL
) && (lStyle
& TCS_BOTTOM
))
1681 rcImage
.top
= drawRect
->top
+ center_offset_h
;
1682 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1683 /* right side of the tab, but the image still uses the left as its x position */
1684 /* this keeps the image always drawn off of the same side of the tab */
1685 rcImage
.left
= drawRect
->right
- cx
- center_offset_v
;
1686 drawRect
->top
+= cy
+ infoPtr
->uHItemPadding
;
1688 else if(lStyle
& TCS_VERTICAL
)
1690 rcImage
.top
= drawRect
->bottom
- cy
- center_offset_h
;
1691 rcImage
.left
= drawRect
->left
+ center_offset_v
;
1692 drawRect
->bottom
-= cy
+ infoPtr
->uHItemPadding
;
1694 else /* normal style, whether TCS_BOTTOM or not */
1696 rcImage
.left
= drawRect
->left
+ center_offset_h
;
1697 rcImage
.top
= drawRect
->top
+ center_offset_v
;
1698 drawRect
->left
+= cx
+ infoPtr
->uHItemPadding
;
1701 TRACE("drawing image=%d, left=%ld, top=%ld\n",
1702 item
->iImage
, rcImage
.left
, rcImage
.top
-1);
1714 /* Now position text */
1715 if (lStyle
& TCS_FIXEDWIDTH
&& lStyle
& TCS_FORCELABELLEFT
)
1716 center_offset_h
= infoPtr
->uHItemPadding
;
1718 if(lStyle
& TCS_VERTICAL
)
1719 center_offset_h
= ((drawRect
->bottom
- drawRect
->top
) - (rcText
.right
- rcText
.left
)) / 2;
1721 center_offset_h
= ((drawRect
->right
- drawRect
->left
) - (rcText
.right
- rcText
.left
)) / 2;
1723 if(lStyle
& TCS_VERTICAL
)
1725 if(lStyle
& TCS_BOTTOM
)
1726 drawRect
->top
+=center_offset_h
;
1728 drawRect
->bottom
-=center_offset_h
;
1730 center_offset_v
= ((drawRect
->right
- drawRect
->left
) - (rcText
.bottom
- rcText
.top
) + infoPtr
->uVItemPadding
) / 2;
1734 drawRect
->left
+= center_offset_h
;
1735 center_offset_v
= ((drawRect
->bottom
- drawRect
->top
) - (rcText
.bottom
- rcText
.top
) + infoPtr
->uVItemPadding
) / 2;
1738 if (center_offset_v
< 0)
1739 center_offset_v
= 0;
1741 if(lStyle
& TCS_VERTICAL
)
1742 drawRect
->left
+= center_offset_v
;
1744 drawRect
->top
+= center_offset_v
;
1747 if(lStyle
& TCS_VERTICAL
) /* if we are vertical rotate the text and each character */
1749 static const WCHAR ArialW
[] = { 'A','r','i','a','l',0 };
1752 INT nEscapement
= 900;
1753 INT nOrientation
= 900;
1755 if(lStyle
& TCS_BOTTOM
)
1758 nOrientation
= -900;
1761 /* to get a font with the escapement and orientation we are looking for, we need to */
1762 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1763 if (!GetObjectW((infoPtr
->hFont
) ?
1764 infoPtr
->hFont
: GetStockObject(SYSTEM_FONT
),
1765 sizeof(LOGFONTW
),&logfont
))
1769 lstrcpyW(logfont
.lfFaceName
, ArialW
);
1770 logfont
.lfHeight
= -MulDiv(iPointSize
, GetDeviceCaps(hdc
, LOGPIXELSY
),
1772 logfont
.lfWeight
= FW_NORMAL
;
1773 logfont
.lfItalic
= 0;
1774 logfont
.lfUnderline
= 0;
1775 logfont
.lfStrikeOut
= 0;
1778 logfont
.lfEscapement
= nEscapement
;
1779 logfont
.lfOrientation
= nOrientation
;
1780 hFont
= CreateFontIndirectW(&logfont
);
1781 SelectObject(hdc
, hFont
);
1786 (lStyle
& TCS_BOTTOM
) ? drawRect
->right
: drawRect
->left
,
1787 (!(lStyle
& TCS_BOTTOM
)) ? drawRect
->bottom
: drawRect
->top
,
1791 lstrlenW(item
->pszText
),
1795 DeleteObject(hFont
);
1799 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1800 debugstr_w(item
->pszText
), center_offset_h
, center_offset_v
,
1801 drawRect
->left
, drawRect
->top
, drawRect
->right
, drawRect
->bottom
,
1802 (rcText
.right
-rcText
.left
));
1809 lstrlenW(item
->pszText
),
1811 DT_LEFT
| DT_SINGLELINE
1816 *drawRect
= rcTemp
; /* restore drawRect */
1822 SelectObject(hdc
, hOldFont
);
1823 SetBkMode(hdc
, oldBkMode
);
1824 SelectObject(hdc
, holdPen
);
1825 DeleteObject( htextPen
);
1828 /******************************************************************************
1831 * This method is used to draw a single tab into the tab control.
1833 static void TAB_DrawItem(
1838 LONG lStyle
= GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
);
1842 RECT r
, fillRect
, r1
;
1845 COLORREF bkgnd
, corner
;
1848 * Get the rectangle for the item.
1850 isVisible
= TAB_InternalGetItemRect(infoPtr
,
1859 /* Clip UpDown control to not draw over it */
1860 if (infoPtr
->needsScrolling
)
1862 GetWindowRect(infoPtr
->hwnd
, &rC
);
1863 GetWindowRect(infoPtr
->hwndUpDown
, &rUD
);
1864 ExcludeClipRect(hdc
, rUD
.left
- rC
.left
, rUD
.top
- rC
.top
, rUD
.right
- rC
.left
, rUD
.bottom
- rC
.top
);
1867 /* If you need to see what the control is doing,
1868 * then override these variables. They will change what
1869 * fill colors are used for filling the tabs, and the
1870 * corners when drawing the edge.
1872 bkgnd
= comctl32_color
.clrBtnFace
;
1873 corner
= comctl32_color
.clrBtnFace
;
1875 if (lStyle
& TCS_BUTTONS
)
1877 /* Get item rectangle */
1880 /* Separators between flat buttons */
1881 if (lStyle
& TCS_FLATBUTTONS
)
1884 r1
.right
+= (FLAT_BTN_SPACINGX
-2);
1885 DrawEdge(hdc
, &r1
, EDGE_ETCHED
, BF_RIGHT
);
1888 if (iItem
== infoPtr
->iSelected
)
1890 DrawEdge(hdc
, &r
, EDGE_SUNKEN
, BF_SOFT
|BF_RECT
);
1892 OffsetRect(&r
, 1, 1);
1894 else /* ! selected */
1896 if (!(lStyle
& TCS_FLATBUTTONS
))
1897 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_RECT
);
1900 else /* !TCS_BUTTONS */
1902 /* We draw a rectangle of different sizes depending on the selection
1904 if (iItem
== infoPtr
->iSelected
) {
1906 GetClientRect (infoPtr
->hwnd
, &rect
);
1907 clRight
= rect
.right
;
1908 clBottom
= rect
.bottom
;
1915 * Erase the background. (Delay it but setup rectangle.)
1916 * This is necessary when drawing the selected item since it is larger
1917 * than the others, it might overlap with stuff already drawn by the
1922 if(lStyle
& TCS_VERTICAL
)
1924 /* These are for adjusting the drawing of a Selected tab */
1925 /* The initial values are for the normal case of non-Selected */
1926 int ZZ
= 1; /* Do not strech if selected */
1927 if (iItem
== infoPtr
->iSelected
) {
1930 /* if leftmost draw the line longer */
1931 if(selectedRect
.top
== 0)
1932 fillRect
.top
+= CONTROL_BORDER_SIZEY
;
1933 /* if rightmost draw the line longer */
1934 if(selectedRect
.bottom
== clBottom
)
1935 fillRect
.bottom
-= CONTROL_BORDER_SIZEY
;
1938 if (lStyle
& TCS_BOTTOM
)
1940 /* Adjust both rectangles to match native */
1943 TRACE("<right> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1945 fillRect
.left
,fillRect
.top
,fillRect
.right
,fillRect
.bottom
,
1946 r
.left
,r
.top
,r
.right
,r
.bottom
);
1948 /* Clear interior */
1949 SetBkColor(hdc
, bkgnd
);
1950 ExtTextOutW(hdc
, 0, 0, 2, &fillRect
, NULL
, 0, 0);
1952 /* Draw rectangular edge around tab */
1953 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_RIGHT
|BF_TOP
|BF_BOTTOM
);
1955 /* Now erase the top corner and draw diagonal edge */
1956 SetBkColor(hdc
, corner
);
1957 r1
.left
= r
.right
- ROUND_CORNER_SIZE
- 1;
1960 r1
.bottom
= r1
.top
+ ROUND_CORNER_SIZE
;
1961 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
1963 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDTOPLEFT
);
1965 /* Now erase the bottom corner and draw diagonal edge */
1966 r1
.left
= r
.right
- ROUND_CORNER_SIZE
- 1;
1967 r1
.bottom
= r
.bottom
;
1969 r1
.top
= r1
.bottom
- ROUND_CORNER_SIZE
;
1970 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
1972 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDBOTTOMLEFT
);
1974 if ((iItem
== infoPtr
->iSelected
) && (selectedRect
.top
== 0)) {
1978 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_TOP
);
1984 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1986 fillRect
.left
,fillRect
.top
,fillRect
.right
,fillRect
.bottom
,
1987 r
.left
,r
.top
,r
.right
,r
.bottom
);
1989 /* Clear interior */
1990 SetBkColor(hdc
, bkgnd
);
1991 ExtTextOutW(hdc
, 0, 0, 2, &fillRect
, NULL
, 0, 0);
1993 /* Draw rectangular edge around tab */
1994 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_LEFT
|BF_TOP
|BF_BOTTOM
);
1996 /* Now erase the top corner and draw diagonal edge */
1997 SetBkColor(hdc
, corner
);
2000 r1
.right
= r1
.left
+ ROUND_CORNER_SIZE
+ 1;
2001 r1
.bottom
= r1
.top
+ ROUND_CORNER_SIZE
;
2002 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2004 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDTOPRIGHT
);
2006 /* Now erase the bottom corner and draw diagonal edge */
2008 r1
.bottom
= r
.bottom
;
2009 r1
.right
= r1
.left
+ ROUND_CORNER_SIZE
+ 1;
2010 r1
.top
= r1
.bottom
- ROUND_CORNER_SIZE
;
2011 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2013 DrawEdge(hdc
, &r1
, EDGE_SUNKEN
, BF_DIAGONAL_ENDTOPLEFT
);
2016 else /* ! TCS_VERTICAL */
2018 /* These are for adjusting the drawing of a Selected tab */
2019 /* The initial values are for the normal case of non-Selected */
2020 if (iItem
== infoPtr
->iSelected
) {
2021 /* if leftmost draw the line longer */
2022 if(selectedRect
.left
== 0)
2023 fillRect
.left
+= CONTROL_BORDER_SIZEX
;
2024 /* if rightmost draw the line longer */
2025 if(selectedRect
.right
== clRight
)
2026 fillRect
.right
-= CONTROL_BORDER_SIZEX
;
2029 if (lStyle
& TCS_BOTTOM
)
2031 /* Adjust both rectangles for topmost row */
2032 if (TAB_GetItem(infoPtr
, iItem
)->rect
.top
== infoPtr
->uNumRows
-1)
2038 TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2040 fillRect
.left
,fillRect
.top
,fillRect
.right
,fillRect
.bottom
,
2041 r
.left
,r
.top
,r
.right
,r
.bottom
);
2043 /* Clear interior */
2044 SetBkColor(hdc
, bkgnd
);
2045 ExtTextOutW(hdc
, 0, 0, 2, &fillRect
, NULL
, 0, 0);
2047 /* Draw rectangular edge around tab */
2048 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_LEFT
|BF_BOTTOM
|BF_RIGHT
);
2050 /* Now erase the righthand corner and draw diagonal edge */
2051 SetBkColor(hdc
, corner
);
2052 r1
.left
= r
.right
- ROUND_CORNER_SIZE
;
2053 r1
.bottom
= r
.bottom
;
2055 r1
.top
= r1
.bottom
- ROUND_CORNER_SIZE
- 1;
2056 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2058 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDBOTTOMLEFT
);
2060 /* Now erase the lefthand corner and draw diagonal edge */
2062 r1
.bottom
= r
.bottom
;
2063 r1
.right
= r1
.left
+ ROUND_CORNER_SIZE
;
2064 r1
.top
= r1
.bottom
- ROUND_CORNER_SIZE
- 1;
2065 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2067 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDTOPLEFT
);
2069 if (iItem
== infoPtr
->iSelected
)
2073 if (selectedRect
.left
== 0)
2078 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_LEFT
);
2085 /* Adjust both rectangles for bottommost row */
2086 if (TAB_GetItem(infoPtr
, iItem
)->rect
.top
== infoPtr
->uNumRows
-1)
2088 fillRect
.bottom
+= 3;
2092 TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2094 fillRect
.left
,fillRect
.top
,fillRect
.right
,fillRect
.bottom
,
2095 r
.left
,r
.top
,r
.right
,r
.bottom
);
2097 /* Clear interior */
2098 SetBkColor(hdc
, bkgnd
);
2099 ExtTextOutW(hdc
, 0, 0, 2, &fillRect
, NULL
, 0, 0);
2101 /* Draw rectangular edge around tab */
2102 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_LEFT
|BF_TOP
|BF_RIGHT
);
2104 /* Now erase the righthand corner and draw diagonal edge */
2105 SetBkColor(hdc
, corner
);
2106 r1
.left
= r
.right
- ROUND_CORNER_SIZE
;
2109 r1
.bottom
= r1
.top
+ ROUND_CORNER_SIZE
+ 1;
2110 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2112 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDBOTTOMRIGHT
);
2114 /* Now erase the lefthand corner and draw diagonal edge */
2117 r1
.right
= r1
.left
+ ROUND_CORNER_SIZE
;
2118 r1
.bottom
= r1
.top
+ ROUND_CORNER_SIZE
+ 1;
2119 ExtTextOutW(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2121 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDTOPRIGHT
);
2126 TAB_DumpItemInternal(infoPtr
, iItem
);
2128 /* This modifies r to be the text rectangle. */
2129 TAB_DrawItemInterior(infoPtr
, hdc
, iItem
, &r
);
2133 /******************************************************************************
2136 * This method is used to draw the raised border around the tab control
2139 static void TAB_DrawBorder (TAB_INFO
*infoPtr
, HDC hdc
)
2142 DWORD lStyle
= GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
);
2144 GetClientRect (infoPtr
->hwnd
, &rect
);
2147 * Adjust for the style
2150 if (infoPtr
->uNumItem
)
2152 if ((lStyle
& TCS_BOTTOM
) && !(lStyle
& TCS_VERTICAL
))
2153 rect
.bottom
-= infoPtr
->tabHeight
* infoPtr
->uNumRows
+ CONTROL_BORDER_SIZEX
;
2154 else if((lStyle
& TCS_BOTTOM
) && (lStyle
& TCS_VERTICAL
))
2155 rect
.right
-= infoPtr
->tabHeight
* infoPtr
->uNumRows
+ CONTROL_BORDER_SIZEX
;
2156 else if(lStyle
& TCS_VERTICAL
)
2157 rect
.left
+= infoPtr
->tabHeight
* infoPtr
->uNumRows
+ CONTROL_BORDER_SIZEX
;
2158 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2159 rect
.top
+= infoPtr
->tabHeight
* infoPtr
->uNumRows
+ CONTROL_BORDER_SIZEX
;
2162 TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2163 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2165 DrawEdge(hdc
, &rect
, EDGE_RAISED
, BF_SOFT
|BF_RECT
);
2168 /******************************************************************************
2171 * This method repaints the tab control..
2173 static void TAB_Refresh (TAB_INFO
*infoPtr
, HDC hdc
)
2178 if (!infoPtr
->DoRedraw
)
2181 hOldFont
= SelectObject (hdc
, infoPtr
->hFont
);
2183 if (GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
) & TCS_BUTTONS
)
2185 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
2186 TAB_DrawItem (infoPtr
, hdc
, i
);
2190 /* Draw all the non selected item first */
2191 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
2193 if (i
!= infoPtr
->iSelected
)
2194 TAB_DrawItem (infoPtr
, hdc
, i
);
2197 /* Now, draw the border, draw it before the selected item
2198 * since the selected item overwrites part of the border. */
2199 TAB_DrawBorder (infoPtr
, hdc
);
2201 /* Then, draw the selected item */
2202 TAB_DrawItem (infoPtr
, hdc
, infoPtr
->iSelected
);
2204 /* If we haven't set the current focus yet, set it now.
2205 * Only happens when we first paint the tab controls */
2206 if (infoPtr
->uFocus
== -1)
2207 TAB_SetCurFocus(infoPtr
, infoPtr
->iSelected
);
2210 SelectObject (hdc
, hOldFont
);
2213 static inline DWORD
TAB_GetRowCount (const TAB_INFO
*infoPtr
)
2215 return infoPtr
->uNumRows
;
2218 static inline LRESULT
TAB_SetRedraw (TAB_INFO
*infoPtr
, BOOL doRedraw
)
2220 infoPtr
->DoRedraw
= doRedraw
;
2224 /******************************************************************************
2225 * TAB_EnsureSelectionVisible
2227 * This method will make sure that the current selection is completely
2228 * visible by scrolling until it is.
2230 static void TAB_EnsureSelectionVisible(
2233 INT iSelected
= infoPtr
->iSelected
;
2234 LONG lStyle
= GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
);
2235 INT iOrigLeftmostVisible
= infoPtr
->leftmostVisible
;
2237 /* set the items row to the bottommost row or topmost row depending on
2239 if ((infoPtr
->uNumRows
> 1) && !(lStyle
& TCS_BUTTONS
))
2241 TAB_ITEM
*selected
= TAB_GetItem(infoPtr
, iSelected
);
2245 if(lStyle
& TCS_VERTICAL
)
2246 newselected
= selected
->rect
.left
;
2248 newselected
= selected
->rect
.top
;
2250 /* the target row is always (number of rows - 1)
2251 as row 0 is furthest from the clientRect */
2252 iTargetRow
= infoPtr
->uNumRows
- 1;
2254 if (newselected
!= iTargetRow
)
2257 if(lStyle
& TCS_VERTICAL
)
2259 for (i
=0; i
< infoPtr
->uNumItem
; i
++)
2261 /* move everything in the row of the selected item to the iTargetRow */
2262 TAB_ITEM
*item
= TAB_GetItem(infoPtr
, i
);
2264 if (item
->rect
.left
== newselected
)
2265 item
->rect
.left
= iTargetRow
;
2268 if (item
->rect
.left
> newselected
)
2275 for (i
=0; i
< infoPtr
->uNumItem
; i
++)
2277 TAB_ITEM
*item
= TAB_GetItem(infoPtr
, i
);
2279 if (item
->rect
.top
== newselected
)
2280 item
->rect
.top
= iTargetRow
;
2283 if (item
->rect
.top
> newselected
)
2288 TAB_RecalcHotTrack(infoPtr
, NULL
, NULL
, NULL
);
2293 * Do the trivial cases first.
2295 if ( (!infoPtr
->needsScrolling
) ||
2296 (infoPtr
->hwndUpDown
==0) || (lStyle
& TCS_VERTICAL
))
2299 if (infoPtr
->leftmostVisible
>= iSelected
)
2301 infoPtr
->leftmostVisible
= iSelected
;
2305 TAB_ITEM
*selected
= TAB_GetItem(infoPtr
, iSelected
);
2310 /* Calculate the part of the client area that is visible */
2311 GetClientRect(infoPtr
->hwnd
, &r
);
2314 GetClientRect(infoPtr
->hwndUpDown
, &r
);
2317 if ((selected
->rect
.right
-
2318 selected
->rect
.left
) >= width
)
2320 /* Special case: width of selected item is greater than visible
2323 infoPtr
->leftmostVisible
= iSelected
;
2327 for (i
= infoPtr
->leftmostVisible
; i
< infoPtr
->uNumItem
; i
++)
2329 if ((selected
->rect
.right
- TAB_GetItem(infoPtr
, i
)->rect
.left
) < width
)
2332 infoPtr
->leftmostVisible
= i
;
2336 if (infoPtr
->leftmostVisible
!= iOrigLeftmostVisible
)
2337 TAB_RecalcHotTrack(infoPtr
, NULL
, NULL
, NULL
);
2339 SendMessageW(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
2340 MAKELONG(infoPtr
->leftmostVisible
, 0));
2343 /******************************************************************************
2344 * TAB_InvalidateTabArea
2346 * This method will invalidate the portion of the control that contains the
2347 * tabs. It is called when the state of the control changes and needs
2350 static void TAB_InvalidateTabArea(TAB_INFO
* infoPtr
)
2352 RECT clientRect
, rInvalidate
, rAdjClient
;
2353 DWORD lStyle
= GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
);
2354 INT lastRow
= infoPtr
->uNumRows
- 1;
2357 if (lastRow
< 0) return;
2359 GetClientRect(infoPtr
->hwnd
, &clientRect
);
2360 rInvalidate
= clientRect
;
2361 rAdjClient
= clientRect
;
2363 TAB_AdjustRect(infoPtr
, 0, &rAdjClient
);
2365 TAB_InternalGetItemRect(infoPtr
, infoPtr
->uNumItem
-1 , &rect
, NULL
);
2366 if ((lStyle
& TCS_BOTTOM
) && (lStyle
& TCS_VERTICAL
))
2368 rInvalidate
.left
= rAdjClient
.right
;
2369 if (infoPtr
->uNumRows
== 1)
2370 rInvalidate
.bottom
= clientRect
.top
+ rect
.bottom
+ 2 * SELECTED_TAB_OFFSET
;
2372 else if(lStyle
& TCS_VERTICAL
)
2374 rInvalidate
.right
= rAdjClient
.left
;
2375 if (infoPtr
->uNumRows
== 1)
2376 rInvalidate
.bottom
= clientRect
.top
+ rect
.bottom
+ 2 * SELECTED_TAB_OFFSET
;
2378 else if (lStyle
& TCS_BOTTOM
)
2380 rInvalidate
.top
= rAdjClient
.bottom
;
2381 if (infoPtr
->uNumRows
== 1)
2382 rInvalidate
.right
= clientRect
.left
+ rect
.right
+ 2 * SELECTED_TAB_OFFSET
;
2386 rInvalidate
.bottom
= rAdjClient
.top
;
2387 if (infoPtr
->uNumRows
== 1)
2388 rInvalidate
.right
= clientRect
.left
+ rect
.right
+ 2 * SELECTED_TAB_OFFSET
;
2391 /* Punch out the updown control */
2392 if (infoPtr
->needsScrolling
&& (rInvalidate
.right
> 0)) {
2394 GetClientRect(infoPtr
->hwndUpDown
, &r
);
2395 if (rInvalidate
.right
> clientRect
.right
- r
.left
)
2396 rInvalidate
.right
= rInvalidate
.right
- (r
.right
- r
.left
);
2398 rInvalidate
.right
= clientRect
.right
- r
.left
;
2401 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2402 rInvalidate
.left
, rInvalidate
.top
,
2403 rInvalidate
.right
, rInvalidate
.bottom
);
2405 InvalidateRect(infoPtr
->hwnd
, &rInvalidate
, TRUE
);
2408 static inline LRESULT
TAB_Paint (TAB_INFO
*infoPtr
, HDC hdcPaint
)
2417 hdc
= BeginPaint (infoPtr
->hwnd
, &ps
);
2418 TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2420 ps
.rcPaint
.left
,ps
.rcPaint
.top
,ps
.rcPaint
.right
,ps
.rcPaint
.bottom
);
2423 TAB_Refresh (infoPtr
, hdc
);
2426 EndPaint (infoPtr
->hwnd
, &ps
);
2432 TAB_InsertItemT (TAB_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
, BOOL bUnicode
)
2439 GetClientRect (infoPtr
->hwnd
, &rect
);
2440 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", infoPtr
->hwnd
,
2441 rect
.top
, rect
.left
, rect
.bottom
, rect
.right
);
2443 pti
= (TCITEMW
*)lParam
;
2444 iItem
= (INT
)wParam
;
2446 if (iItem
< 0) return -1;
2447 if (iItem
> infoPtr
->uNumItem
)
2448 iItem
= infoPtr
->uNumItem
;
2450 TAB_DumpItemExternalT(pti
, iItem
, bUnicode
);
2453 if (infoPtr
->uNumItem
== 0) {
2454 infoPtr
->items
= Alloc (TAB_ITEM_SIZE(infoPtr
));
2455 infoPtr
->uNumItem
++;
2456 infoPtr
->iSelected
= 0;
2459 LPBYTE oldItems
= (LPBYTE
)infoPtr
->items
;
2461 infoPtr
->uNumItem
++;
2462 infoPtr
->items
= Alloc (TAB_ITEM_SIZE(infoPtr
) * infoPtr
->uNumItem
);
2464 /* pre insert copy */
2466 memcpy (infoPtr
->items
, oldItems
,
2467 iItem
* TAB_ITEM_SIZE(infoPtr
));
2470 /* post insert copy */
2471 if (iItem
< infoPtr
->uNumItem
- 1) {
2472 memcpy (TAB_GetItem(infoPtr
, iItem
+ 1),
2473 oldItems
+ iItem
* TAB_ITEM_SIZE(infoPtr
),
2474 (infoPtr
->uNumItem
- iItem
- 1) * TAB_ITEM_SIZE(infoPtr
));
2478 if (iItem
<= infoPtr
->iSelected
)
2479 infoPtr
->iSelected
++;
2484 item
= TAB_GetItem(infoPtr
, iItem
);
2486 item
->mask
= pti
->mask
;
2487 item
->pszText
= NULL
;
2489 if (pti
->mask
& TCIF_TEXT
)
2492 Str_SetPtrW (&item
->pszText
, pti
->pszText
);
2494 Str_SetPtrAtoW (&item
->pszText
, (LPSTR
)pti
->pszText
);
2497 if (pti
->mask
& TCIF_IMAGE
)
2498 item
->iImage
= pti
->iImage
;
2502 if (pti
->mask
& TCIF_PARAM
)
2503 memcpy(item
->extra
, &pti
->lParam
, infoPtr
->cbInfo
);
2505 memset(item
->extra
, 0, infoPtr
->cbInfo
);
2507 TAB_SetItemBounds(infoPtr
);
2508 if (infoPtr
->uNumItem
> 1)
2509 TAB_InvalidateTabArea(infoPtr
);
2511 InvalidateRect(infoPtr
->hwnd
, NULL
, TRUE
);
2513 TRACE("[%p]: added item %d %s\n",
2514 infoPtr
->hwnd
, iItem
, debugstr_w(item
->pszText
));
2520 TAB_SetItemSize (TAB_INFO
*infoPtr
, LPARAM lParam
)
2522 LONG lStyle
= GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
);
2524 BOOL bNeedPaint
= FALSE
;
2526 lResult
= MAKELONG(infoPtr
->tabWidth
, infoPtr
->tabHeight
);
2528 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2529 if (lStyle
& TCS_FIXEDWIDTH
&& (infoPtr
->tabWidth
!= (INT
)LOWORD(lParam
)))
2531 infoPtr
->tabWidth
= max((INT
)LOWORD(lParam
), infoPtr
->tabMinWidth
);
2535 if (infoPtr
->tabHeight
!= (INT
)HIWORD(lParam
))
2537 if ((infoPtr
->fHeightSet
= ((INT
)HIWORD(lParam
) != 0)))
2538 infoPtr
->tabHeight
= (INT
)HIWORD(lParam
);
2542 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2543 HIWORD(lResult
), LOWORD(lResult
),
2544 infoPtr
->tabHeight
, infoPtr
->tabWidth
);
2548 TAB_SetItemBounds(infoPtr
);
2549 RedrawWindow(infoPtr
->hwnd
, NULL
, NULL
, RDW_ERASE
| RDW_INVALIDATE
| RDW_UPDATENOW
);
2555 static inline LRESULT
TAB_SetMinTabWidth (TAB_INFO
*infoPtr
, INT cx
)
2559 TRACE("(%p,%d)\n", infoPtr
, cx
);
2562 oldcx
= infoPtr
->tabMinWidth
;
2563 infoPtr
->tabMinWidth
= (cx
==-1)?DEFAULT_TAB_WIDTH
:cx
;
2569 static inline LRESULT
2570 TAB_HighlightItem (TAB_INFO
*infoPtr
, INT iItem
, BOOL fHighlight
)
2574 TRACE("(%p,%d,%s)\n", infoPtr
, iItem
, fHighlight
? "true" : "false");
2576 if (!infoPtr
|| iItem
< 0 || iItem
>= infoPtr
->uNumItem
)
2579 lpState
= &TAB_GetItem(infoPtr
, iItem
)->dwState
;
2582 *lpState
|= TCIS_HIGHLIGHTED
;
2584 *lpState
&= ~TCIS_HIGHLIGHTED
;
2590 TAB_SetItemT (TAB_INFO
*infoPtr
, INT iItem
, LPTCITEMW tabItem
, BOOL bUnicode
)
2594 TRACE("(%p,%d,%p,%s)\n", infoPtr
, iItem
, tabItem
, bUnicode
? "true" : "false");
2596 if (iItem
< 0 || iItem
>= infoPtr
->uNumItem
)
2599 TAB_DumpItemExternalT(tabItem
, iItem
, bUnicode
);
2601 wineItem
= TAB_GetItem(infoPtr
, iItem
);
2603 if (tabItem
->mask
& TCIF_IMAGE
)
2604 wineItem
->iImage
= tabItem
->iImage
;
2606 if (tabItem
->mask
& TCIF_PARAM
)
2607 memcpy(wineItem
->extra
, &tabItem
->lParam
, infoPtr
->cbInfo
);
2609 if (tabItem
->mask
& TCIF_RTLREADING
)
2610 FIXME("TCIF_RTLREADING\n");
2612 if (tabItem
->mask
& TCIF_STATE
)
2613 wineItem
->dwState
= tabItem
->dwState
;
2615 if (tabItem
->mask
& TCIF_TEXT
)
2617 if (wineItem
->pszText
)
2619 Free(wineItem
->pszText
);
2620 wineItem
->pszText
= NULL
;
2623 Str_SetPtrW(&wineItem
->pszText
, tabItem
->pszText
);
2625 Str_SetPtrAtoW(&wineItem
->pszText
, (LPSTR
)tabItem
->pszText
);
2628 /* Update and repaint tabs */
2629 TAB_SetItemBounds(infoPtr
);
2630 TAB_InvalidateTabArea(infoPtr
);
2635 static inline LRESULT
TAB_GetItemCount (const TAB_INFO
*infoPtr
)
2637 return infoPtr
->uNumItem
;
2642 TAB_GetItemT (TAB_INFO
*infoPtr
, INT iItem
, LPTCITEMW tabItem
, BOOL bUnicode
)
2646 TRACE("(%p,%d,%p,%s)\n", infoPtr
, iItem
, tabItem
, bUnicode
? "true" : "false");
2648 if (iItem
< 0 || iItem
>= infoPtr
->uNumItem
)
2651 wineItem
= TAB_GetItem(infoPtr
, iItem
);
2653 if (tabItem
->mask
& TCIF_IMAGE
)
2654 tabItem
->iImage
= wineItem
->iImage
;
2656 if (tabItem
->mask
& TCIF_PARAM
)
2657 memcpy(&tabItem
->lParam
, wineItem
->extra
, infoPtr
->cbInfo
);
2659 if (tabItem
->mask
& TCIF_RTLREADING
)
2660 FIXME("TCIF_RTLREADING\n");
2662 if (tabItem
->mask
& TCIF_STATE
)
2663 tabItem
->dwState
= wineItem
->dwState
;
2665 if (tabItem
->mask
& TCIF_TEXT
)
2668 Str_GetPtrW (wineItem
->pszText
, tabItem
->pszText
, tabItem
->cchTextMax
);
2670 Str_GetPtrWtoA (wineItem
->pszText
, (LPSTR
)tabItem
->pszText
, tabItem
->cchTextMax
);
2673 TAB_DumpItemExternalT(tabItem
, iItem
, bUnicode
);
2679 static LRESULT
TAB_DeleteItem (TAB_INFO
*infoPtr
, INT iItem
)
2681 BOOL bResult
= FALSE
;
2683 TRACE("(%p, %d)\n", infoPtr
, iItem
);
2685 if ((iItem
>= 0) && (iItem
< infoPtr
->uNumItem
))
2687 TAB_ITEM
*item
= TAB_GetItem(infoPtr
, iItem
);
2688 LPBYTE oldItems
= (LPBYTE
)infoPtr
->items
;
2690 TAB_InvalidateTabArea(infoPtr
);
2692 if ((item
->mask
& TCIF_TEXT
) && item
->pszText
)
2693 Free(item
->pszText
);
2695 infoPtr
->uNumItem
--;
2697 if (!infoPtr
->uNumItem
)
2699 infoPtr
->items
= NULL
;
2700 if (infoPtr
->iHotTracked
>= 0)
2702 KillTimer(infoPtr
->hwnd
, TAB_HOTTRACK_TIMER
);
2703 infoPtr
->iHotTracked
= -1;
2708 infoPtr
->items
= Alloc(TAB_ITEM_SIZE(infoPtr
) * infoPtr
->uNumItem
);
2711 memcpy(infoPtr
->items
, oldItems
, iItem
* TAB_ITEM_SIZE(infoPtr
));
2713 if (iItem
< infoPtr
->uNumItem
)
2714 memcpy(TAB_GetItem(infoPtr
, iItem
),
2715 oldItems
+ (iItem
+ 1) * TAB_ITEM_SIZE(infoPtr
),
2716 (infoPtr
->uNumItem
- iItem
) * TAB_ITEM_SIZE(infoPtr
));
2718 if (iItem
<= infoPtr
->iHotTracked
)
2720 /* When tabs move left/up, the hot track item may change */
2721 FIXME("Recalc hot track");
2726 /* Readjust the selected index */
2727 if ((iItem
== infoPtr
->iSelected
) && (iItem
> 0))
2728 infoPtr
->iSelected
--;
2730 if (iItem
< infoPtr
->iSelected
)
2731 infoPtr
->iSelected
--;
2733 if (infoPtr
->uNumItem
== 0)
2734 infoPtr
->iSelected
= -1;
2736 /* Reposition and repaint tabs */
2737 TAB_SetItemBounds(infoPtr
);
2745 static inline LRESULT
TAB_DeleteAllItems (TAB_INFO
*infoPtr
)
2747 TRACE("(%p)\n", infoPtr
);
2748 while (infoPtr
->uNumItem
)
2749 TAB_DeleteItem (infoPtr
, 0);
2754 static inline LRESULT
TAB_GetFont (const TAB_INFO
*infoPtr
)
2756 TRACE("(%p) returning %p\n", infoPtr
, infoPtr
->hFont
);
2757 return (LRESULT
)infoPtr
->hFont
;
2760 static inline LRESULT
TAB_SetFont (TAB_INFO
*infoPtr
, HFONT hNewFont
)
2762 TRACE("(%p,%p)\n", infoPtr
, hNewFont
);
2764 infoPtr
->hFont
= hNewFont
;
2766 TAB_SetItemBounds(infoPtr
);
2768 TAB_InvalidateTabArea(infoPtr
);
2774 static inline LRESULT
TAB_GetImageList (const TAB_INFO
*infoPtr
)
2777 return (LRESULT
)infoPtr
->himl
;
2780 static inline LRESULT
TAB_SetImageList (TAB_INFO
*infoPtr
, HIMAGELIST himlNew
)
2782 HIMAGELIST himlPrev
= infoPtr
->himl
;
2784 infoPtr
->himl
= himlNew
;
2785 return (LRESULT
)himlPrev
;
2788 static inline LRESULT
TAB_GetUnicodeFormat (const TAB_INFO
*infoPtr
)
2790 return infoPtr
->bUnicode
;
2793 static inline LRESULT
TAB_SetUnicodeFormat (TAB_INFO
*infoPtr
, BOOL bUnicode
)
2795 BOOL bTemp
= infoPtr
->bUnicode
;
2797 infoPtr
->bUnicode
= bUnicode
;
2802 static inline LRESULT
TAB_Size (TAB_INFO
*infoPtr
)
2804 /* I'm not really sure what the following code was meant to do.
2805 This is what it is doing:
2806 When WM_SIZE is sent with SIZE_RESTORED, the control
2807 gets positioned in the top left corner.
2811 UINT uPosFlags,cx,cy;
2815 parent = GetParent (hwnd);
2816 GetClientRect(parent, &parent_rect);
2819 if (GetWindowLongW(hwnd, GWL_STYLE) & CCS_NORESIZE)
2820 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2822 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2823 cx, cy, uPosFlags | SWP_NOZORDER);
2825 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2828 /* Recompute the size/position of the tabs. */
2829 TAB_SetItemBounds (infoPtr
);
2831 /* Force a repaint of the control. */
2832 InvalidateRect(infoPtr
->hwnd
, NULL
, TRUE
);
2838 static LRESULT
TAB_Create (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2841 TEXTMETRICW fontMetrics
;
2846 infoPtr
= (TAB_INFO
*)Alloc (sizeof(TAB_INFO
));
2848 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
2850 infoPtr
->hwnd
= hwnd
;
2851 infoPtr
->hwndNotify
= ((LPCREATESTRUCTW
)lParam
)->hwndParent
;
2852 infoPtr
->uNumItem
= 0;
2853 infoPtr
->uNumRows
= 0;
2854 infoPtr
->uHItemPadding
= 6;
2855 infoPtr
->uVItemPadding
= 3;
2856 infoPtr
->uHItemPadding_s
= 6;
2857 infoPtr
->uVItemPadding_s
= 3;
2860 infoPtr
->hcurArrow
= LoadCursorW (0, (LPWSTR
)IDC_ARROW
);
2861 infoPtr
->iSelected
= -1;
2862 infoPtr
->iHotTracked
= -1;
2863 infoPtr
->uFocus
= -1;
2864 infoPtr
->hwndToolTip
= 0;
2865 infoPtr
->DoRedraw
= TRUE
;
2866 infoPtr
->needsScrolling
= FALSE
;
2867 infoPtr
->hwndUpDown
= 0;
2868 infoPtr
->leftmostVisible
= 0;
2869 infoPtr
->fHeightSet
= FALSE
;
2870 infoPtr
->bUnicode
= IsWindowUnicode (hwnd
);
2871 infoPtr
->cbInfo
= sizeof(LPARAM
);
2873 TRACE("Created tab control, hwnd [%p]\n", hwnd
);
2875 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2876 if you don't specify it in CreateWindow. This is necessary in
2877 order for paint to work correctly. This follows windows behaviour. */
2878 dwStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
2879 SetWindowLongW(hwnd
, GWL_STYLE
, dwStyle
|WS_CLIPSIBLINGS
);
2881 if (dwStyle
& TCS_TOOLTIPS
) {
2882 /* Create tooltip control */
2883 infoPtr
->hwndToolTip
=
2884 CreateWindowExW (0, TOOLTIPS_CLASSW
, NULL
, 0,
2885 CW_USEDEFAULT
, CW_USEDEFAULT
,
2886 CW_USEDEFAULT
, CW_USEDEFAULT
,
2889 /* Send NM_TOOLTIPSCREATED notification */
2890 if (infoPtr
->hwndToolTip
) {
2891 NMTOOLTIPSCREATED nmttc
;
2893 nmttc
.hdr
.hwndFrom
= hwnd
;
2894 nmttc
.hdr
.idFrom
= GetWindowLongPtrW(hwnd
, GWLP_ID
);
2895 nmttc
.hdr
.code
= NM_TOOLTIPSCREATED
;
2896 nmttc
.hwndToolTips
= infoPtr
->hwndToolTip
;
2898 SendMessageW (infoPtr
->hwndNotify
, WM_NOTIFY
,
2899 (WPARAM
)GetWindowLongPtrW(hwnd
, GWLP_ID
), (LPARAM
)&nmttc
);
2904 * We need to get text information so we need a DC and we need to select
2908 hOldFont
= SelectObject (hdc
, GetStockObject (SYSTEM_FONT
));
2910 /* Use the system font to determine the initial height of a tab. */
2911 GetTextMetricsW(hdc
, &fontMetrics
);
2914 * Make sure there is enough space for the letters + growing the
2915 * selected item + extra space for the selected item.
2917 infoPtr
->tabHeight
= fontMetrics
.tmHeight
+ SELECTED_TAB_OFFSET
+
2918 ((dwStyle
& TCS_BUTTONS
) ? 2 : 1) *
2919 infoPtr
->uVItemPadding
;
2921 /* Initialize the width of a tab. */
2922 infoPtr
->tabWidth
= DEFAULT_TAB_WIDTH
;
2923 infoPtr
->tabMinWidth
= 0;
2925 TRACE("tabH=%d, tabW=%d\n", infoPtr
->tabHeight
, infoPtr
->tabWidth
);
2927 SelectObject (hdc
, hOldFont
);
2928 ReleaseDC(hwnd
, hdc
);
2934 TAB_Destroy (TAB_INFO
*infoPtr
)
2941 SetWindowLongPtrW(infoPtr
->hwnd
, 0, 0);
2943 if (infoPtr
->items
) {
2944 for (iItem
= 0; iItem
< infoPtr
->uNumItem
; iItem
++) {
2945 if (TAB_GetItem(infoPtr
, iItem
)->pszText
)
2946 Free (TAB_GetItem(infoPtr
, iItem
)->pszText
);
2948 Free (infoPtr
->items
);
2951 if (infoPtr
->hwndToolTip
)
2952 DestroyWindow (infoPtr
->hwndToolTip
);
2954 if (infoPtr
->hwndUpDown
)
2955 DestroyWindow(infoPtr
->hwndUpDown
);
2957 if (infoPtr
->iHotTracked
>= 0)
2958 KillTimer(infoPtr
->hwnd
, TAB_HOTTRACK_TIMER
);
2964 static LRESULT
TAB_NCCalcSize(HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2968 return WVR_ALIGNTOP
;
2971 static inline LRESULT
2972 TAB_SetItemExtra (TAB_INFO
*infoPtr
, INT cbInfo
)
2974 if (!infoPtr
|| cbInfo
<= 0)
2977 if (infoPtr
->uNumItem
)
2979 /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
2983 infoPtr
->cbInfo
= cbInfo
;
2987 static LRESULT WINAPI
2988 TAB_WindowProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
2990 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2992 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd
, uMsg
, wParam
, lParam
);
2993 if (!infoPtr
&& (uMsg
!= WM_CREATE
))
2994 return DefWindowProcW (hwnd
, uMsg
, wParam
, lParam
);
2998 case TCM_GETIMAGELIST
:
2999 return TAB_GetImageList (infoPtr
);
3001 case TCM_SETIMAGELIST
:
3002 return TAB_SetImageList (infoPtr
, (HIMAGELIST
)lParam
);
3004 case TCM_GETITEMCOUNT
:
3005 return TAB_GetItemCount (infoPtr
);
3009 return TAB_GetItemT (infoPtr
, (INT
)wParam
, (LPTCITEMW
)lParam
, uMsg
== TCM_GETITEMW
);
3013 return TAB_SetItemT (infoPtr
, (INT
)wParam
, (LPTCITEMW
)lParam
, uMsg
== TCM_SETITEMW
);
3015 case TCM_DELETEITEM
:
3016 return TAB_DeleteItem (infoPtr
, (INT
)wParam
);
3018 case TCM_DELETEALLITEMS
:
3019 return TAB_DeleteAllItems (infoPtr
);
3021 case TCM_GETITEMRECT
:
3022 return TAB_GetItemRect (infoPtr
, wParam
, lParam
);
3025 return TAB_GetCurSel (infoPtr
);
3028 return TAB_HitTest (infoPtr
, (LPTCHITTESTINFO
)lParam
);
3031 return TAB_SetCurSel (infoPtr
, (INT
)wParam
);
3033 case TCM_INSERTITEMA
:
3034 case TCM_INSERTITEMW
:
3035 return TAB_InsertItemT (infoPtr
, wParam
, lParam
, uMsg
== TCM_INSERTITEMW
);
3037 case TCM_SETITEMEXTRA
:
3038 return TAB_SetItemExtra (infoPtr
, (int)wParam
);
3040 case TCM_ADJUSTRECT
:
3041 return TAB_AdjustRect (infoPtr
, (BOOL
)wParam
, (LPRECT
)lParam
);
3043 case TCM_SETITEMSIZE
:
3044 return TAB_SetItemSize (infoPtr
, lParam
);
3046 case TCM_REMOVEIMAGE
:
3047 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3050 case TCM_SETPADDING
:
3051 return TAB_SetPadding (infoPtr
, lParam
);
3053 case TCM_GETROWCOUNT
:
3054 return TAB_GetRowCount(infoPtr
);
3056 case TCM_GETUNICODEFORMAT
:
3057 return TAB_GetUnicodeFormat (infoPtr
);
3059 case TCM_SETUNICODEFORMAT
:
3060 return TAB_SetUnicodeFormat (infoPtr
, (BOOL
)wParam
);
3062 case TCM_HIGHLIGHTITEM
:
3063 return TAB_HighlightItem (infoPtr
, (INT
)wParam
, (BOOL
)LOWORD(lParam
));
3065 case TCM_GETTOOLTIPS
:
3066 return TAB_GetToolTips (infoPtr
);
3068 case TCM_SETTOOLTIPS
:
3069 return TAB_SetToolTips (infoPtr
, (HWND
)wParam
);
3071 case TCM_GETCURFOCUS
:
3072 return TAB_GetCurFocus (infoPtr
);
3074 case TCM_SETCURFOCUS
:
3075 return TAB_SetCurFocus (infoPtr
, (INT
)wParam
);
3077 case TCM_SETMINTABWIDTH
:
3078 return TAB_SetMinTabWidth(infoPtr
, (INT
)lParam
);
3080 case TCM_DESELECTALL
:
3081 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3084 case TCM_GETEXTENDEDSTYLE
:
3085 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3088 case TCM_SETEXTENDEDSTYLE
:
3089 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3093 return TAB_GetFont (infoPtr
);
3096 return TAB_SetFont (infoPtr
, (HFONT
)wParam
);
3099 return TAB_Create (hwnd
, wParam
, lParam
);
3102 return TAB_Destroy (infoPtr
);
3105 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
3107 case WM_LBUTTONDOWN
:
3108 return TAB_LButtonDown (infoPtr
, wParam
, lParam
);
3111 return TAB_LButtonUp (infoPtr
);
3114 return SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, wParam
, lParam
);
3116 case WM_RBUTTONDOWN
:
3117 return TAB_RButtonDown (infoPtr
);
3120 return TAB_MouseMove (infoPtr
, wParam
, lParam
);
3123 return TAB_Paint (infoPtr
, (HDC
)wParam
);
3126 return TAB_Size (infoPtr
);
3129 return TAB_SetRedraw (infoPtr
, (BOOL
)wParam
);
3132 return TAB_OnHScroll(infoPtr
, (int)LOWORD(wParam
), (int)HIWORD(wParam
), (HWND
)lParam
);
3134 case WM_STYLECHANGED
:
3135 TAB_SetItemBounds (infoPtr
);
3136 InvalidateRect(hwnd
, NULL
, TRUE
);
3139 case WM_SYSCOLORCHANGE
:
3140 COMCTL32_RefreshSysColors();
3145 TAB_FocusChanging(infoPtr
);
3146 break; /* Don't disturb normal focus behavior */
3149 return TAB_KeyUp(infoPtr
, wParam
);
3151 return TAB_NCHitTest(infoPtr
, lParam
);
3154 return TAB_NCCalcSize(hwnd
, wParam
, lParam
);
3157 if (uMsg
>= WM_USER
&& uMsg
< WM_APP
)
3158 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3159 uMsg
, wParam
, lParam
);
3162 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
3171 ZeroMemory (&wndClass
, sizeof(WNDCLASSW
));
3172 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
;
3173 wndClass
.lpfnWndProc
= TAB_WindowProc
;
3174 wndClass
.cbClsExtra
= 0;
3175 wndClass
.cbWndExtra
= sizeof(TAB_INFO
*);
3176 wndClass
.hCursor
= LoadCursorW (0, (LPWSTR
)IDC_ARROW
);
3177 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_BTNFACE
+1);
3178 wndClass
.lpszClassName
= WC_TABCONTROLW
;
3180 RegisterClassW (&wndClass
);
3185 TAB_Unregister (void)
3187 UnregisterClassW (WC_TABCONTROLW
, NULL
);