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
24 * Unicode support (under construction)
33 * TCM_GETEXTENDEDSTYLE
34 * TCM_SETEXTENDEDSTYLE
37 * UpDown control not displayed until after a tab is clicked on
50 #include "wine/debug.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(tab
);
62 RECT rect
; /* bounding rectangle of the item relative to the
63 * leftmost item (the leftmost item, 0, would have a
64 * "left" member of 0 in this rectangle)
66 * additionally the top member hold the row number
67 * and bottom is unused and should be 0 */
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 a 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 */
100 /******************************************************************************
101 * Positioning constants
103 #define SELECTED_TAB_OFFSET 2
104 #define ROUND_CORNER_SIZE 2
105 #define DISPLAY_AREA_PADDINGX 2
106 #define DISPLAY_AREA_PADDINGY 2
107 #define CONTROL_BORDER_SIZEX 2
108 #define CONTROL_BORDER_SIZEY 2
109 #define BUTTON_SPACINGX 3
110 #define BUTTON_SPACINGY 4
111 #define FLAT_BTN_SPACINGX 8
112 #define DEFAULT_TAB_WIDTH 96
114 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
116 /******************************************************************************
117 * Hot-tracking timer constants
119 #define TAB_HOTTRACK_TIMER 1
120 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
122 /******************************************************************************
125 static void TAB_Refresh (HWND hwnd
, HDC hdc
);
126 static void TAB_InvalidateTabArea(HWND hwnd
, TAB_INFO
* infoPtr
);
127 static void TAB_EnsureSelectionVisible(HWND hwnd
, TAB_INFO
* infoPtr
);
128 static void TAB_DrawItem(HWND hwnd
, HDC hdc
, INT iItem
);
129 static void TAB_DrawItemInterior(HWND hwnd
, HDC hdc
, INT iItem
, RECT
* drawRect
);
132 TAB_SendSimpleNotify (HWND hwnd
, UINT code
)
134 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
137 nmhdr
.hwndFrom
= hwnd
;
138 nmhdr
.idFrom
= GetWindowLongA(hwnd
, GWL_ID
);
141 return (BOOL
) SendMessageA (infoPtr
->hwndNotify
, WM_NOTIFY
,
142 (WPARAM
) nmhdr
.idFrom
, (LPARAM
) &nmhdr
);
146 TAB_RelayEvent (HWND hwndTip
, HWND hwndMsg
, UINT uMsg
,
147 WPARAM wParam
, LPARAM lParam
)
155 msg
.time
= GetMessageTime ();
156 msg
.pt
.x
= LOWORD(GetMessagePos ());
157 msg
.pt
.y
= HIWORD(GetMessagePos ());
159 SendMessageA (hwndTip
, TTM_RELAYEVENT
, 0, (LPARAM
)&msg
);
163 TAB_DumpItemExternalA(TCITEMA
*pti
, UINT iItem
)
166 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
167 iItem
, pti
->mask
, pti
->dwState
, pti
->dwStateMask
, pti
->cchTextMax
);
168 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextA=%s\n",
169 iItem
, pti
->iImage
, pti
->lParam
, debugstr_a(pti
->pszText
));
175 TAB_DumpItemExternalW(TCITEMW
*pti
, UINT iItem
)
178 TRACE("external tab %d, mask=0x%08x, dwState=0x%08lx, dwStateMask=0x%08lx, cchTextMax=0x%08x\n",
179 iItem
, pti
->mask
, pti
->dwState
, pti
->dwStateMask
, pti
->cchTextMax
);
180 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
181 iItem
, pti
->iImage
, pti
->lParam
, debugstr_w(pti
->pszText
));
186 TAB_DumpItemInternal(TAB_INFO
*infoPtr
, UINT iItem
)
191 ti
= &infoPtr
->items
[iItem
];
192 TRACE("tab %d, mask=0x%08x, dwState=0x%08lx, pszText=%s, iImage=%d\n",
193 iItem
, ti
->mask
, ti
->dwState
, debugstr_w(ti
->pszText
),
195 TRACE("tab %d, lParam=0x%08lx, rect.left=%ld, rect.top(row)=%ld\n",
196 iItem
, ti
->lParam
, ti
->rect
.left
, ti
->rect
.top
);
201 TAB_GetCurSel (HWND hwnd
)
203 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
205 return infoPtr
->iSelected
;
209 TAB_GetCurFocus (HWND hwnd
)
211 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
213 return infoPtr
->uFocus
;
217 TAB_GetToolTips (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
219 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
221 if (infoPtr
== NULL
) return 0;
222 return (LRESULT
)infoPtr
->hwndToolTip
;
226 TAB_SetCurSel (HWND hwnd
,WPARAM wParam
)
228 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
229 INT iItem
= (INT
)wParam
;
233 if ((iItem
>= 0) && (iItem
< infoPtr
->uNumItem
)) {
234 prevItem
=infoPtr
->iSelected
;
235 infoPtr
->iSelected
=iItem
;
236 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
237 TAB_InvalidateTabArea(hwnd
, infoPtr
);
243 TAB_SetCurFocus (HWND hwnd
,WPARAM wParam
)
245 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
246 INT iItem
=(INT
) wParam
;
248 if ((iItem
< 0) || (iItem
>= infoPtr
->uNumItem
)) return 0;
250 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BUTTONS
) {
251 FIXME("Should set input focus\n");
253 int oldFocus
= infoPtr
->uFocus
;
254 if (infoPtr
->iSelected
!= iItem
|| infoPtr
->uFocus
== -1 ) {
255 infoPtr
->uFocus
= iItem
;
256 if (oldFocus
!= -1) {
257 if (TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
)!=TRUE
) {
258 infoPtr
->iSelected
= iItem
;
259 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
262 infoPtr
->iSelected
= iItem
;
263 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
264 TAB_InvalidateTabArea(hwnd
, infoPtr
);
272 TAB_SetToolTips (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
274 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
276 if (infoPtr
== NULL
) return 0;
277 infoPtr
->hwndToolTip
= (HWND
)wParam
;
282 TAB_SetPadding (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
284 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
286 if (infoPtr
== NULL
) return 0;
287 infoPtr
->uHItemPadding_s
=LOWORD(lParam
);
288 infoPtr
->uVItemPadding_s
=HIWORD(lParam
);
292 /******************************************************************************
293 * TAB_InternalGetItemRect
295 * This method will calculate the rectangle representing a given tab item in
296 * client coordinates. This method takes scrolling into account.
298 * This method returns TRUE if the item is visible in the window and FALSE
299 * if it is completely outside the client area.
301 static BOOL
TAB_InternalGetItemRect(
308 RECT tmpItemRect
,clientRect
;
309 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
311 /* Perform a sanity check and a trivial visibility check. */
312 if ( (infoPtr
->uNumItem
<= 0) ||
313 (itemIndex
>= infoPtr
->uNumItem
) ||
314 (!((lStyle
& TCS_MULTILINE
) || (lStyle
& TCS_VERTICAL
)) && (itemIndex
< infoPtr
->leftmostVisible
)) )
318 * Avoid special cases in this procedure by assigning the "out"
319 * parameters if the caller didn't supply them
321 if (itemRect
== NULL
)
322 itemRect
= &tmpItemRect
;
324 /* Retrieve the unmodified item rect. */
325 *itemRect
= infoPtr
->items
[itemIndex
].rect
;
327 /* calculate the times bottom and top based on the row */
328 GetClientRect(hwnd
, &clientRect
);
330 if ((lStyle
& TCS_BOTTOM
) && !(lStyle
& TCS_VERTICAL
))
332 itemRect
->bottom
= clientRect
.bottom
-
333 itemRect
->top
* (infoPtr
->tabHeight
- 2) -
334 ((lStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: SELECTED_TAB_OFFSET
);
336 itemRect
->top
= clientRect
.bottom
-
338 itemRect
->top
* (infoPtr
->tabHeight
- 2) -
339 ((lStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: SELECTED_TAB_OFFSET
);
341 else if((lStyle
& TCS_BOTTOM
) && (lStyle
& TCS_VERTICAL
))
343 itemRect
->right
= clientRect
.right
- SELECTED_TAB_OFFSET
- itemRect
->left
* (infoPtr
->tabHeight
- 2) -
344 ((lStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGY
: 0);
345 itemRect
->left
= clientRect
.right
- infoPtr
->tabHeight
- itemRect
->left
* (infoPtr
->tabHeight
- 2) -
346 ((lStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGY
: 0);
348 else if((lStyle
& TCS_VERTICAL
) && !(lStyle
& TCS_BOTTOM
))
350 itemRect
->right
= clientRect
.left
+ infoPtr
->tabHeight
+ itemRect
->left
* (infoPtr
->tabHeight
- 2) +
351 ((lStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGY
: 0);
352 itemRect
->left
= clientRect
.left
+ SELECTED_TAB_OFFSET
+ itemRect
->left
* (infoPtr
->tabHeight
- 2) +
353 ((lStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGY
: 0);
355 else if(!(lStyle
& TCS_VERTICAL
) && !(lStyle
& TCS_BOTTOM
)) /* not TCS_BOTTOM and not TCS_VERTICAL */
357 itemRect
->bottom
= clientRect
.top
+
359 itemRect
->top
* (infoPtr
->tabHeight
- 2) +
360 ((lStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: SELECTED_TAB_OFFSET
);
361 itemRect
->top
= clientRect
.top
+
362 itemRect
->top
* (infoPtr
->tabHeight
- 2) +
363 ((lStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: SELECTED_TAB_OFFSET
);
367 * "scroll" it to make sure the item at the very left of the
368 * tab control is the leftmost visible tab.
370 if(lStyle
& TCS_VERTICAL
)
374 -(clientRect
.bottom
- infoPtr
->items
[infoPtr
->leftmostVisible
].rect
.bottom
));
377 * Move the rectangle so the first item is slightly offset from
378 * the bottom of the tab control.
382 -SELECTED_TAB_OFFSET
);
387 -infoPtr
->items
[infoPtr
->leftmostVisible
].rect
.left
,
391 * Move the rectangle so the first item is slightly offset from
392 * the left of the tab control.
398 TRACE("item %d tab h=%d, rect=(%ld,%ld)-(%ld,%ld)\n",
399 itemIndex
, infoPtr
->tabHeight
,
400 itemRect
->left
, itemRect
->top
, itemRect
->right
, itemRect
->bottom
);
402 /* Now, calculate the position of the item as if it were selected. */
403 if (selectedRect
!=NULL
)
405 CopyRect(selectedRect
, itemRect
);
407 /* The rectangle of a selected item is a bit wider. */
408 if(lStyle
& TCS_VERTICAL
)
409 InflateRect(selectedRect
, 0, SELECTED_TAB_OFFSET
);
411 InflateRect(selectedRect
, SELECTED_TAB_OFFSET
, 0);
413 /* If it also a bit higher. */
414 if ((lStyle
& TCS_BOTTOM
) && !(lStyle
& TCS_VERTICAL
))
416 selectedRect
->bottom
+= SELECTED_TAB_OFFSET
;
418 else if((lStyle
& TCS_BOTTOM
) && (lStyle
& TCS_VERTICAL
))
420 selectedRect
->left
-= 2; /* the border is thicker on the right */
421 selectedRect
->right
+= SELECTED_TAB_OFFSET
;
423 else if(lStyle
& TCS_VERTICAL
)
425 selectedRect
->left
-= SELECTED_TAB_OFFSET
;
426 selectedRect
->right
+= 1;
430 selectedRect
->top
-= SELECTED_TAB_OFFSET
;
431 selectedRect
->bottom
-= 1;
438 static BOOL
TAB_GetItemRect(HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
440 return TAB_InternalGetItemRect(hwnd
, TAB_GetInfoPtr(hwnd
), (INT
)wParam
,
441 (LPRECT
)lParam
, (LPRECT
)NULL
);
444 /******************************************************************************
447 * This method is called to handle keyboard input
449 static LRESULT
TAB_KeyUp(
453 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
459 newItem
= infoPtr
->uFocus
- 1;
462 newItem
= infoPtr
->uFocus
+ 1;
467 * If we changed to a valid item, change the selection
469 if ((newItem
>= 0) &&
470 (newItem
< infoPtr
->uNumItem
) &&
471 (infoPtr
->uFocus
!= newItem
))
473 if (!TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
))
475 infoPtr
->iSelected
= newItem
;
476 infoPtr
->uFocus
= newItem
;
477 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
479 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
480 TAB_InvalidateTabArea(hwnd
, infoPtr
);
487 /******************************************************************************
490 * This method is called whenever the focus goes in or out of this control
491 * it is used to update the visual state of the control.
493 static LRESULT
TAB_FocusChanging(
499 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
504 * Get the rectangle for the item.
506 isVisible
= TAB_InternalGetItemRect(hwnd
,
513 * If the rectangle is not completely invisible, invalidate that
514 * portion of the window.
518 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
519 selectedRect
.left
,selectedRect
.top
,
520 selectedRect
.right
,selectedRect
.bottom
);
521 InvalidateRect(hwnd
, &selectedRect
, TRUE
);
525 * Don't otherwise disturb normal behavior.
527 return DefWindowProcA (hwnd
, uMsg
, wParam
, lParam
);
530 static INT
TAB_InternalHitTest (
540 for (iCount
= 0; iCount
< infoPtr
->uNumItem
; iCount
++)
542 TAB_InternalGetItemRect(hwnd
, infoPtr
, iCount
, &rect
, NULL
);
544 if (PtInRect(&rect
, pt
))
546 *flags
= TCHT_ONITEM
;
551 *flags
= TCHT_NOWHERE
;
556 TAB_HitTest (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
558 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
559 LPTCHITTESTINFO lptest
= (LPTCHITTESTINFO
) lParam
;
561 return TAB_InternalHitTest (hwnd
, infoPtr
, lptest
->pt
, &lptest
->flags
);
564 /******************************************************************************
567 * Napster v2b5 has a tab control for its main navigation which has a client
568 * area that covers the whole area of the dialog pages.
569 * That's why it receives all msgs for that area and the underlying dialog ctrls
571 * So I decided that we should handle WM_NCHITTEST here and return
572 * HTTRANSPARENT if we don't hit the tab control buttons.
573 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
574 * doesn't do it that way. Maybe depends on tab control styles ?
577 TAB_NCHitTest (HWND hwnd
, LPARAM lParam
)
579 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
583 pt
.x
= LOWORD(lParam
);
584 pt
.y
= HIWORD(lParam
);
585 ScreenToClient(hwnd
, &pt
);
587 if (TAB_InternalHitTest(hwnd
, infoPtr
, pt
, &dummyflag
) == -1)
588 return HTTRANSPARENT
;
594 TAB_LButtonDown (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
596 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
600 if (infoPtr
->hwndToolTip
)
601 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
602 WM_LBUTTONDOWN
, wParam
, lParam
);
604 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_FOCUSONBUTTONDOWN
) {
608 if (infoPtr
->hwndToolTip
)
609 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
610 WM_LBUTTONDOWN
, wParam
, lParam
);
612 pt
.x
= (INT
)LOWORD(lParam
);
613 pt
.y
= (INT
)HIWORD(lParam
);
615 newItem
= TAB_InternalHitTest (hwnd
, infoPtr
, pt
, &dummy
);
617 TRACE("On Tab, item %d\n", newItem
);
619 if ((newItem
!= -1) && (infoPtr
->iSelected
!= newItem
))
621 if (TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
) != TRUE
)
623 infoPtr
->iSelected
= newItem
;
624 infoPtr
->uFocus
= newItem
;
625 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
627 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
629 TAB_InvalidateTabArea(hwnd
, infoPtr
);
636 TAB_LButtonUp (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
638 TAB_SendSimpleNotify(hwnd
, NM_CLICK
);
644 TAB_RButtonDown (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
646 TAB_SendSimpleNotify(hwnd
, NM_RCLICK
);
650 /******************************************************************************
651 * TAB_DrawLoneItemInterior
653 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
654 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
655 * up the device context and font. This routine does the same setup but
656 * only calls TAB_DrawItemInterior for the single specified item.
659 TAB_DrawLoneItemInterior(HWND hwnd
, TAB_INFO
* infoPtr
, int iItem
)
661 HDC hdc
= GetDC(hwnd
);
664 /* Clip UpDown control to not draw over it */
665 if (infoPtr
->needsScrolling
)
667 GetWindowRect(hwnd
, &rC
);
668 GetWindowRect(infoPtr
->hwndUpDown
, &r
);
669 ExcludeClipRect(hdc
, r
.left
- rC
.left
, r
.top
- rC
.top
, r
.right
- rC
.left
, r
.bottom
- rC
.top
);
671 TAB_DrawItemInterior(hwnd
, hdc
, iItem
, NULL
);
672 ReleaseDC(hwnd
, hdc
);
675 /******************************************************************************
676 * TAB_HotTrackTimerProc
678 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
679 * timer is setup so we can check if the mouse is moved out of our window.
680 * (We don't get an event when the mouse leaves, the mouse-move events just
681 * stop being delivered to our window and just start being delivered to
682 * another window.) This function is called when the timer triggers so
683 * we can check if the mouse has left our window. If so, we un-highlight
684 * the hot-tracked tab.
687 TAB_HotTrackTimerProc
689 HWND hwnd
, /* handle of window for timer messages */
690 UINT uMsg
, /* WM_TIMER message */
691 UINT idEvent
, /* timer identifier */
692 DWORD dwTime
/* current system time */
695 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
697 if (infoPtr
!= NULL
&& infoPtr
->iHotTracked
>= 0)
702 ** If we can't get the cursor position, or if the cursor is outside our
703 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
704 ** "outside" even if it is within our bounding rect if another window
705 ** overlaps. Note also that the case where the cursor stayed within our
706 ** window but has moved off the hot-tracked tab will be handled by the
707 ** WM_MOUSEMOVE event.
709 if (!GetCursorPos(&pt
) || WindowFromPoint(pt
) != hwnd
)
711 /* Redraw iHotTracked to look normal */
712 INT iRedraw
= infoPtr
->iHotTracked
;
713 infoPtr
->iHotTracked
= -1;
714 TAB_DrawLoneItemInterior(hwnd
, infoPtr
, iRedraw
);
716 /* Kill this timer */
717 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
722 /******************************************************************************
725 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
726 * should be highlighted. This function determines which tab in a tab control,
727 * if any, is under the mouse and records that information. The caller may
728 * supply output parameters to receive the item number of the tab item which
729 * was highlighted but isn't any longer and of the tab item which is now
730 * highlighted but wasn't previously. The caller can use this information to
731 * selectively redraw those tab items.
733 * If the caller has a mouse position, it can supply it through the pos
734 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
735 * supplies NULL and this function determines the current mouse position
743 int* out_redrawLeave
,
747 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
752 if (out_redrawLeave
!= NULL
)
753 *out_redrawLeave
= -1;
754 if (out_redrawEnter
!= NULL
)
755 *out_redrawEnter
= -1;
757 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_HOTTRACK
)
765 ScreenToClient(hwnd
, &pt
);
773 item
= TAB_InternalHitTest(hwnd
, infoPtr
, pt
, &flags
);
776 if (item
!= infoPtr
->iHotTracked
)
778 if (infoPtr
->iHotTracked
>= 0)
780 /* Mark currently hot-tracked to be redrawn to look normal */
781 if (out_redrawLeave
!= NULL
)
782 *out_redrawLeave
= infoPtr
->iHotTracked
;
786 /* Kill timer which forces recheck of mouse pos */
787 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
792 /* Start timer so we recheck mouse pos */
793 UINT timerID
= SetTimer
797 TAB_HOTTRACK_TIMER_INTERVAL
,
798 TAB_HotTrackTimerProc
802 return; /* Hot tracking not available */
805 infoPtr
->iHotTracked
= item
;
809 /* Mark new hot-tracked to be redrawn to look highlighted */
810 if (out_redrawEnter
!= NULL
)
811 *out_redrawEnter
= item
;
816 /******************************************************************************
819 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
822 TAB_MouseMove (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
827 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
829 if (infoPtr
->hwndToolTip
)
830 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
831 WM_LBUTTONDOWN
, wParam
, lParam
);
833 /* Determine which tab to highlight. Redraw tabs which change highlight
835 TAB_RecalcHotTrack(hwnd
, &lParam
, &redrawLeave
, &redrawEnter
);
837 if (redrawLeave
!= -1)
838 TAB_DrawLoneItemInterior(hwnd
, infoPtr
, redrawLeave
);
839 if (redrawEnter
!= -1)
840 TAB_DrawLoneItemInterior(hwnd
, infoPtr
, redrawEnter
);
845 /******************************************************************************
848 * Calculates the tab control's display area given the window rectangle or
849 * the window rectangle given the requested display rectangle.
851 static LRESULT
TAB_AdjustRect(
856 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
857 DWORD lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
859 if(lStyle
& TCS_VERTICAL
)
861 if (fLarger
) /* Go from display rectangle */
863 /* Add the height of the tabs. */
864 if (lStyle
& TCS_BOTTOM
)
865 prc
->right
+= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
867 prc
->left
-= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
869 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
870 /* Inflate the rectangle for the padding */
871 InflateRect(prc
, DISPLAY_AREA_PADDINGX
, DISPLAY_AREA_PADDINGY
);
873 /* Inflate for the border */
874 InflateRect(prc
, CONTROL_BORDER_SIZEX
, CONTROL_BORDER_SIZEX
);
876 else /* Go from window rectangle. */
878 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
879 /* Deflate the rectangle for the border */
880 InflateRect(prc
, -CONTROL_BORDER_SIZEX
, -CONTROL_BORDER_SIZEX
);
882 /* Deflate the rectangle for the padding */
883 InflateRect(prc
, -DISPLAY_AREA_PADDINGX
, -DISPLAY_AREA_PADDINGY
);
885 /* Remove the height of the tabs. */
886 if (lStyle
& TCS_BOTTOM
)
887 prc
->right
-= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
889 prc
->left
+= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
893 if (fLarger
) /* Go from display rectangle */
895 /* Add the height of the tabs. */
896 if (lStyle
& TCS_BOTTOM
)
897 prc
->bottom
+= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
899 prc
->top
-= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
901 /* Inflate the rectangle for the padding */
902 InflateRect(prc
, DISPLAY_AREA_PADDINGX
, DISPLAY_AREA_PADDINGY
);
904 /* Inflate for the border */
905 InflateRect(prc
, CONTROL_BORDER_SIZEX
, CONTROL_BORDER_SIZEX
);
907 else /* Go from window rectangle. */
909 /* Deflate the rectangle for the border */
910 InflateRect(prc
, -CONTROL_BORDER_SIZEX
, -CONTROL_BORDER_SIZEX
);
912 /* Deflate the rectangle for the padding */
913 InflateRect(prc
, -DISPLAY_AREA_PADDINGX
, -DISPLAY_AREA_PADDINGY
);
915 /* Remove the height of the tabs. */
916 if (lStyle
& TCS_BOTTOM
)
917 prc
->bottom
-= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
919 prc
->top
+= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
926 /******************************************************************************
929 * This method will handle the notification from the scroll control and
930 * perform the scrolling operation on the tab control.
932 static LRESULT
TAB_OnHScroll(
938 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
940 if(nScrollCode
== SB_THUMBPOSITION
&& nPos
!= infoPtr
->leftmostVisible
)
942 if(nPos
< infoPtr
->leftmostVisible
)
943 infoPtr
->leftmostVisible
--;
945 infoPtr
->leftmostVisible
++;
947 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
948 TAB_InvalidateTabArea(hwnd
, infoPtr
);
949 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
950 MAKELONG(infoPtr
->leftmostVisible
, 0));
956 /******************************************************************************
959 * This method will check the current scrolling state and make sure the
960 * scrolling control is displayed (or not).
962 static void TAB_SetupScrolling(
965 const RECT
* clientRect
)
968 DWORD lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
970 if (infoPtr
->needsScrolling
)
976 * Calculate the position of the scroll control.
978 if(lStyle
& TCS_VERTICAL
)
980 controlPos
.right
= clientRect
->right
;
981 controlPos
.left
= controlPos
.right
- 2 * GetSystemMetrics(SM_CXHSCROLL
);
983 if (lStyle
& TCS_BOTTOM
)
985 controlPos
.top
= clientRect
->bottom
- infoPtr
->tabHeight
;
986 controlPos
.bottom
= controlPos
.top
+ GetSystemMetrics(SM_CYHSCROLL
);
990 controlPos
.bottom
= clientRect
->top
+ infoPtr
->tabHeight
;
991 controlPos
.top
= controlPos
.bottom
- GetSystemMetrics(SM_CYHSCROLL
);
996 controlPos
.right
= clientRect
->right
;
997 controlPos
.left
= controlPos
.right
- 2 * GetSystemMetrics(SM_CXHSCROLL
);
999 if (lStyle
& TCS_BOTTOM
)
1001 controlPos
.top
= clientRect
->bottom
- infoPtr
->tabHeight
;
1002 controlPos
.bottom
= controlPos
.top
+ GetSystemMetrics(SM_CYHSCROLL
);
1006 controlPos
.bottom
= clientRect
->top
+ infoPtr
->tabHeight
;
1007 controlPos
.top
= controlPos
.bottom
- GetSystemMetrics(SM_CYHSCROLL
);
1012 * If we don't have a scroll control yet, we want to create one.
1013 * If we have one, we want to make sure it's positioned properly.
1015 if (infoPtr
->hwndUpDown
==0)
1017 infoPtr
->hwndUpDown
= CreateWindowA("msctls_updown32",
1019 WS_VISIBLE
| WS_CHILD
| UDS_HORZ
,
1020 controlPos
.left
, controlPos
.top
,
1021 controlPos
.right
- controlPos
.left
,
1022 controlPos
.bottom
- controlPos
.top
,
1030 SetWindowPos(infoPtr
->hwndUpDown
,
1032 controlPos
.left
, controlPos
.top
,
1033 controlPos
.right
- controlPos
.left
,
1034 controlPos
.bottom
- controlPos
.top
,
1035 SWP_SHOWWINDOW
| SWP_NOZORDER
);
1038 /* Now calculate upper limit of the updown control range.
1039 * We do this by calculating how many tabs will be offscreen when the
1040 * last tab is visible.
1042 if(infoPtr
->uNumItem
)
1044 vsize
= clientRect
->right
- (controlPos
.right
- controlPos
.left
+ 1);
1045 maxRange
= infoPtr
->uNumItem
;
1046 tabwidth
= infoPtr
->items
[maxRange
- 1].rect
.right
;
1048 for(; maxRange
> 0; maxRange
--)
1050 if(tabwidth
- infoPtr
->items
[maxRange
- 1].rect
.left
> vsize
)
1054 if(maxRange
== infoPtr
->uNumItem
)
1060 /* If we once had a scroll control... hide it */
1061 if (infoPtr
->hwndUpDown
!=0)
1062 ShowWindow(infoPtr
->hwndUpDown
, SW_HIDE
);
1064 if (infoPtr
->hwndUpDown
)
1065 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETRANGE32
, 0, maxRange
);
1068 /******************************************************************************
1071 * This method will calculate the position rectangles of all the items in the
1072 * control. The rectangle calculated starts at 0 for the first item in the
1073 * list and ignores scrolling and selection.
1074 * It also uses the current font to determine the height of the tab row and
1075 * it checks if all the tabs fit in the client area of the window. If they
1076 * don't, a scrolling control is added.
1078 static void TAB_SetItemBounds (HWND hwnd
)
1080 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
1081 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1082 TEXTMETRICA fontMetrics
;
1085 INT curItemRowCount
;
1086 HFONT hFont
, hOldFont
;
1096 * We need to get text information so we need a DC and we need to select
1101 hFont
= infoPtr
->hFont
? infoPtr
->hFont
: GetStockObject (SYSTEM_FONT
);
1102 hOldFont
= SelectObject (hdc
, hFont
);
1105 * We will base the rectangle calculations on the client rectangle
1108 GetClientRect(hwnd
, &clientRect
);
1110 /* if TCS_VERTICAL then swap the height and width so this code places the
1111 tabs along the top of the rectangle and we can just rotate them after
1112 rather than duplicate all of the below code */
1113 if(lStyle
& TCS_VERTICAL
)
1115 iTemp
= clientRect
.bottom
;
1116 clientRect
.bottom
= clientRect
.right
;
1117 clientRect
.right
= iTemp
;
1120 /* Now use hPadding and vPadding */
1121 infoPtr
->uHItemPadding
= infoPtr
->uHItemPadding_s
;
1122 infoPtr
->uVItemPadding
= infoPtr
->uVItemPadding_s
;
1124 /* The leftmost item will be "0" aligned */
1126 curItemRowCount
= infoPtr
->uNumItem
? 1 : 0;
1128 if (!(infoPtr
->fHeightSet
))
1131 int icon_height
= 0;
1133 /* Use the current font to determine the height of a tab. */
1134 GetTextMetricsA(hdc
, &fontMetrics
);
1136 /* Get the icon height */
1138 ImageList_GetIconSize(infoPtr
->himl
, 0, &icon_height
);
1140 /* Take the highest between font or icon */
1141 if (fontMetrics
.tmHeight
> icon_height
)
1142 item_height
= fontMetrics
.tmHeight
+ 2;
1144 item_height
= icon_height
;
1147 * Make sure there is enough space for the letters + icon + growing the
1148 * selected item + extra space for the selected item.
1150 infoPtr
->tabHeight
= item_height
+ SELECTED_TAB_OFFSET
+
1151 ((lStyle
& TCS_BUTTONS
) ? 2 : 1) *
1152 infoPtr
->uVItemPadding
;
1154 TRACE("tabH=%d, tmH=%ld, iconh=%d\n",
1155 infoPtr
->tabHeight
, fontMetrics
.tmHeight
, icon_height
);
1158 TRACE("client right=%ld\n", clientRect
.right
);
1160 /* Get the icon width */
1163 ImageList_GetIconSize(infoPtr
->himl
, &icon_width
, 0);
1165 if (lStyle
& TCS_FIXEDWIDTH
)
1168 /* Add padding if icon is present */
1169 icon_width
+= infoPtr
->uHItemPadding
;
1172 for (curItem
= 0; curItem
< infoPtr
->uNumItem
; curItem
++)
1174 /* Set the leftmost position of the tab. */
1175 infoPtr
->items
[curItem
].rect
.left
= curItemLeftPos
;
1177 if (lStyle
& TCS_FIXEDWIDTH
)
1179 infoPtr
->items
[curItem
].rect
.right
= infoPtr
->items
[curItem
].rect
.left
+
1180 max(infoPtr
->tabWidth
, icon_width
);
1186 /* Calculate how wide the tab is depending on the text it contains */
1187 GetTextExtentPoint32W(hdc
, infoPtr
->items
[curItem
].pszText
,
1188 lstrlenW(infoPtr
->items
[curItem
].pszText
), &size
);
1190 infoPtr
->items
[curItem
].rect
.right
= infoPtr
->items
[curItem
].rect
.left
+
1191 size
.cx
+ icon_width
+
1192 num
* infoPtr
->uHItemPadding
;
1193 TRACE("for <%s>, l,r=%ld,%ld, num=%d\n",
1194 debugstr_w(infoPtr
->items
[curItem
].pszText
),
1195 infoPtr
->items
[curItem
].rect
.left
,
1196 infoPtr
->items
[curItem
].rect
.right
,
1201 * Check if this is a multiline tab control and if so
1202 * check to see if we should wrap the tabs
1204 * Wrap all these tabs. We will arange them evenly later.
1208 if (((lStyle
& TCS_MULTILINE
) || (lStyle
& TCS_VERTICAL
)) &&
1209 (infoPtr
->items
[curItem
].rect
.right
> clientRect
.right
))
1211 infoPtr
->items
[curItem
].rect
.right
-=
1212 infoPtr
->items
[curItem
].rect
.left
;
1214 infoPtr
->items
[curItem
].rect
.left
= 0;
1216 TRACE("wrapping <%s>, l,r=%ld,%ld\n",
1217 debugstr_w(infoPtr
->items
[curItem
].pszText
),
1218 infoPtr
->items
[curItem
].rect
.left
,
1219 infoPtr
->items
[curItem
].rect
.right
);
1222 infoPtr
->items
[curItem
].rect
.bottom
= 0;
1223 infoPtr
->items
[curItem
].rect
.top
= curItemRowCount
- 1;
1225 TRACE("TextSize: %li\n", size
.cx
);
1226 TRACE("Rect: T %li, L %li, B %li, R %li\n",
1227 infoPtr
->items
[curItem
].rect
.top
,
1228 infoPtr
->items
[curItem
].rect
.left
,
1229 infoPtr
->items
[curItem
].rect
.bottom
,
1230 infoPtr
->items
[curItem
].rect
.right
);
1233 * The leftmost position of the next item is the rightmost position
1236 if (lStyle
& TCS_BUTTONS
)
1238 curItemLeftPos
= infoPtr
->items
[curItem
].rect
.right
+ BUTTON_SPACINGX
;
1239 if (lStyle
& TCS_FLATBUTTONS
)
1240 curItemLeftPos
+= FLAT_BTN_SPACINGX
;
1243 curItemLeftPos
= infoPtr
->items
[curItem
].rect
.right
;
1246 if (!((lStyle
& TCS_MULTILINE
) || (lStyle
& TCS_VERTICAL
)))
1249 * Check if we need a scrolling control.
1251 infoPtr
->needsScrolling
= (curItemLeftPos
+ (2 * SELECTED_TAB_OFFSET
) >
1254 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1255 if(!infoPtr
->needsScrolling
)
1256 infoPtr
->leftmostVisible
= 0;
1261 * No scrolling in Multiline or Vertical styles.
1263 infoPtr
->needsScrolling
= FALSE
;
1264 infoPtr
->leftmostVisible
= 0;
1266 TAB_SetupScrolling(hwnd
, infoPtr
, &clientRect
);
1268 /* Set the number of rows */
1269 infoPtr
->uNumRows
= curItemRowCount
;
1271 /* Arange all tabs evenly if style says so */
1272 if (!(lStyle
& TCS_RAGGEDRIGHT
) && ((lStyle
& TCS_MULTILINE
) || (lStyle
& TCS_VERTICAL
)) && (infoPtr
->uNumItem
> 0))
1274 INT tabPerRow
,remTab
;
1279 * Ok windows tries to even out the rows. place the same
1280 * number of tabs in each row. So lets give that a shot
1283 tabPerRow
= infoPtr
->uNumItem
/ (infoPtr
->uNumRows
);
1284 remTab
= infoPtr
->uNumItem
% (infoPtr
->uNumRows
);
1286 for (iItm
=0,iRow
=0,iCount
=0,curItemLeftPos
=0;
1287 iItm
<infoPtr
->uNumItem
;
1290 /* normalize the current rect */
1292 /* shift the item to the left side of the clientRect */
1293 infoPtr
->items
[iItm
].rect
.right
-=
1294 infoPtr
->items
[iItm
].rect
.left
;
1295 infoPtr
->items
[iItm
].rect
.left
= 0;
1297 TRACE("r=%ld, cl=%d, cl.r=%ld, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1298 infoPtr
->items
[iItm
].rect
.right
,
1299 curItemLeftPos
, clientRect
.right
,
1300 iCount
, iRow
, infoPtr
->uNumRows
, remTab
, tabPerRow
);
1302 /* if we have reached the maximum number of tabs on this row */
1303 /* move to the next row, reset our current item left position and */
1304 /* the count of items on this row */
1306 /* ************ FIXME FIXME FIXME *************** */
1310 /* if item n and n+1 are in the same row, */
1311 /* then the display has n+1 lower (toward the */
1312 /* bottom) than n. We do it just the */
1315 /* ************ FIXME FIXME FIXME *************** */
1317 if (lStyle
& TCS_VERTICAL
) {
1318 /* Vert: Add the remaining tabs in the *last* remainder rows */
1319 if (iCount
>= ((iRow
>=(INT
)infoPtr
->uNumRows
- remTab
)?tabPerRow
+ 1:tabPerRow
)) {
1325 /* Horz: Add the remaining tabs in the *first* remainder rows */
1326 if (iCount
>= ((iRow
<remTab
)?tabPerRow
+ 1:tabPerRow
)) {
1333 /* shift the item to the right to place it as the next item in this row */
1334 infoPtr
->items
[iItm
].rect
.left
+= curItemLeftPos
;
1335 infoPtr
->items
[iItm
].rect
.right
+= curItemLeftPos
;
1336 infoPtr
->items
[iItm
].rect
.top
= iRow
;
1337 if (lStyle
& TCS_BUTTONS
)
1339 curItemLeftPos
= infoPtr
->items
[iItm
].rect
.right
+ 1;
1340 if (lStyle
& TCS_FLATBUTTONS
)
1341 curItemLeftPos
+= FLAT_BTN_SPACINGX
;
1344 curItemLeftPos
= infoPtr
->items
[iItm
].rect
.right
;
1346 TRACE("arranging <%s>, l,r=%ld,%ld, row=%ld\n",
1347 debugstr_w(infoPtr
->items
[iItm
].pszText
),
1348 infoPtr
->items
[iItm
].rect
.left
,
1349 infoPtr
->items
[iItm
].rect
.right
,
1350 infoPtr
->items
[iItm
].rect
.top
);
1357 INT widthDiff
, iIndexStart
=0, iIndexEnd
=0;
1361 while(iIndexStart
< infoPtr
->uNumItem
)
1364 * find the indexs of the row
1366 /* find the first item on the next row */
1367 for (iIndexEnd
=iIndexStart
;
1368 (iIndexEnd
< infoPtr
->uNumItem
) &&
1369 (infoPtr
->items
[iIndexEnd
].rect
.top
==
1370 infoPtr
->items
[iIndexStart
].rect
.top
) ;
1372 /* intentionally blank */;
1375 * we need to justify these tabs so they fill the whole given
1379 /* find the amount of space remaining on this row */
1380 widthDiff
= clientRect
.right
- (2 * SELECTED_TAB_OFFSET
) -
1381 infoPtr
->items
[iIndexEnd
- 1].rect
.right
;
1383 /* iCount is the number of tab items on this row */
1384 iCount
= iIndexEnd
- iIndexStart
;
1388 remainder
= widthDiff
% iCount
;
1389 widthDiff
= widthDiff
/ iCount
;
1390 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1391 for (iIndex
=iIndexStart
, iCount
=0; iIndex
< iIndexEnd
; iIndex
++, iCount
++)
1393 infoPtr
->items
[iIndex
].rect
.left
+= iCount
* widthDiff
;
1394 infoPtr
->items
[iIndex
].rect
.right
+= (iCount
+ 1) * widthDiff
;
1396 TRACE("adjusting 1 <%s>, l,r=%ld,%ld\n",
1397 debugstr_w(infoPtr
->items
[iIndex
].pszText
),
1398 infoPtr
->items
[iIndex
].rect
.left
,
1399 infoPtr
->items
[iIndex
].rect
.right
);
1402 infoPtr
->items
[iIndex
- 1].rect
.right
+= remainder
;
1404 else /* we have only one item on this row, make it take up the entire row */
1406 infoPtr
->items
[iIndexStart
].rect
.left
= clientRect
.left
;
1407 infoPtr
->items
[iIndexStart
].rect
.right
= clientRect
.right
- 4;
1409 TRACE("adjusting 2 <%s>, l,r=%ld,%ld\n",
1410 debugstr_w(infoPtr
->items
[iIndexStart
].pszText
),
1411 infoPtr
->items
[iIndexStart
].rect
.left
,
1412 infoPtr
->items
[iIndexStart
].rect
.right
);
1417 iIndexStart
= iIndexEnd
;
1422 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1423 if(lStyle
& TCS_VERTICAL
)
1426 for(iIndex
= 0; iIndex
< infoPtr
->uNumItem
; iIndex
++)
1428 rcItem
= &(infoPtr
->items
[iIndex
].rect
);
1430 rcOriginal
= *rcItem
;
1432 /* this is rotating the items by 90 degrees around the center of the control */
1433 rcItem
->top
= (clientRect
.right
- (rcOriginal
.left
- clientRect
.left
)) - (rcOriginal
.right
- rcOriginal
.left
);
1434 rcItem
->bottom
= rcItem
->top
+ (rcOriginal
.right
- rcOriginal
.left
);
1435 rcItem
->left
= rcOriginal
.top
;
1436 rcItem
->right
= rcOriginal
.bottom
;
1440 TAB_EnsureSelectionVisible(hwnd
,infoPtr
);
1441 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
1444 SelectObject (hdc
, hOldFont
);
1445 ReleaseDC (hwnd
, hdc
);
1450 TAB_EraseTabInterior
1458 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1459 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1460 HBRUSH hbr
= CreateSolidBrush (comctl32_color
.clrBtnFace
);
1461 BOOL deleteBrush
= TRUE
;
1462 RECT rTemp
= *drawRect
;
1464 InflateRect(&rTemp
, -2, -2);
1465 if (lStyle
& TCS_BUTTONS
)
1467 if (iItem
== infoPtr
->iSelected
)
1469 /* Background color */
1470 if (!(lStyle
& TCS_OWNERDRAWFIXED
))
1473 hbr
= GetSysColorBrush(COLOR_SCROLLBAR
);
1475 SetTextColor(hdc
, comctl32_color
.clr3dFace
);
1476 SetBkColor(hdc
, comctl32_color
.clr3dHilight
);
1478 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1479 * we better use 0x55aa bitmap brush to make scrollbar's background
1480 * look different from the window background.
1482 if (comctl32_color
.clr3dHilight
== comctl32_color
.clrWindow
)
1483 hbr
= COMCTL32_hPattern55AABrush
;
1485 deleteBrush
= FALSE
;
1487 FillRect(hdc
, &rTemp
, hbr
);
1489 else /* ! selected */
1491 if (lStyle
& TCS_FLATBUTTONS
)
1493 FillRect(hdc
, drawRect
, hbr
);
1494 if (iItem
== infoPtr
->iHotTracked
)
1495 DrawEdge(hdc
, drawRect
, EDGE_RAISED
, BF_SOFT
|BF_RECT
);
1498 FillRect(hdc
, &rTemp
, hbr
);
1502 else /* !TCS_BUTTONS */
1504 FillRect(hdc
, &rTemp
, hbr
);
1508 if (deleteBrush
) DeleteObject(hbr
);
1511 /******************************************************************************
1512 * TAB_DrawItemInterior
1514 * This method is used to draw the interior (text and icon) of a single tab
1515 * into the tab control.
1518 TAB_DrawItemInterior
1526 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
1527 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1536 if (drawRect
== NULL
)
1543 * Get the rectangle for the item.
1545 isVisible
= TAB_InternalGetItemRect(hwnd
, infoPtr
, iItem
, &itemRect
, &selectedRect
);
1550 * Make sure drawRect points to something valid; simplifies code.
1552 drawRect
= &localRect
;
1555 * This logic copied from the part of TAB_DrawItem which draws
1556 * the tab background. It's important to keep it in sync. I
1557 * would have liked to avoid code duplication, but couldn't figure
1558 * out how without making spaghetti of TAB_DrawItem.
1560 if (lStyle
& TCS_BUTTONS
)
1562 *drawRect
= itemRect
;
1563 if (iItem
== infoPtr
->iSelected
)
1565 OffsetRect(drawRect
, 1, 1);
1570 if (iItem
== infoPtr
->iSelected
)
1572 *drawRect
= selectedRect
;
1573 if (lStyle
& TCS_BOTTOM
)
1575 if (lStyle
& TCS_VERTICAL
)
1582 drawRect
->left
+= 1;
1587 *drawRect
= itemRect
;
1590 if (lStyle
& TCS_BOTTOM
&& !(lStyle
& TCS_VERTICAL
))
1597 TRACE("drawRect=(%ld,%ld)-(%ld,%ld)\n",
1598 drawRect
->left
, drawRect
->top
, drawRect
->right
, drawRect
->bottom
);
1600 /* Clear interior */
1601 TAB_EraseTabInterior (hwnd
, hdc
, iItem
, drawRect
);
1603 /* Draw the focus rectangle */
1604 if (!(lStyle
& TCS_FOCUSNEVER
) &&
1605 (GetFocus() == hwnd
) &&
1606 (iItem
== infoPtr
->uFocus
) )
1608 RECT rFocus
= *drawRect
;
1609 InflateRect(&rFocus
, -3, -3);
1610 if (lStyle
& TCS_BOTTOM
&& !(lStyle
& TCS_VERTICAL
))
1612 if (lStyle
& TCS_BUTTONS
)
1618 DrawFocusRect(hdc
, &rFocus
);
1624 htextPen
= CreatePen( PS_SOLID
, 1, GetSysColor(COLOR_BTNTEXT
) );
1625 holdPen
= SelectObject(hdc
, htextPen
);
1626 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
1629 * Setup for text output
1631 oldBkMode
= SetBkMode(hdc
, TRANSPARENT
);
1632 SetTextColor(hdc
, (((iItem
== infoPtr
->iHotTracked
) && !(lStyle
& TCS_FLATBUTTONS
)) |
1633 (infoPtr
->items
[iItem
].dwState
& TCIS_HIGHLIGHTED
)) ?
1634 comctl32_color
.clrHighlight
: comctl32_color
.clrBtnText
);
1637 * if owner draw, tell the owner to draw
1639 if ((lStyle
& TCS_OWNERDRAWFIXED
) && GetParent(hwnd
))
1645 * get the control id
1647 id
= GetWindowLongA( hwnd
, GWL_ID
);
1650 * put together the DRAWITEMSTRUCT
1652 dis
.CtlType
= ODT_TAB
;
1655 dis
.itemAction
= ODA_DRAWENTIRE
;
1657 if ( iItem
== infoPtr
->iSelected
)
1658 dis
.itemState
|= ODS_SELECTED
;
1659 if (infoPtr
->uFocus
== iItem
)
1660 dis
.itemState
|= ODS_FOCUS
;
1661 dis
.hwndItem
= hwnd
; /* */
1663 CopyRect(&dis
.rcItem
,drawRect
);
1664 InflateRect(&dis
.rcItem
, -2, -2);
1665 dis
.itemData
= infoPtr
->items
[iItem
].lParam
;
1668 * send the draw message
1670 SendMessageA( infoPtr
->hwndNotify
, WM_DRAWITEM
, (WPARAM
)id
, (LPARAM
)&dis
);
1677 /* used to center the icon and text in the tab */
1679 INT center_offset_h
, center_offset_v
;
1681 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1682 rcImage
= *drawRect
;
1686 rcText
.left
= rcText
.top
= rcText
.right
= rcText
.bottom
= 0;
1688 /* get the rectangle that the text fits in */
1689 DrawTextW(hdc
, infoPtr
->items
[iItem
].pszText
, -1,
1690 &rcText
, DT_CALCRECT
);
1692 * If not owner draw, then do the drawing ourselves.
1696 if (infoPtr
->himl
&& (infoPtr
->items
[iItem
].mask
& TCIF_IMAGE
))
1701 ImageList_GetIconSize(infoPtr
->himl
, &cx
, &cy
);
1703 if(lStyle
& TCS_VERTICAL
)
1705 center_offset_h
= ((drawRect
->bottom
- drawRect
->top
) - (cy
+ infoPtr
->uHItemPadding
+ (rcText
.right
- rcText
.left
))) / 2;
1706 center_offset_v
= ((drawRect
->right
- drawRect
->left
) - (cx
+ infoPtr
->uVItemPadding
)) / 2;
1710 center_offset_h
= ((drawRect
->right
- drawRect
->left
) - (cx
+ infoPtr
->uHItemPadding
+ (rcText
.right
- rcText
.left
))) / 2;
1711 center_offset_v
= ((drawRect
->bottom
- drawRect
->top
) - (cy
+ infoPtr
->uVItemPadding
)) / 2;
1714 if (lStyle
& TCS_FIXEDWIDTH
&& lStyle
& (TCS_FORCELABELLEFT
| TCS_FORCEICONLEFT
))
1715 center_offset_h
= infoPtr
->uHItemPadding
;
1717 if (center_offset_h
< 2)
1718 center_offset_h
= 2;
1720 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1721 debugstr_w(infoPtr
->items
[iItem
].pszText
), center_offset_h
, center_offset_v
,
1722 drawRect
->left
, drawRect
->top
, drawRect
->right
, drawRect
->bottom
,
1723 (rcText
.right
-rcText
.left
));
1725 if((lStyle
& TCS_VERTICAL
) && (lStyle
& TCS_BOTTOM
))
1727 rcImage
.top
= drawRect
->top
+ center_offset_h
;
1728 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1729 /* right side of the tab, but the image still uses the left as its x position */
1730 /* this keeps the image always drawn off of the same side of the tab */
1731 rcImage
.left
= drawRect
->right
- cx
- center_offset_v
;
1732 drawRect
->top
+= cy
+ infoPtr
->uHItemPadding
;
1734 else if(lStyle
& TCS_VERTICAL
)
1736 rcImage
.top
= drawRect
->bottom
- cy
- center_offset_h
;
1737 rcImage
.left
= drawRect
->left
+ center_offset_v
;
1738 drawRect
->bottom
-= cy
+ infoPtr
->uHItemPadding
;
1740 else /* normal style, whether TCS_BOTTOM or not */
1742 rcImage
.left
= drawRect
->left
+ center_offset_h
;
1743 rcImage
.top
= drawRect
->top
+ center_offset_v
;
1744 drawRect
->left
+= cx
+ infoPtr
->uHItemPadding
;
1747 TRACE("drawing image=%d, left=%ld, top=%ld\n",
1748 infoPtr
->items
[iItem
].iImage
, rcImage
.left
, rcImage
.top
-1);
1752 infoPtr
->items
[iItem
].iImage
,
1760 /* Now position text */
1761 if (lStyle
& TCS_FIXEDWIDTH
&& lStyle
& TCS_FORCELABELLEFT
)
1762 center_offset_h
= infoPtr
->uHItemPadding
;
1764 if(lStyle
& TCS_VERTICAL
)
1765 center_offset_h
= ((drawRect
->bottom
- drawRect
->top
) - (rcText
.right
- rcText
.left
)) / 2;
1767 center_offset_h
= ((drawRect
->right
- drawRect
->left
) - (rcText
.right
- rcText
.left
)) / 2;
1769 if(lStyle
& TCS_VERTICAL
)
1771 if(lStyle
& TCS_BOTTOM
)
1772 drawRect
->top
+=center_offset_h
;
1774 drawRect
->bottom
-=center_offset_h
;
1776 drawRect
->left
+= ((drawRect
->right
- drawRect
->left
) - ((rcText
.bottom
- rcText
.top
) + infoPtr
->uVItemPadding
)) / 2;
1780 drawRect
->left
+= center_offset_h
;
1781 drawRect
->top
+= ((drawRect
->bottom
- drawRect
->top
) - ((rcText
.bottom
- rcText
.top
) + infoPtr
->uVItemPadding
)) / 2;
1785 if(lStyle
& TCS_VERTICAL
) /* if we are vertical rotate the text and each character */
1789 INT nEscapement
= 900;
1790 INT nOrientation
= 900;
1792 if(lStyle
& TCS_BOTTOM
)
1795 nOrientation
= -900;
1798 /* to get a font with the escapement and orientation we are looking for, we need to */
1799 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1800 if (!GetObjectA((infoPtr
->hFont
) ?
1801 infoPtr
->hFont
: GetStockObject(SYSTEM_FONT
),
1802 sizeof(LOGFONTA
),&logfont
))
1806 lstrcpyA(logfont
.lfFaceName
, "Arial");
1807 logfont
.lfHeight
= -MulDiv(iPointSize
, GetDeviceCaps(hdc
, LOGPIXELSY
),
1809 logfont
.lfWeight
= FW_NORMAL
;
1810 logfont
.lfItalic
= 0;
1811 logfont
.lfUnderline
= 0;
1812 logfont
.lfStrikeOut
= 0;
1815 logfont
.lfEscapement
= nEscapement
;
1816 logfont
.lfOrientation
= nOrientation
;
1817 hFont
= CreateFontIndirectA(&logfont
);
1818 SelectObject(hdc
, hFont
);
1821 (lStyle
& TCS_BOTTOM
) ? drawRect
->right
: drawRect
->left
,
1822 (!(lStyle
& TCS_BOTTOM
)) ? drawRect
->bottom
: drawRect
->top
,
1825 infoPtr
->items
[iItem
].pszText
,
1826 lstrlenW(infoPtr
->items
[iItem
].pszText
),
1829 DeleteObject(hFont
);
1836 infoPtr
->items
[iItem
].pszText
,
1837 lstrlenW(infoPtr
->items
[iItem
].pszText
),
1839 DT_LEFT
| DT_SINGLELINE
1843 *drawRect
= rcTemp
; /* restore drawRect */
1849 SelectObject(hdc
, hOldFont
);
1850 SetBkMode(hdc
, oldBkMode
);
1851 SelectObject(hdc
, holdPen
);
1852 DeleteObject( htextPen
);
1855 /******************************************************************************
1858 * This method is used to draw a single tab into the tab control.
1860 static void TAB_DrawItem(
1865 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1866 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1870 RECT r
, fillRect
, r1
;
1873 COLORREF bkgnd
, corner
;
1876 * Get the rectangle for the item.
1878 isVisible
= TAB_InternalGetItemRect(hwnd
,
1888 /* Clip UpDown control to not draw over it */
1889 if (infoPtr
->needsScrolling
)
1891 GetWindowRect(hwnd
, &rC
);
1892 GetWindowRect(infoPtr
->hwndUpDown
, &rUD
);
1893 ExcludeClipRect(hdc
, rUD
.left
- rC
.left
, rUD
.top
- rC
.top
, rUD
.right
- rC
.left
, rUD
.bottom
- rC
.top
);
1896 /* If you need to see what the control is doing,
1897 * then override these variables. They will change what
1898 * fill colors are used for filling the tabs, and the
1899 * corners when drawing the edge.
1901 bkgnd
= comctl32_color
.clrBtnFace
;
1902 corner
= comctl32_color
.clrBtnFace
;
1904 if (lStyle
& TCS_BUTTONS
)
1906 /* Get item rectangle */
1909 /* Separators between flat buttons */
1910 if (lStyle
& TCS_FLATBUTTONS
)
1913 r1
.right
+= (FLAT_BTN_SPACINGX
-2);
1914 DrawEdge(hdc
, &r1
, EDGE_ETCHED
, BF_RIGHT
);
1917 if (iItem
== infoPtr
->iSelected
)
1919 DrawEdge(hdc
, &r
, EDGE_SUNKEN
, BF_SOFT
|BF_RECT
);
1921 OffsetRect(&r
, 1, 1);
1923 else /* ! selected */
1925 if (!(lStyle
& TCS_FLATBUTTONS
))
1926 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_RECT
);
1929 else /* !TCS_BUTTONS */
1931 /* We draw a rectangle of different sizes depending on the selection
1933 if (iItem
== infoPtr
->iSelected
) {
1935 GetClientRect (hwnd
, &rect
);
1936 clRight
= rect
.right
;
1937 clBottom
= rect
.bottom
;
1944 * Erase the background. (Delay it but setup rectangle.)
1945 * This is necessary when drawing the selected item since it is larger
1946 * than the others, it might overlap with stuff already drawn by the
1951 if(lStyle
& TCS_VERTICAL
)
1953 /* These are for adjusting the drawing of a Selected tab */
1954 /* The initial values are for the normal case of non-Selected */
1955 int ZZ
= 1; /* Do not strech if selected */
1956 if (iItem
== infoPtr
->iSelected
) {
1959 /* if leftmost draw the line longer */
1960 if(selectedRect
.top
== 0)
1962 /* if rightmost draw the line longer */
1963 if(selectedRect
.bottom
== clBottom
)
1964 fillRect
.bottom
-= 2;
1967 if (lStyle
& TCS_BOTTOM
)
1969 /* Adjust both rectangles to match native */
1972 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1974 fillRect
.left
,fillRect
.top
,fillRect
.right
,fillRect
.bottom
,
1975 r
.left
,r
.top
,r
.right
,r
.bottom
);
1977 /* Clear interior */
1978 SetBkColor(hdc
, bkgnd
);
1979 ExtTextOutA(hdc
, 0, 0, 2, &fillRect
, NULL
, 0, 0);
1981 /* Draw rectangular edge around tab */
1982 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_RIGHT
|BF_TOP
|BF_BOTTOM
);
1984 /* Now erase the top corner and draw diagonal edge */
1985 SetBkColor(hdc
, corner
);
1986 r1
.left
= r
.right
- ROUND_CORNER_SIZE
- 1;
1989 r1
.bottom
= r1
.top
+ ROUND_CORNER_SIZE
;
1990 ExtTextOutA(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
1992 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDTOPLEFT
);
1994 /* Now erase the bottom corner and draw diagonal edge */
1995 r1
.left
= r
.right
- ROUND_CORNER_SIZE
- 1;
1996 r1
.bottom
= r
.bottom
;
1998 r1
.top
= r1
.bottom
- ROUND_CORNER_SIZE
;
1999 ExtTextOutA(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2001 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDBOTTOMLEFT
);
2003 if ((iItem
== infoPtr
->iSelected
) && (selectedRect
.top
== 0)) {
2007 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_TOP
);
2013 /* Adjust both rectangles to match native */
2014 fillRect
.right
+= (1-ZZ
);
2016 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2018 fillRect
.left
,fillRect
.top
,fillRect
.right
,fillRect
.bottom
,
2019 r
.left
,r
.top
,r
.right
,r
.bottom
);
2021 /* Clear interior */
2022 SetBkColor(hdc
, bkgnd
);
2023 ExtTextOutA(hdc
, 0, 0, 2, &fillRect
, NULL
, 0, 0);
2025 /* Draw rectangular edge around tab */
2026 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_LEFT
|BF_TOP
|BF_BOTTOM
);
2028 /* Now erase the top corner and draw diagonal edge */
2029 SetBkColor(hdc
, corner
);
2032 r1
.right
= r1
.left
+ ROUND_CORNER_SIZE
+ 1;
2033 r1
.bottom
= r1
.top
+ ROUND_CORNER_SIZE
;
2034 ExtTextOutA(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2036 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDTOPRIGHT
);
2038 /* Now erase the bottom corner and draw diagonal edge */
2040 r1
.bottom
= r
.bottom
;
2041 r1
.right
= r1
.left
+ ROUND_CORNER_SIZE
+ 1;
2042 r1
.top
= r1
.bottom
- ROUND_CORNER_SIZE
;
2043 ExtTextOutA(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2045 DrawEdge(hdc
, &r1
, EDGE_SUNKEN
, BF_DIAGONAL_ENDTOPLEFT
);
2048 else /* ! TCS_VERTICAL */
2050 /* These are for adjusting the drawing of a Selected tab */
2051 /* The initial values are for the normal case of non-Selected */
2052 int ZZ
= 1; /* Do not strech if selected */
2053 if (iItem
== infoPtr
->iSelected
) {
2056 /* if leftmost draw the line longer */
2057 if(selectedRect
.left
== 0)
2059 /* if rightmost draw the line longer */
2060 if(selectedRect
.right
== clRight
)
2061 fillRect
.right
-= 2;
2064 if (lStyle
& TCS_BOTTOM
)
2067 /* Adjust both rectangles to match native */
2073 TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2075 fillRect
.left
,fillRect
.top
,fillRect
.right
,fillRect
.bottom
,
2076 r
.left
,r
.top
,r
.right
,r
.bottom
);
2078 /* Clear interior */
2079 SetBkColor(hdc
, bkgnd
);
2080 ExtTextOutA(hdc
, 0, 0, 2, &fillRect
, NULL
, 0, 0);
2082 /* Draw rectangular edge around tab */
2083 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_LEFT
|BF_BOTTOM
|BF_RIGHT
);
2085 /* Now erase the righthand corner and draw diagonal edge */
2086 SetBkColor(hdc
, corner
);
2087 r1
.left
= r
.right
- ROUND_CORNER_SIZE
;
2088 r1
.bottom
= r
.bottom
;
2090 r1
.top
= r1
.bottom
- ROUND_CORNER_SIZE
- 1;
2091 ExtTextOutA(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2093 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDBOTTOMLEFT
);
2095 /* Now erase the lefthand corner and draw diagonal edge */
2097 r1
.bottom
= r
.bottom
;
2098 r1
.right
= r1
.left
+ ROUND_CORNER_SIZE
;
2099 r1
.top
= r1
.bottom
- ROUND_CORNER_SIZE
- 1;
2100 ExtTextOutA(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2102 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDTOPLEFT
);
2104 if (iItem
== infoPtr
->iSelected
)
2108 if (selectedRect
.left
== 0)
2113 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_LEFT
);
2121 /* Adjust both rectangles to match native */
2122 fillRect
.bottom
+= (1-ZZ
);
2124 TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2126 fillRect
.left
,fillRect
.top
,fillRect
.right
,fillRect
.bottom
,
2127 r
.left
,r
.top
,r
.right
,r
.bottom
);
2129 /* Clear interior */
2130 SetBkColor(hdc
, bkgnd
);
2131 ExtTextOutA(hdc
, 0, 0, 2, &fillRect
, NULL
, 0, 0);
2133 /* Draw rectangular edge around tab */
2134 DrawEdge(hdc
, &r
, EDGE_RAISED
, BF_SOFT
|BF_LEFT
|BF_TOP
|BF_RIGHT
);
2136 /* Now erase the righthand corner and draw diagonal edge */
2137 SetBkColor(hdc
, corner
);
2138 r1
.left
= r
.right
- ROUND_CORNER_SIZE
;
2141 r1
.bottom
= r1
.top
+ ROUND_CORNER_SIZE
+ 1;
2142 ExtTextOutA(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2144 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDBOTTOMRIGHT
);
2146 /* Now erase the lefthand corner and draw diagonal edge */
2149 r1
.right
= r1
.left
+ ROUND_CORNER_SIZE
;
2150 r1
.bottom
= r1
.top
+ ROUND_CORNER_SIZE
+ 1;
2151 ExtTextOutA(hdc
, 0, 0, 2, &r1
, NULL
, 0, 0);
2153 DrawEdge(hdc
, &r1
, EDGE_RAISED
, BF_SOFT
|BF_DIAGONAL_ENDTOPRIGHT
);
2159 TAB_DumpItemInternal(infoPtr
, iItem
);
2161 /* This modifies r to be the text rectangle. */
2162 TAB_DrawItemInterior(hwnd
, hdc
, iItem
, &r
);
2166 /******************************************************************************
2169 * This method is used to draw the raised border around the tab control
2172 static void TAB_DrawBorder (HWND hwnd
, HDC hdc
)
2174 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2176 DWORD lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
2178 GetClientRect (hwnd
, &rect
);
2181 * Adjust for the style
2184 if (infoPtr
->uNumItem
)
2186 if ((lStyle
& TCS_BOTTOM
) && !(lStyle
& TCS_VERTICAL
))
2188 rect
.bottom
-= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 3;
2190 else if((lStyle
& TCS_BOTTOM
) && (lStyle
& TCS_VERTICAL
))
2192 rect
.right
-= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
2194 else if(lStyle
& TCS_VERTICAL
)
2196 rect
.left
+= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
2198 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2200 rect
.top
+= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
2204 TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2205 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2207 DrawEdge(hdc
, &rect
, EDGE_RAISED
, BF_SOFT
|BF_RECT
);
2210 /******************************************************************************
2213 * This method repaints the tab control..
2215 static void TAB_Refresh (HWND hwnd
, HDC hdc
)
2217 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2221 if (!infoPtr
->DoRedraw
)
2224 hOldFont
= SelectObject (hdc
, infoPtr
->hFont
);
2226 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BUTTONS
)
2228 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
2229 TAB_DrawItem (hwnd
, hdc
, i
);
2233 /* Draw all the non selected item first */
2234 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
2236 if (i
!= infoPtr
->iSelected
)
2237 TAB_DrawItem (hwnd
, hdc
, i
);
2240 /* Now, draw the border, draw it before the selected item
2241 * since the selected item overwrites part of the border. */
2242 TAB_DrawBorder (hwnd
, hdc
);
2244 /* Then, draw the selected item */
2245 TAB_DrawItem (hwnd
, hdc
, infoPtr
->iSelected
);
2247 /* If we haven't set the current focus yet, set it now.
2248 * Only happens when we first paint the tab controls */
2249 if (infoPtr
->uFocus
== -1)
2250 TAB_SetCurFocus(hwnd
, infoPtr
->iSelected
);
2253 SelectObject (hdc
, hOldFont
);
2257 TAB_GetRowCount (HWND hwnd
)
2259 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2261 return infoPtr
->uNumRows
;
2265 TAB_SetRedraw (HWND hwnd
, WPARAM wParam
)
2267 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2269 infoPtr
->DoRedraw
=(BOOL
) wParam
;
2273 static LRESULT
TAB_EraseBackground(
2280 HBRUSH brush
= CreateSolidBrush(comctl32_color
.clrBtnFace
);
2282 hdc
= givenDC
? givenDC
: GetDC(hwnd
);
2284 GetClientRect(hwnd
, &clientRect
);
2286 FillRect(hdc
, &clientRect
, brush
);
2289 ReleaseDC(hwnd
, hdc
);
2291 DeleteObject(brush
);
2296 /******************************************************************************
2297 * TAB_EnsureSelectionVisible
2299 * This method will make sure that the current selection is completely
2300 * visible by scrolling until it is.
2302 static void TAB_EnsureSelectionVisible(
2306 INT iSelected
= infoPtr
->iSelected
;
2307 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
2308 INT iOrigLeftmostVisible
= infoPtr
->leftmostVisible
;
2310 /* set the items row to the bottommost row or topmost row depending on
2312 if ((infoPtr
->uNumRows
> 1) && !(lStyle
& TCS_BUTTONS
))
2317 if(lStyle
& TCS_VERTICAL
)
2318 newselected
= infoPtr
->items
[iSelected
].rect
.left
;
2320 newselected
= infoPtr
->items
[iSelected
].rect
.top
;
2322 /* the target row is always (number of rows - 1)
2323 as row 0 is furthest from the clientRect */
2324 iTargetRow
= infoPtr
->uNumRows
- 1;
2326 if (newselected
!= iTargetRow
)
2329 if(lStyle
& TCS_VERTICAL
)
2331 for (i
=0; i
< infoPtr
->uNumItem
; i
++)
2333 /* move everything in the row of the selected item to the iTargetRow */
2334 if (infoPtr
->items
[i
].rect
.left
== newselected
)
2335 infoPtr
->items
[i
].rect
.left
= iTargetRow
;
2338 if (infoPtr
->items
[i
].rect
.left
> newselected
)
2339 infoPtr
->items
[i
].rect
.left
-=1;
2345 for (i
=0; i
< infoPtr
->uNumItem
; i
++)
2347 if (infoPtr
->items
[i
].rect
.top
== newselected
)
2348 infoPtr
->items
[i
].rect
.top
= iTargetRow
;
2351 if (infoPtr
->items
[i
].rect
.top
> newselected
)
2352 infoPtr
->items
[i
].rect
.top
-=1;
2356 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
2361 * Do the trivial cases first.
2363 if ( (!infoPtr
->needsScrolling
) ||
2364 (infoPtr
->hwndUpDown
==0) || (lStyle
& TCS_VERTICAL
))
2367 if (infoPtr
->leftmostVisible
>= iSelected
)
2369 infoPtr
->leftmostVisible
= iSelected
;
2376 /* Calculate the part of the client area that is visible */
2377 GetClientRect(hwnd
, &r
);
2380 GetClientRect(infoPtr
->hwndUpDown
, &r
);
2383 if ((infoPtr
->items
[iSelected
].rect
.right
-
2384 infoPtr
->items
[iSelected
].rect
.left
) >= width
)
2386 /* Special case: width of selected item is greater than visible
2389 infoPtr
->leftmostVisible
= iSelected
;
2393 for (i
= infoPtr
->leftmostVisible
; i
< infoPtr
->uNumItem
; i
++)
2395 if ((infoPtr
->items
[iSelected
].rect
.right
-
2396 infoPtr
->items
[i
].rect
.left
) < width
)
2399 infoPtr
->leftmostVisible
= i
;
2403 if (infoPtr
->leftmostVisible
!= iOrigLeftmostVisible
)
2404 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
2406 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
2407 MAKELONG(infoPtr
->leftmostVisible
, 0));
2410 /******************************************************************************
2411 * TAB_InvalidateTabArea
2413 * This method will invalidate the portion of the control that contains the
2414 * tabs. It is called when the state of the control changes and needs
2417 static void TAB_InvalidateTabArea(
2421 RECT clientRect
, rInvalidate
;
2422 DWORD lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
2423 INT lastRow
= infoPtr
->uNumRows
- 1;
2426 if (lastRow
< 0) return;
2428 GetClientRect(hwnd
, &clientRect
);
2429 rInvalidate
= clientRect
;
2431 TAB_InternalGetItemRect(hwnd
, infoPtr
, infoPtr
->uNumItem
-1 , &rect
, NULL
);
2432 if ((lStyle
& TCS_BOTTOM
) && !(lStyle
& TCS_VERTICAL
))
2434 rInvalidate
.top
= clientRect
.bottom
-
2435 infoPtr
->tabHeight
-
2436 lastRow
* (infoPtr
->tabHeight
- 2) -
2437 ((lStyle
& TCS_BUTTONS
) ? lastRow
* BUTTON_SPACINGY
: 0) - 3;
2438 rInvalidate
.right
= clientRect
.left
+ rect
.right
+ 2 * SELECTED_TAB_OFFSET
;
2440 else if((lStyle
& TCS_BOTTOM
) && (lStyle
& TCS_VERTICAL
))
2442 rInvalidate
.left
= clientRect
.right
- infoPtr
->tabHeight
-
2443 lastRow
* (infoPtr
->tabHeight
- 2) -
2444 ((lStyle
& TCS_BUTTONS
) ? lastRow
* BUTTON_SPACINGY
: 0) - 2;
2445 rInvalidate
.bottom
= clientRect
.top
+ rect
.bottom
+ 2 * SELECTED_TAB_OFFSET
;
2447 else if(lStyle
& TCS_VERTICAL
)
2449 rInvalidate
.right
= clientRect
.left
+ infoPtr
->tabHeight
+
2450 lastRow
* (infoPtr
->tabHeight
- 2) -
2451 ((lStyle
& TCS_BUTTONS
) ? lastRow
* BUTTON_SPACINGY
: 0) + 2;
2452 rInvalidate
.bottom
= clientRect
.top
+ rect
.bottom
+ 2 * SELECTED_TAB_OFFSET
;
2456 rInvalidate
.bottom
= clientRect
.top
+ infoPtr
->tabHeight
+
2457 lastRow
* (infoPtr
->tabHeight
- 2) +
2458 ((lStyle
& TCS_BUTTONS
) ? lastRow
* BUTTON_SPACINGY
: 0) + 2;
2459 rInvalidate
.right
= clientRect
.left
+ rect
.right
+ 2 * SELECTED_TAB_OFFSET
;
2462 /* Punch out the updown control */
2463 if (infoPtr
->needsScrolling
&& (rInvalidate
.right
> 0)) {
2465 GetClientRect(infoPtr
->hwndUpDown
, &r
);
2466 if (rInvalidate
.right
> clientRect
.right
- r
.left
)
2467 rInvalidate
.right
= rInvalidate
.right
- (r
.right
- r
.left
);
2469 rInvalidate
.right
= clientRect
.right
- r
.left
;
2472 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2473 clientRect
.left
,clientRect
.top
,
2474 clientRect
.right
,clientRect
.bottom
);
2476 InvalidateRect(hwnd
, &rInvalidate
, TRUE
);
2480 TAB_Paint (HWND hwnd
, WPARAM wParam
)
2487 hdc
= BeginPaint (hwnd
, &ps
);
2488 TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2490 ps
.rcPaint
.left
,ps
.rcPaint
.top
,ps
.rcPaint
.right
,ps
.rcPaint
.bottom
);
2493 TAB_EraseBackground (hwnd
, hdc
);
2499 TAB_Refresh (hwnd
, hdc
);
2502 EndPaint (hwnd
, &ps
);
2508 TAB_InsertItemA (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2510 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2515 GetClientRect (hwnd
, &rect
);
2516 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd
,
2517 rect
.top
, rect
.left
, rect
.bottom
, rect
.right
);
2519 pti
= (TCITEMA
*)lParam
;
2520 iItem
= (INT
)wParam
;
2522 if (iItem
< 0) return -1;
2523 if (iItem
> infoPtr
->uNumItem
)
2524 iItem
= infoPtr
->uNumItem
;
2526 TAB_DumpItemExternalA(pti
, iItem
);
2529 if (infoPtr
->uNumItem
== 0) {
2530 infoPtr
->items
= Alloc (sizeof (TAB_ITEM
));
2531 infoPtr
->uNumItem
++;
2532 infoPtr
->iSelected
= 0;
2535 TAB_ITEM
*oldItems
= infoPtr
->items
;
2537 infoPtr
->uNumItem
++;
2538 infoPtr
->items
= Alloc (sizeof (TAB_ITEM
) * infoPtr
->uNumItem
);
2540 /* pre insert copy */
2542 memcpy (&infoPtr
->items
[0], &oldItems
[0],
2543 iItem
* sizeof(TAB_ITEM
));
2546 /* post insert copy */
2547 if (iItem
< infoPtr
->uNumItem
- 1) {
2548 memcpy (&infoPtr
->items
[iItem
+1], &oldItems
[iItem
],
2549 (infoPtr
->uNumItem
- iItem
- 1) * sizeof(TAB_ITEM
));
2553 if (iItem
<= infoPtr
->iSelected
)
2554 infoPtr
->iSelected
++;
2559 infoPtr
->items
[iItem
].mask
= pti
->mask
;
2560 if (pti
->mask
& TCIF_TEXT
)
2561 Str_SetPtrAtoW (&infoPtr
->items
[iItem
].pszText
, pti
->pszText
);
2563 if (pti
->mask
& TCIF_IMAGE
)
2564 infoPtr
->items
[iItem
].iImage
= pti
->iImage
;
2566 if (pti
->mask
& TCIF_PARAM
)
2567 infoPtr
->items
[iItem
].lParam
= pti
->lParam
;
2569 TAB_SetItemBounds(hwnd
);
2570 if (infoPtr
->uNumItem
> 1)
2571 TAB_InvalidateTabArea(hwnd
, infoPtr
);
2573 InvalidateRect(hwnd
, NULL
, TRUE
);
2575 TRACE("[%p]: added item %d %s\n",
2576 hwnd
, iItem
, debugstr_w(infoPtr
->items
[iItem
].pszText
));
2583 TAB_InsertItemW (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2585 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2590 GetClientRect (hwnd
, &rect
);
2591 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd
,
2592 rect
.top
, rect
.left
, rect
.bottom
, rect
.right
);
2594 pti
= (TCITEMW
*)lParam
;
2595 iItem
= (INT
)wParam
;
2597 if (iItem
< 0) return -1;
2598 if (iItem
> infoPtr
->uNumItem
)
2599 iItem
= infoPtr
->uNumItem
;
2601 TAB_DumpItemExternalW(pti
, iItem
);
2603 if (infoPtr
->uNumItem
== 0) {
2604 infoPtr
->items
= Alloc (sizeof (TAB_ITEM
));
2605 infoPtr
->uNumItem
++;
2606 infoPtr
->iSelected
= 0;
2609 TAB_ITEM
*oldItems
= infoPtr
->items
;
2611 infoPtr
->uNumItem
++;
2612 infoPtr
->items
= Alloc (sizeof (TAB_ITEM
) * infoPtr
->uNumItem
);
2614 /* pre insert copy */
2616 memcpy (&infoPtr
->items
[0], &oldItems
[0],
2617 iItem
* sizeof(TAB_ITEM
));
2620 /* post insert copy */
2621 if (iItem
< infoPtr
->uNumItem
- 1) {
2622 memcpy (&infoPtr
->items
[iItem
+1], &oldItems
[iItem
],
2623 (infoPtr
->uNumItem
- iItem
- 1) * sizeof(TAB_ITEM
));
2627 if (iItem
<= infoPtr
->iSelected
)
2628 infoPtr
->iSelected
++;
2633 infoPtr
->items
[iItem
].mask
= pti
->mask
;
2634 if (pti
->mask
& TCIF_TEXT
)
2635 Str_SetPtrW (&infoPtr
->items
[iItem
].pszText
, pti
->pszText
);
2637 if (pti
->mask
& TCIF_IMAGE
)
2638 infoPtr
->items
[iItem
].iImage
= pti
->iImage
;
2640 if (pti
->mask
& TCIF_PARAM
)
2641 infoPtr
->items
[iItem
].lParam
= pti
->lParam
;
2643 TAB_SetItemBounds(hwnd
);
2644 if (infoPtr
->uNumItem
> 1)
2645 TAB_InvalidateTabArea(hwnd
, infoPtr
);
2647 InvalidateRect(hwnd
, NULL
, TRUE
);
2649 TRACE("[%p]: added item %d %s\n",
2650 hwnd
, iItem
, debugstr_w(infoPtr
->items
[iItem
].pszText
));
2657 TAB_SetItemSize (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2659 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2660 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
2662 BOOL bNeedPaint
= FALSE
;
2664 lResult
= MAKELONG(infoPtr
->tabWidth
, infoPtr
->tabHeight
);
2666 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2667 if (lStyle
& TCS_FIXEDWIDTH
&& (infoPtr
->tabWidth
!= (INT
)LOWORD(lParam
)))
2669 infoPtr
->tabWidth
= max((INT
)LOWORD(lParam
), infoPtr
->tabMinWidth
);
2673 if (infoPtr
->tabHeight
!= (INT
)HIWORD(lParam
))
2675 if ((infoPtr
->fHeightSet
= ((INT
)HIWORD(lParam
) != 0)))
2676 infoPtr
->tabHeight
= (INT
)HIWORD(lParam
);
2680 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2681 HIWORD(lResult
), LOWORD(lResult
),
2682 infoPtr
->tabHeight
, infoPtr
->tabWidth
);
2686 TAB_SetItemBounds(hwnd
);
2687 RedrawWindow(hwnd
, NULL
, NULL
, RDW_ERASE
| RDW_INVALIDATE
| RDW_UPDATENOW
);
2694 TAB_SetMinTabWidth (HWND hwnd
, LPARAM lParam
)
2696 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2697 INT cx
= (INT
)lParam
;
2701 oldcx
= infoPtr
->tabMinWidth
;
2702 infoPtr
->tabMinWidth
= (cx
==-1)?DEFAULT_TAB_WIDTH
:cx
;
2710 TAB_HighlightItem (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2712 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2713 INT iItem
= (INT
)wParam
;
2714 BOOL fHighlight
= (BOOL
)LOWORD(lParam
);
2716 if ((infoPtr
) && (iItem
>=0) && (iItem
<infoPtr
->uNumItem
)) {
2718 infoPtr
->items
[iItem
].dwState
|= TCIS_HIGHLIGHTED
;
2720 infoPtr
->items
[iItem
].dwState
&= ~TCIS_HIGHLIGHTED
;
2728 TAB_SetItemA (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2730 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2735 iItem
= (INT
)wParam
;
2736 tabItem
= (LPTCITEMA
)lParam
;
2738 TRACE("%d %p\n", iItem
, tabItem
);
2739 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
)) return FALSE
;
2741 TAB_DumpItemExternalA(tabItem
, iItem
);
2743 wineItem
= &infoPtr
->items
[iItem
];
2745 if (tabItem
->mask
& TCIF_IMAGE
)
2746 wineItem
->iImage
= tabItem
->iImage
;
2748 if (tabItem
->mask
& TCIF_PARAM
)
2749 wineItem
->lParam
= tabItem
->lParam
;
2751 if (tabItem
->mask
& TCIF_RTLREADING
)
2752 FIXME("TCIF_RTLREADING\n");
2754 if (tabItem
->mask
& TCIF_STATE
)
2755 wineItem
->dwState
= tabItem
->dwState
;
2757 if (tabItem
->mask
& TCIF_TEXT
)
2758 Str_SetPtrAtoW(&wineItem
->pszText
, tabItem
->pszText
);
2760 /* Update and repaint tabs */
2761 TAB_SetItemBounds(hwnd
);
2762 TAB_InvalidateTabArea(hwnd
,infoPtr
);
2769 TAB_SetItemW (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2771 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2776 iItem
= (INT
)wParam
;
2777 tabItem
= (LPTCITEMW
)lParam
;
2779 TRACE("%d %p\n", iItem
, tabItem
);
2780 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
)) return FALSE
;
2782 TAB_DumpItemExternalW(tabItem
, iItem
);
2784 wineItem
= &infoPtr
->items
[iItem
];
2786 if (tabItem
->mask
& TCIF_IMAGE
)
2787 wineItem
->iImage
= tabItem
->iImage
;
2789 if (tabItem
->mask
& TCIF_PARAM
)
2790 wineItem
->lParam
= tabItem
->lParam
;
2792 if (tabItem
->mask
& TCIF_RTLREADING
)
2793 FIXME("TCIF_RTLREADING\n");
2795 if (tabItem
->mask
& TCIF_STATE
)
2796 wineItem
->dwState
= tabItem
->dwState
;
2798 if (tabItem
->mask
& TCIF_TEXT
)
2799 Str_SetPtrW(&wineItem
->pszText
, tabItem
->pszText
);
2801 /* Update and repaint tabs */
2802 TAB_SetItemBounds(hwnd
);
2803 TAB_InvalidateTabArea(hwnd
,infoPtr
);
2810 TAB_GetItemCount (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2812 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2814 return infoPtr
->uNumItem
;
2819 TAB_GetItemA (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2821 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2826 iItem
= (INT
)wParam
;
2827 tabItem
= (LPTCITEMA
)lParam
;
2829 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
))
2832 wineItem
= &infoPtr
->items
[iItem
];
2834 if (tabItem
->mask
& TCIF_IMAGE
)
2835 tabItem
->iImage
= wineItem
->iImage
;
2837 if (tabItem
->mask
& TCIF_PARAM
)
2838 tabItem
->lParam
= wineItem
->lParam
;
2840 if (tabItem
->mask
& TCIF_RTLREADING
)
2841 FIXME("TCIF_RTLREADING\n");
2843 if (tabItem
->mask
& TCIF_STATE
)
2844 tabItem
->dwState
= wineItem
->dwState
;
2846 if (tabItem
->mask
& TCIF_TEXT
)
2847 Str_GetPtrWtoA (wineItem
->pszText
, tabItem
->pszText
, tabItem
->cchTextMax
);
2849 TAB_DumpItemExternalA(tabItem
, iItem
);
2856 TAB_GetItemW (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2858 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2863 iItem
= (INT
)wParam
;
2864 tabItem
= (LPTCITEMW
)lParam
;
2866 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
))
2869 wineItem
=& infoPtr
->items
[iItem
];
2871 if (tabItem
->mask
& TCIF_IMAGE
)
2872 tabItem
->iImage
= wineItem
->iImage
;
2874 if (tabItem
->mask
& TCIF_PARAM
)
2875 tabItem
->lParam
= wineItem
->lParam
;
2877 if (tabItem
->mask
& TCIF_RTLREADING
)
2878 FIXME("TCIF_RTLREADING\n");
2880 if (tabItem
->mask
& TCIF_STATE
)
2881 tabItem
->dwState
= wineItem
->dwState
;
2883 if (tabItem
->mask
& TCIF_TEXT
)
2884 Str_GetPtrW (wineItem
->pszText
, tabItem
->pszText
, tabItem
->cchTextMax
);
2886 TAB_DumpItemExternalW(tabItem
, iItem
);
2893 TAB_DeleteItem (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2895 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2896 INT iItem
= (INT
) wParam
;
2897 BOOL bResult
= FALSE
;
2899 if ((iItem
>= 0) && (iItem
< infoPtr
->uNumItem
))
2901 TAB_ITEM
*oldItems
= infoPtr
->items
;
2903 TAB_InvalidateTabArea(hwnd
, infoPtr
);
2905 infoPtr
->uNumItem
--;
2906 infoPtr
->items
= Alloc(sizeof (TAB_ITEM
) * infoPtr
->uNumItem
);
2909 memcpy(&infoPtr
->items
[0], &oldItems
[0], iItem
* sizeof(TAB_ITEM
));
2911 if (iItem
< infoPtr
->uNumItem
)
2912 memcpy(&infoPtr
->items
[iItem
], &oldItems
[iItem
+ 1],
2913 (infoPtr
->uNumItem
- iItem
) * sizeof(TAB_ITEM
));
2917 /* Readjust the selected index */
2918 if ((iItem
== infoPtr
->iSelected
) && (iItem
> 0))
2919 infoPtr
->iSelected
--;
2921 if (iItem
< infoPtr
->iSelected
)
2922 infoPtr
->iSelected
--;
2924 if (infoPtr
->uNumItem
== 0)
2925 infoPtr
->iSelected
= -1;
2927 /* Reposition and repaint tabs */
2928 TAB_SetItemBounds(hwnd
);
2937 TAB_DeleteAllItems (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2939 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2941 TAB_InvalidateTabArea(hwnd
,infoPtr
);
2943 Free (infoPtr
->items
);
2944 infoPtr
->uNumItem
= 0;
2945 infoPtr
->iSelected
= -1;
2946 if (infoPtr
->iHotTracked
>= 0)
2947 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
2948 infoPtr
->iHotTracked
= -1;
2950 TAB_SetItemBounds(hwnd
);
2956 TAB_GetFont (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2958 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2961 return (LRESULT
)infoPtr
->hFont
;
2965 TAB_SetFont (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2968 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2970 TRACE("%x %lx\n",wParam
, lParam
);
2972 infoPtr
->hFont
= (HFONT
)wParam
;
2974 TAB_SetItemBounds(hwnd
);
2976 TAB_InvalidateTabArea(hwnd
, infoPtr
);
2983 TAB_GetImageList (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2985 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2988 return (LRESULT
)infoPtr
->himl
;
2992 TAB_SetImageList (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2994 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2995 HIMAGELIST himlPrev
;
2998 himlPrev
= infoPtr
->himl
;
2999 infoPtr
->himl
= (HIMAGELIST
)lParam
;
3000 return (LRESULT
)himlPrev
;
3004 TAB_GetUnicodeFormat (HWND hwnd
)
3006 TAB_INFO
*infoPtr
= TAB_GetInfoPtr (hwnd
);
3007 return infoPtr
->bUnicode
;
3011 TAB_SetUnicodeFormat (HWND hwnd
, WPARAM wParam
)
3013 TAB_INFO
*infoPtr
= TAB_GetInfoPtr (hwnd
);
3014 BOOL bTemp
= infoPtr
->bUnicode
;
3016 infoPtr
->bUnicode
= (BOOL
)wParam
;
3022 TAB_Size (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
3025 /* I'm not really sure what the following code was meant to do.
3026 This is what it is doing:
3027 When WM_SIZE is sent with SIZE_RESTORED, the control
3028 gets positioned in the top left corner.
3032 UINT uPosFlags,cx,cy;
3036 parent = GetParent (hwnd);
3037 GetClientRect(parent, &parent_rect);
3040 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
3041 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
3043 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
3044 cx, cy, uPosFlags | SWP_NOZORDER);
3046 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
3049 /* Recompute the size/position of the tabs. */
3050 TAB_SetItemBounds (hwnd
);
3052 /* Force a repaint of the control. */
3053 InvalidateRect(hwnd
, NULL
, TRUE
);
3060 TAB_Create (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
3063 TEXTMETRICA fontMetrics
;
3068 infoPtr
= (TAB_INFO
*)Alloc (sizeof(TAB_INFO
));
3070 SetWindowLongA(hwnd
, 0, (DWORD
)infoPtr
);
3072 infoPtr
->hwndNotify
= ((LPCREATESTRUCTW
)lParam
)->hwndParent
;
3073 infoPtr
->uNumItem
= 0;
3074 infoPtr
->uNumRows
= 0;
3075 infoPtr
->uHItemPadding
= 6;
3076 infoPtr
->uVItemPadding
= 3;
3077 infoPtr
->uHItemPadding_s
= 6;
3078 infoPtr
->uVItemPadding_s
= 3;
3081 infoPtr
->hcurArrow
= LoadCursorA (0, (LPSTR
)IDC_ARROW
);
3082 infoPtr
->iSelected
= -1;
3083 infoPtr
->iHotTracked
= -1;
3084 infoPtr
->uFocus
= -1;
3085 infoPtr
->hwndToolTip
= 0;
3086 infoPtr
->DoRedraw
= TRUE
;
3087 infoPtr
->needsScrolling
= FALSE
;
3088 infoPtr
->hwndUpDown
= 0;
3089 infoPtr
->leftmostVisible
= 0;
3090 infoPtr
->fHeightSet
= FALSE
;
3091 infoPtr
->bUnicode
= IsWindowUnicode (hwnd
);
3093 TRACE("Created tab control, hwnd [%p]\n", hwnd
);
3095 /* The tab control always has the WS_CLIPSIBLINGS style. Even
3096 if you don't specify it in CreateWindow. This is necessary in
3097 order for paint to work correctly. This follows windows behaviour. */
3098 dwStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
3099 SetWindowLongA(hwnd
, GWL_STYLE
, dwStyle
|WS_CLIPSIBLINGS
);
3101 if (dwStyle
& TCS_TOOLTIPS
) {
3102 /* Create tooltip control */
3103 infoPtr
->hwndToolTip
=
3104 CreateWindowExA (0, TOOLTIPS_CLASSA
, NULL
, 0,
3105 CW_USEDEFAULT
, CW_USEDEFAULT
,
3106 CW_USEDEFAULT
, CW_USEDEFAULT
,
3109 /* Send NM_TOOLTIPSCREATED notification */
3110 if (infoPtr
->hwndToolTip
) {
3111 NMTOOLTIPSCREATED nmttc
;
3113 nmttc
.hdr
.hwndFrom
= hwnd
;
3114 nmttc
.hdr
.idFrom
= GetWindowLongA(hwnd
, GWL_ID
);
3115 nmttc
.hdr
.code
= NM_TOOLTIPSCREATED
;
3116 nmttc
.hwndToolTips
= infoPtr
->hwndToolTip
;
3118 SendMessageA (infoPtr
->hwndNotify
, WM_NOTIFY
,
3119 (WPARAM
)GetWindowLongA(hwnd
, GWL_ID
), (LPARAM
)&nmttc
);
3124 * We need to get text information so we need a DC and we need to select
3128 hOldFont
= SelectObject (hdc
, GetStockObject (SYSTEM_FONT
));
3130 /* Use the system font to determine the initial height of a tab. */
3131 GetTextMetricsA(hdc
, &fontMetrics
);
3134 * Make sure there is enough space for the letters + growing the
3135 * selected item + extra space for the selected item.
3137 infoPtr
->tabHeight
= fontMetrics
.tmHeight
+ SELECTED_TAB_OFFSET
+
3138 ((dwStyle
& TCS_BUTTONS
) ? 2 : 1) *
3139 infoPtr
->uVItemPadding
;
3141 /* Initialize the width of a tab. */
3142 infoPtr
->tabWidth
= DEFAULT_TAB_WIDTH
;
3143 infoPtr
->tabMinWidth
= 0;
3145 TRACE("tabH=%d, tabW=%d\n", infoPtr
->tabHeight
, infoPtr
->tabWidth
);
3147 SelectObject (hdc
, hOldFont
);
3148 ReleaseDC(hwnd
, hdc
);
3154 TAB_Destroy (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
3156 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
3162 if (infoPtr
->items
) {
3163 for (iItem
= 0; iItem
< infoPtr
->uNumItem
; iItem
++) {
3164 if (infoPtr
->items
[iItem
].pszText
)
3165 Free (infoPtr
->items
[iItem
].pszText
);
3167 Free (infoPtr
->items
);
3170 if (infoPtr
->hwndToolTip
)
3171 DestroyWindow (infoPtr
->hwndToolTip
);
3173 if (infoPtr
->hwndUpDown
)
3174 DestroyWindow(infoPtr
->hwndUpDown
);
3176 if (infoPtr
->iHotTracked
>= 0)
3177 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
3180 SetWindowLongA(hwnd
, 0, 0);
3184 static LRESULT WINAPI
3185 TAB_WindowProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
3187 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
3189 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd
, uMsg
, wParam
, lParam
);
3190 if (!TAB_GetInfoPtr(hwnd
) && (uMsg
!= WM_CREATE
))
3191 return DefWindowProcA (hwnd
, uMsg
, wParam
, lParam
);
3195 case TCM_GETIMAGELIST
:
3196 return TAB_GetImageList (hwnd
, wParam
, lParam
);
3198 case TCM_SETIMAGELIST
:
3199 return TAB_SetImageList (hwnd
, wParam
, lParam
);
3201 case TCM_GETITEMCOUNT
:
3202 return TAB_GetItemCount (hwnd
, wParam
, lParam
);
3205 return TAB_GetItemA (hwnd
, wParam
, lParam
);
3208 return TAB_GetItemW (hwnd
, wParam
, lParam
);
3211 return TAB_SetItemA (hwnd
, wParam
, lParam
);
3214 return TAB_SetItemW (hwnd
, wParam
, lParam
);
3216 case TCM_DELETEITEM
:
3217 return TAB_DeleteItem (hwnd
, wParam
, lParam
);
3219 case TCM_DELETEALLITEMS
:
3220 return TAB_DeleteAllItems (hwnd
, wParam
, lParam
);
3222 case TCM_GETITEMRECT
:
3223 return TAB_GetItemRect (hwnd
, wParam
, lParam
);
3226 return TAB_GetCurSel (hwnd
);
3229 return TAB_HitTest (hwnd
, wParam
, lParam
);
3232 return TAB_SetCurSel (hwnd
, wParam
);
3234 case TCM_INSERTITEMA
:
3235 return TAB_InsertItemA (hwnd
, wParam
, lParam
);
3237 case TCM_INSERTITEMW
:
3238 return TAB_InsertItemW (hwnd
, wParam
, lParam
);
3240 case TCM_SETITEMEXTRA
:
3241 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
3244 case TCM_ADJUSTRECT
:
3245 return TAB_AdjustRect (hwnd
, (BOOL
)wParam
, (LPRECT
)lParam
);
3247 case TCM_SETITEMSIZE
:
3248 return TAB_SetItemSize (hwnd
, wParam
, lParam
);
3250 case TCM_REMOVEIMAGE
:
3251 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3254 case TCM_SETPADDING
:
3255 return TAB_SetPadding (hwnd
, wParam
, lParam
);
3257 case TCM_GETROWCOUNT
:
3258 return TAB_GetRowCount(hwnd
);
3260 case TCM_GETUNICODEFORMAT
:
3261 return TAB_GetUnicodeFormat (hwnd
);
3263 case TCM_SETUNICODEFORMAT
:
3264 return TAB_SetUnicodeFormat (hwnd
, wParam
);
3266 case TCM_HIGHLIGHTITEM
:
3267 return TAB_HighlightItem (hwnd
, wParam
, lParam
);
3269 case TCM_GETTOOLTIPS
:
3270 return TAB_GetToolTips (hwnd
, wParam
, lParam
);
3272 case TCM_SETTOOLTIPS
:
3273 return TAB_SetToolTips (hwnd
, wParam
, lParam
);
3275 case TCM_GETCURFOCUS
:
3276 return TAB_GetCurFocus (hwnd
);
3278 case TCM_SETCURFOCUS
:
3279 return TAB_SetCurFocus (hwnd
, wParam
);
3281 case TCM_SETMINTABWIDTH
:
3282 return TAB_SetMinTabWidth(hwnd
, lParam
);
3284 case TCM_DESELECTALL
:
3285 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3288 case TCM_GETEXTENDEDSTYLE
:
3289 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3292 case TCM_SETEXTENDEDSTYLE
:
3293 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3297 return TAB_GetFont (hwnd
, wParam
, lParam
);
3300 return TAB_SetFont (hwnd
, wParam
, lParam
);
3303 return TAB_Create (hwnd
, wParam
, lParam
);
3306 return TAB_Destroy (hwnd
, wParam
, lParam
);
3309 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
3311 case WM_LBUTTONDOWN
:
3312 return TAB_LButtonDown (hwnd
, wParam
, lParam
);
3315 return TAB_LButtonUp (hwnd
, wParam
, lParam
);
3318 return SendMessageA(infoPtr
->hwndNotify
, WM_NOTIFY
, wParam
, lParam
);
3320 case WM_RBUTTONDOWN
:
3321 return TAB_RButtonDown (hwnd
, wParam
, lParam
);
3324 return TAB_MouseMove (hwnd
, wParam
, lParam
);
3327 return TAB_EraseBackground (hwnd
, (HDC
)wParam
);
3330 return TAB_Paint (hwnd
, wParam
);
3333 return TAB_Size (hwnd
, wParam
, lParam
);
3336 return TAB_SetRedraw (hwnd
, wParam
);
3339 return TAB_OnHScroll(hwnd
, (int)LOWORD(wParam
), (int)HIWORD(wParam
), (HWND
)lParam
);
3341 case WM_STYLECHANGED
:
3342 TAB_SetItemBounds (hwnd
);
3343 InvalidateRect(hwnd
, NULL
, TRUE
);
3346 case WM_SYSCOLORCHANGE
:
3347 COMCTL32_RefreshSysColors();
3352 return TAB_FocusChanging(hwnd
, uMsg
, wParam
, lParam
);
3355 return TAB_KeyUp(hwnd
, wParam
);
3357 return TAB_NCHitTest(hwnd
, lParam
);
3360 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
))
3361 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3362 uMsg
, wParam
, lParam
);
3363 return DefWindowProcA(hwnd
, uMsg
, wParam
, lParam
);
3375 ZeroMemory (&wndClass
, sizeof(WNDCLASSA
));
3376 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
;
3377 wndClass
.lpfnWndProc
= (WNDPROC
)TAB_WindowProc
;
3378 wndClass
.cbClsExtra
= 0;
3379 wndClass
.cbWndExtra
= sizeof(TAB_INFO
*);
3380 wndClass
.hCursor
= LoadCursorA (0, (LPSTR
)IDC_ARROW
);
3381 wndClass
.hbrBackground
= NULL
;
3382 wndClass
.lpszClassName
= WC_TABCONTROLA
;
3384 RegisterClassA (&wndClass
);
3389 TAB_Unregister (void)
3391 UnregisterClassA (WC_TABCONTROLA
, NULL
);