4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
19 #include "debugtools.h"
23 DEFAULT_DEBUG_CHANNEL(tab
)
25 /******************************************************************************
26 * Positioning constants
28 #define SELECTED_TAB_OFFSET 2
29 #define HORIZONTAL_ITEM_PADDING 5
30 #define VERTICAL_ITEM_PADDING 3
31 #define ROUND_CORNER_SIZE 2
32 #define FOCUS_RECT_HOFFSET 2
33 #define FOCUS_RECT_VOFFSET 1
34 #define DISPLAY_AREA_PADDINGX 2
35 #define DISPLAY_AREA_PADDINGY 2
36 #define CONTROL_BORDER_SIZEX 2
37 #define CONTROL_BORDER_SIZEY 2
38 #define BUTTON_SPACINGX 10
39 #define DEFAULT_TAB_WIDTH 96
41 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
43 /******************************************************************************
44 * Hot-tracking timer constants
46 #define TAB_HOTTRACK_TIMER 1
47 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
49 /******************************************************************************
52 static void TAB_Refresh (HWND hwnd
, HDC hdc
);
53 static void TAB_InvalidateTabArea(HWND hwnd
, TAB_INFO
* infoPtr
);
54 static void TAB_EnsureSelectionVisible(HWND hwnd
, TAB_INFO
* infoPtr
);
55 static void TAB_DrawItem(HWND hwnd
, HDC hdc
, INT iItem
);
56 static void TAB_DrawItemInterior(HWND hwnd
, HDC hdc
, INT iItem
, RECT
* drawRect
);
59 TAB_SendSimpleNotify (HWND hwnd
, UINT code
)
63 nmhdr
.hwndFrom
= hwnd
;
64 nmhdr
.idFrom
= GetWindowLongA(hwnd
, GWL_ID
);
67 return (BOOL
) SendMessageA (GetParent (hwnd
), WM_NOTIFY
,
68 (WPARAM
) nmhdr
.idFrom
, (LPARAM
) &nmhdr
);
73 TAB_RelayEvent (HWND hwndTip
, HWND hwndMsg
, UINT uMsg
,
74 WPARAM wParam
, LPARAM lParam
)
82 msg
.time
= GetMessageTime ();
83 msg
.pt
.x
= LOWORD(GetMessagePos ());
84 msg
.pt
.y
= HIWORD(GetMessagePos ());
86 SendMessageA (hwndTip
, TTM_RELAYEVENT
, 0, (LPARAM
)&msg
);
92 TAB_GetCurSel (HWND hwnd
)
94 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
96 return infoPtr
->iSelected
;
100 TAB_GetCurFocus (HWND hwnd
)
102 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
104 return infoPtr
->uFocus
;
108 TAB_GetToolTips (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
110 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
112 if (infoPtr
== NULL
) return 0;
113 return infoPtr
->hwndToolTip
;
118 TAB_SetCurSel (HWND hwnd
,WPARAM wParam
)
120 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
121 INT iItem
=(INT
) wParam
;
125 if ((iItem
>= 0) && (iItem
< infoPtr
->uNumItem
)) {
126 prevItem
=infoPtr
->iSelected
;
127 infoPtr
->iSelected
=iItem
;
128 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
129 TAB_InvalidateTabArea(hwnd
, infoPtr
);
135 TAB_SetCurFocus (HWND hwnd
,WPARAM wParam
)
137 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
138 INT iItem
=(INT
) wParam
;
140 if ((iItem
< 0) || (iItem
>= infoPtr
->uNumItem
)) return 0;
142 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BUTTONS
) {
143 FIXME("Should set input focus\n");
145 if (infoPtr
->iSelected
!= iItem
|| infoPtr
->uFocus
== -1 ) {
146 infoPtr
->uFocus
=iItem
;
147 if (TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
)!=TRUE
) {
148 infoPtr
->iSelected
= iItem
;
149 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
151 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
152 TAB_InvalidateTabArea(hwnd
, infoPtr
);
160 TAB_SetToolTips (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
162 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
164 if (infoPtr
== NULL
) return 0;
165 infoPtr
->hwndToolTip
= (HWND
)wParam
;
169 /******************************************************************************
170 * TAB_InternalGetItemRect
172 * This method will calculate the rectangle representing a given tab item in
173 * client coordinates. This method takes scrolling into account.
175 * This method returns TRUE if the item is visible in the window and FALSE
176 * if it is completely outside the client area.
178 static BOOL
TAB_InternalGetItemRect(
185 RECT tmpItemRect
,clientRect
;
186 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
189 * Perform a sanity check and a trivial visibility check.
191 if ( (infoPtr
->uNumItem
<=0) ||
192 (itemIndex
>= infoPtr
->uNumItem
) ||
193 (!(lStyle
&TCS_MULTILINE
) && (itemIndex
< infoPtr
->leftmostVisible
)) )
197 * Avoid special cases in this procedure by assigning the "out"
198 * parameters if the caller didn't supply them
201 itemRect
= &tmpItemRect
;
204 * Retrieve the unmodified item rect.
206 *itemRect
= infoPtr
->items
[itemIndex
].rect
;
209 * calculate the times bottom and top based on the row
211 GetClientRect(hwnd
, &clientRect
);
213 if (lStyle
& TCS_BOTTOM
)
215 itemRect
->bottom
= clientRect
.bottom
-
216 SELECTED_TAB_OFFSET
-
217 itemRect
->top
* (infoPtr
->tabHeight
- 2);
219 itemRect
->top
= clientRect
.bottom
-
221 itemRect
->top
* ( infoPtr
->tabHeight
- 2);
225 itemRect
->bottom
= clientRect
.top
+
227 itemRect
->top
* (infoPtr
->tabHeight
- 2);
228 itemRect
->top
= clientRect
.top
+
230 itemRect
->top
* (infoPtr
->tabHeight
- 2);
234 * "scroll" it to make sure the item at the very left of the
235 * tab control is the leftmost visible tab.
238 -infoPtr
->items
[infoPtr
->leftmostVisible
].rect
.left
,
242 * Move the rectangle so the first item is slightly offset from
243 * the left of the tab control.
251 * Now, calculate the position of the item as if it were selected.
253 if (selectedRect
!=NULL
)
255 CopyRect(selectedRect
, itemRect
);
258 * The rectangle of a selected item is a bit wider.
260 InflateRect(selectedRect
, SELECTED_TAB_OFFSET
, 0);
263 * If it also a bit higher.
265 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
267 selectedRect
->top
-=2; /* the border is thicker on the bottom */
268 selectedRect
->bottom
+=SELECTED_TAB_OFFSET
;
272 selectedRect
->top
-=SELECTED_TAB_OFFSET
;
273 selectedRect
->bottom
+=1;
280 static BOOL
TAB_GetItemRect(HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
282 return TAB_InternalGetItemRect(hwnd
, TAB_GetInfoPtr(hwnd
), (INT
)wParam
,
283 (LPRECT
)lParam
, (LPRECT
)NULL
);
286 /******************************************************************************
289 * This method is called to handle keyboard input
291 static LRESULT
TAB_KeyUp(
295 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
301 newItem
= infoPtr
->uFocus
-1;
304 newItem
= infoPtr
->uFocus
+1;
309 * If we changed to a valid item, change the selection
311 if ( (newItem
>= 0) &&
312 (newItem
< infoPtr
->uNumItem
) &&
313 (infoPtr
->uFocus
!= newItem
) )
315 if (!TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
))
317 infoPtr
->iSelected
= newItem
;
318 infoPtr
->uFocus
= newItem
;
319 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
321 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
322 TAB_InvalidateTabArea(hwnd
, infoPtr
);
329 /******************************************************************************
332 * This method is called whenever the focus goes in or out of this control
333 * it is used to update the visual state of the control.
335 static LRESULT
TAB_FocusChanging(
341 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
346 * Get the rectangle for the item.
348 isVisible
= TAB_InternalGetItemRect(hwnd
,
355 * If the rectangle is not completely invisible, invalidate that
356 * portion of the window.
360 InvalidateRect(hwnd
, &selectedRect
, TRUE
);
364 * Don't otherwise disturb normal behavior.
366 return DefWindowProcA (hwnd
, uMsg
, wParam
, lParam
);
369 static HWND
TAB_InternalHitTest (
379 for (iCount
= 0; iCount
< infoPtr
->uNumItem
; iCount
++)
381 TAB_InternalGetItemRect(hwnd
,
387 if (PtInRect (&rect
, pt
))
389 *flags
= TCHT_ONITEM
;
399 TAB_HitTest (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
401 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
402 LPTCHITTESTINFO lptest
=(LPTCHITTESTINFO
) lParam
;
404 return TAB_InternalHitTest (hwnd
, infoPtr
,lptest
->pt
,&lptest
->flags
);
409 TAB_LButtonDown (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
411 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
415 if (infoPtr
->hwndToolTip
)
416 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
417 WM_LBUTTONDOWN
, wParam
, lParam
);
419 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_FOCUSONBUTTONDOWN
) {
423 if (infoPtr
->hwndToolTip
)
424 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
425 WM_LBUTTONDOWN
, wParam
, lParam
);
427 pt
.x
= (INT
)LOWORD(lParam
);
428 pt
.y
= (INT
)HIWORD(lParam
);
430 newItem
=TAB_InternalHitTest (hwnd
, infoPtr
,pt
,&dummy
);
432 TRACE("On Tab, item %d\n", newItem
);
434 if ( (newItem
!=-1) &&
435 (infoPtr
->iSelected
!= newItem
) )
437 if (TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
)!=TRUE
)
439 infoPtr
->iSelected
= newItem
;
440 infoPtr
->uFocus
= newItem
;
441 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
443 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
445 TAB_InvalidateTabArea(hwnd
, infoPtr
);
452 TAB_LButtonUp (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
454 TAB_SendSimpleNotify(hwnd
, NM_CLICK
);
460 TAB_RButtonDown (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
462 TAB_SendSimpleNotify(hwnd
, NM_RCLICK
);
466 /******************************************************************************
467 * TAB_DrawLoneItemInterior
469 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
470 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
471 * up the device context and font. This routine does the same setup but
472 * only calls TAB_DrawItemInterior for the single specified item.
475 TAB_DrawLoneItemInterior(HWND hwnd
, TAB_INFO
* infoPtr
, int iItem
)
477 HDC hdc
= GetDC(hwnd
);
478 HFONT hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
479 TAB_DrawItemInterior(hwnd
, hdc
, iItem
, NULL
);
480 SelectObject(hdc
, hOldFont
);
481 ReleaseDC(hwnd
, hdc
);
484 /******************************************************************************
485 * TAB_HotTrackTimerProc
487 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
488 * timer is setup so we can check if the mouse is moved out of our window.
489 * (We don't get an event when the mouse leaves, the mouse-move events just
490 * stop being delivered to our window and just start being delivered to
491 * another window.) This function is called when the timer triggers so
492 * we can check if the mouse has left our window. If so, we un-highlight
493 * the hot-tracked tab.
496 TAB_HotTrackTimerProc
498 HWND hwnd
, /* handle of window for timer messages */
499 UINT uMsg
, /* WM_TIMER message */
500 UINT idEvent
, /* timer identifier */
501 DWORD dwTime
/* current system time */
504 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
506 if (infoPtr
!= NULL
&& infoPtr
->iHotTracked
>= 0)
511 ** If we can't get the cursor position, or if the cursor is outside our
512 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
513 ** "outside" even if it is within our bounding rect if another window
514 ** overlaps. Note also that the case where the cursor stayed within our
515 ** window but has moved off the hot-tracked tab will be handled by the
516 ** WM_MOUSEMOVE event.
518 if (!GetCursorPos(&pt
) || WindowFromPoint(pt
) != hwnd
)
520 /* Redraw iHotTracked to look normal */
521 INT iRedraw
= infoPtr
->iHotTracked
;
522 infoPtr
->iHotTracked
= -1;
523 TAB_DrawLoneItemInterior(hwnd
, infoPtr
, iRedraw
);
525 /* Kill this timer */
526 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
531 /******************************************************************************
534 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
535 * should be highlighted. This function determines which tab in a tab control,
536 * if any, is under the mouse and records that information. The caller may
537 * supply output parameters to receive the item number of the tab item which
538 * was highlighted but isn't any longer and of the tab item which is now
539 * highlighted but wasn't previously. The caller can use this information to
540 * selectively redraw those tab items.
542 * If the caller has a mouse position, it can supply it through the pos
543 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
544 * supplies NULL and this function determines the current mouse position
552 int* out_redrawLeave
,
556 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
561 if (out_redrawLeave
!= NULL
)
562 *out_redrawLeave
= -1;
563 if (out_redrawEnter
!= NULL
)
564 *out_redrawEnter
= -1;
566 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_HOTTRACK
)
574 ScreenToClient(hwnd
, &pt
);
582 item
= TAB_InternalHitTest(hwnd
, infoPtr
, pt
, &flags
);
585 if (item
!= infoPtr
->iHotTracked
)
587 if (infoPtr
->iHotTracked
>= 0)
589 /* Mark currently hot-tracked to be redrawn to look normal */
590 if (out_redrawLeave
!= NULL
)
591 *out_redrawLeave
= infoPtr
->iHotTracked
;
595 /* Kill timer which forces recheck of mouse pos */
596 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
601 /* Start timer so we recheck mouse pos */
602 UINT timerID
= SetTimer
606 TAB_HOTTRACK_TIMER_INTERVAL
,
607 TAB_HotTrackTimerProc
611 return; /* Hot tracking not available */
614 infoPtr
->iHotTracked
= item
;
618 /* Mark new hot-tracked to be redrawn to look highlighted */
619 if (out_redrawEnter
!= NULL
)
620 *out_redrawEnter
= item
;
625 /******************************************************************************
628 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
631 TAB_MouseMove (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
636 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
638 if (infoPtr
->hwndToolTip
)
639 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
640 WM_LBUTTONDOWN
, wParam
, lParam
);
642 /* Determine which tab to highlight. Redraw tabs which change highlight
644 TAB_RecalcHotTrack(hwnd
, &lParam
, &redrawLeave
, &redrawEnter
);
646 if (redrawLeave
!= -1)
647 TAB_DrawLoneItemInterior(hwnd
, infoPtr
, redrawLeave
);
648 if (redrawEnter
!= -1)
649 TAB_DrawLoneItemInterior(hwnd
, infoPtr
, redrawEnter
);
654 /******************************************************************************
657 * Calculates the tab control's display area given the windows rectangle or
658 * the window rectangle given the requested display rectangle.
660 static LRESULT
TAB_AdjustRect(
665 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
670 * Go from display rectangle
674 * Add the height of the tabs.
676 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
677 prc
->bottom
+= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
679 prc
->top
-= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
682 * Inflate the rectangle for the padding
684 InflateRect(prc
, DISPLAY_AREA_PADDINGX
, DISPLAY_AREA_PADDINGY
);
687 * Inflate for the border
689 InflateRect(prc
, CONTROL_BORDER_SIZEX
, CONTROL_BORDER_SIZEX
);
694 * Go from window rectangle.
698 * Deflate the rectangle for the border
700 InflateRect(prc
, -CONTROL_BORDER_SIZEX
, -CONTROL_BORDER_SIZEX
);
703 * Deflate the rectangle for the padding
705 InflateRect(prc
, -DISPLAY_AREA_PADDINGX
, -DISPLAY_AREA_PADDINGY
);
708 * Remove the height of the tabs.
710 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
711 prc
->bottom
-= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
713 prc
->top
+= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
720 /******************************************************************************
723 * This method will handle the notification from the scroll control and
724 * perform the scrolling operation on the tab control.
726 static LRESULT
TAB_OnHScroll(
732 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
734 if(nScrollCode
== SB_THUMBPOSITION
&& nPos
!= infoPtr
->leftmostVisible
)
736 if(nPos
< infoPtr
->leftmostVisible
)
737 infoPtr
->leftmostVisible
--;
739 infoPtr
->leftmostVisible
++;
741 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
742 TAB_InvalidateTabArea(hwnd
, infoPtr
);
743 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
744 MAKELONG(infoPtr
->leftmostVisible
, 0));
750 /******************************************************************************
753 * This method will check the current scrolling state and make sure the
754 * scrolling control is displayed (or not).
756 static void TAB_SetupScrolling(
759 const RECT
* clientRect
)
762 if (infoPtr
->needsScrolling
)
768 * Calculate the position of the scroll control.
770 controlPos
.right
= clientRect
->right
;
771 controlPos
.left
= controlPos
.right
- 2*GetSystemMetrics(SM_CXHSCROLL
);
773 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
775 controlPos
.top
= clientRect
->bottom
- infoPtr
->tabHeight
;
776 controlPos
.bottom
= controlPos
.top
+ GetSystemMetrics(SM_CYHSCROLL
);
780 controlPos
.bottom
= clientRect
->top
+ infoPtr
->tabHeight
;
781 controlPos
.top
= controlPos
.bottom
- GetSystemMetrics(SM_CYHSCROLL
);
785 * If we don't have a scroll control yet, we want to create one.
786 * If we have one, we want to make sure it's positioned right.
788 if (infoPtr
->hwndUpDown
==0)
791 * I use a scrollbar since it seems to be more stable than the Updown
794 infoPtr
->hwndUpDown
= CreateWindowA("msctls_updown32",
796 WS_VISIBLE
| WS_CHILD
| UDS_HORZ
,
797 controlPos
.left
, controlPos
.top
,
798 controlPos
.right
- controlPos
.left
,
799 controlPos
.bottom
- controlPos
.top
,
807 SetWindowPos(infoPtr
->hwndUpDown
,
809 controlPos
.left
, controlPos
.top
,
810 controlPos
.right
- controlPos
.left
,
811 controlPos
.bottom
- controlPos
.top
,
812 SWP_SHOWWINDOW
| SWP_NOZORDER
);
815 /* Now calculate upper limit of the updown control range.
816 * We do this by calculating how many tabs will be offscreen when the
817 * last tab is visible.
819 if(infoPtr
->uNumItem
)
821 vsize
= clientRect
->right
- (controlPos
.right
- controlPos
.left
+ 1);
822 maxRange
= infoPtr
->uNumItem
;
823 tabwidth
= infoPtr
->items
[maxRange
-1].rect
.right
;
825 for(; maxRange
> 0; maxRange
--)
827 if(tabwidth
- infoPtr
->items
[maxRange
- 1].rect
.left
> vsize
)
831 if(maxRange
== infoPtr
->uNumItem
)
838 * If we once had a scroll control... hide it.
840 if (infoPtr
->hwndUpDown
!=0)
842 ShowWindow(infoPtr
->hwndUpDown
, SW_HIDE
);
845 if (infoPtr
->hwndUpDown
)
846 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETRANGE32
, 0, maxRange
);
849 /******************************************************************************
852 * This method will calculate the position rectangles of all the items in the
853 * control. The rectangle calculated starts at 0 for the first item in the
854 * list and ignores scrolling and selection.
855 * It also uses the current font to determine the height of the tab row and
856 * it checks if all the tabs fit in the client area of the window. If they
857 * dont, a scrolling control is added.
859 static void TAB_SetItemBounds (HWND hwnd
)
861 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
862 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
863 TEXTMETRICA fontMetrics
;
867 HFONT hFont
, hOldFont
;
873 * We need to get text information so we need a DC and we need to select
878 hFont
= infoPtr
->hFont
? infoPtr
->hFont
: GetStockObject (SYSTEM_FONT
);
879 hOldFont
= SelectObject (hdc
, hFont
);
882 * We will base the rectangle calculations on the client rectangle
885 GetClientRect(hwnd
, &clientRect
);
888 * The leftmost item will be "0" aligned
893 if ( !(lStyle
& TCS_FIXEDWIDTH
) && !((lStyle
& TCS_OWNERDRAWFIXED
) && infoPtr
->fSizeSet
) )
899 * Use the current font to determine the height of a tab.
901 GetTextMetricsA(hdc
, &fontMetrics
);
904 * Get the icon height
907 ImageList_GetIconSize(infoPtr
->himl
, 0, &icon_height
);
910 * Take the highest between font or icon
912 if (fontMetrics
.tmHeight
> icon_height
)
913 item_height
= fontMetrics
.tmHeight
;
915 item_height
= icon_height
;
918 * Make sure there is enough space for the letters + icon + growing the
919 * selected item + extra space for the selected item.
921 infoPtr
->tabHeight
= item_height
+ 2*VERTICAL_ITEM_PADDING
+
925 for (curItem
= 0; curItem
< infoPtr
->uNumItem
; curItem
++)
928 * Set the leftmost position of the tab.
930 infoPtr
->items
[curItem
].rect
.left
= curItemLeftPos
;
932 if ( (lStyle
& TCS_FIXEDWIDTH
) || ((lStyle
& TCS_OWNERDRAWFIXED
) && infoPtr
->fSizeSet
))
934 infoPtr
->items
[curItem
].rect
.right
= infoPtr
->items
[curItem
].rect
.left
+
936 2*HORIZONTAL_ITEM_PADDING
;
944 * Calculate how wide the tab is depending on the text it contains
946 GetTextExtentPoint32A(hdc
, infoPtr
->items
[curItem
].pszText
,
947 lstrlenA(infoPtr
->items
[curItem
].pszText
), &size
);
954 ImageList_GetIconSize(infoPtr
->himl
, &icon_width
, 0);
958 infoPtr
->items
[curItem
].rect
.right
= infoPtr
->items
[curItem
].rect
.left
+
959 size
.cx
+ icon_width
+
960 num
*HORIZONTAL_ITEM_PADDING
;
964 * Check if this is a multiline tab control and if so
965 * check to see if we should wrap the tabs
967 * Because we are going to arange all these tabs evenly
968 * really we are basically just counting rows at this point
972 if ((lStyle
& TCS_MULTILINE
)&&
973 (infoPtr
->items
[curItem
].rect
.right
> clientRect
.right
))
975 infoPtr
->items
[curItem
].rect
.right
-=
976 infoPtr
->items
[curItem
].rect
.left
;
977 infoPtr
->items
[curItem
].rect
.left
= 0;
981 infoPtr
->items
[curItem
].rect
.bottom
= 0;
982 infoPtr
->items
[curItem
].rect
.top
= curItemRowCount
;
984 TRACE("TextSize: %i\n ", size
.cx
);
985 TRACE("Rect: T %i, L %i, B %i, R %i\n",
986 infoPtr
->items
[curItem
].rect
.top
,
987 infoPtr
->items
[curItem
].rect
.left
,
988 infoPtr
->items
[curItem
].rect
.bottom
,
989 infoPtr
->items
[curItem
].rect
.right
);
992 * The leftmost position of the next item is the rightmost position
995 if (lStyle
& TCS_BUTTONS
)
996 curItemLeftPos
= infoPtr
->items
[curItem
].rect
.right
+ BUTTON_SPACINGX
;
998 curItemLeftPos
= infoPtr
->items
[curItem
].rect
.right
;
1001 if (!(lStyle
& TCS_MULTILINE
))
1004 * Check if we need a scrolling control.
1006 infoPtr
->needsScrolling
= (curItemLeftPos
+ (2*SELECTED_TAB_OFFSET
) >
1009 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1010 if(!infoPtr
->needsScrolling
)
1011 infoPtr
->leftmostVisible
= 0;
1013 TAB_SetupScrolling(hwnd
, infoPtr
, &clientRect
);
1017 * Set the number of rows
1020 infoPtr
->uNumRows
= curItemRowCount
;
1022 if ((lStyle
& TCS_MULTILINE
)&&(infoPtr
->uNumItem
> 0))
1024 INT widthDiff
,remainder
;
1025 INT tabPerRow
,remTab
;
1027 INT iIndexStart
=0,iIndexEnd
=0, iCount
=0;
1030 * Ok Microsoft trys to even out the rows. place the same
1031 * number of tabs in each row. So lets give that a shot
1035 tabPerRow
= infoPtr
->uNumItem
/ (infoPtr
->uNumRows
+ 1);
1036 remTab
= infoPtr
->uNumItem
% (infoPtr
->uNumRows
+ 1);
1038 for (iItm
=0,iRow
=0,iCount
=0,curItemLeftPos
=0;
1039 iItm
<infoPtr
->uNumItem
;
1042 if (iCount
>= ((iRow
<remTab
)?tabPerRow
+1:tabPerRow
))
1049 * normalize the current rect
1051 infoPtr
->items
[iItm
].rect
.right
-=
1052 infoPtr
->items
[iItm
].rect
.left
;
1053 infoPtr
->items
[iItm
].rect
.left
= 0;
1055 infoPtr
->items
[iItm
].rect
.top
= iRow
;
1056 infoPtr
->items
[iItm
].rect
.left
+=curItemLeftPos
;
1057 infoPtr
->items
[iItm
].rect
.right
+=curItemLeftPos
;
1058 if (lStyle
& TCS_BUTTONS
)
1059 curItemLeftPos
= infoPtr
->items
[iItm
].rect
.right
+
1062 curItemLeftPos
= infoPtr
->items
[iItm
].rect
.right
;
1070 while(iIndexStart
< infoPtr
->uNumItem
)
1073 * find the indexs of the row
1075 for (iIndexEnd
=iIndexStart
;
1076 (iIndexEnd
< infoPtr
->uNumItem
) &&
1077 (infoPtr
->items
[iIndexEnd
].rect
.top
==
1078 infoPtr
->items
[iIndexStart
].rect
.top
) ;
1080 /* intentionaly blank */;
1083 * we need to justify these tabs so they fill the whole given
1087 widthDiff
= clientRect
.right
- (2*SELECTED_TAB_OFFSET
) -
1088 infoPtr
->items
[iIndexEnd
-1].rect
.right
;
1090 iCount
= iIndexEnd
-iIndexStart
;
1095 remainder
= widthDiff
% iCount
;
1096 widthDiff
= widthDiff
/ iCount
;
1097 for (iIndex
=iIndexStart
,iCount
=0; iIndex
< iIndexEnd
;
1100 infoPtr
->items
[iIndex
].rect
.left
+=iCount
*widthDiff
;
1101 infoPtr
->items
[iIndex
].rect
.right
+=(iCount
+1)*widthDiff
;
1103 infoPtr
->items
[iIndex
-1].rect
.right
+= remainder
;
1106 iIndexStart
=iIndexEnd
;
1111 TAB_EnsureSelectionVisible(hwnd
,infoPtr
);
1112 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
1116 SelectObject (hdc
, hOldFont
);
1117 ReleaseDC (hwnd
, hdc
);
1120 /******************************************************************************
1121 * TAB_DrawItemInterior
1123 * This method is used to draw the interior (text and icon) of a single tab
1124 * into the tab control.
1127 TAB_DrawItemInterior
1135 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
1136 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1140 HPEN htextPen
= GetSysColorPen (COLOR_BTNTEXT
);
1144 if (drawRect
== NULL
)
1151 * Get the rectangle for the item.
1153 isVisible
= TAB_InternalGetItemRect
1165 * Make sure drawRect points to something valid; simplifies code.
1167 drawRect
= &localRect
;
1170 * This logic copied from the part of TAB_DrawItem which draws
1171 * the tab background. It's important to keep it in sync. I
1172 * would have liked to avoid code duplication, but couldn't figure
1173 * out how without making spaghetti of TAB_DrawItem.
1175 if (lStyle
& TCS_BUTTONS
)
1177 *drawRect
= itemRect
;
1178 if (iItem
== infoPtr
->iSelected
)
1186 if (iItem
== infoPtr
->iSelected
)
1187 *drawRect
= selectedRect
;
1189 *drawRect
= itemRect
;
1198 holdPen
= SelectObject(hdc
, htextPen
);
1200 oldBkMode
= SetBkMode(hdc
, TRANSPARENT
);
1206 (iItem
== infoPtr
->iHotTracked
) ? COLOR_HIGHLIGHT
: COLOR_BTNTEXT
1211 * Deflate the rectangle to acount for the padding
1213 InflateRect(drawRect
, -HORIZONTAL_ITEM_PADDING
, -VERTICAL_ITEM_PADDING
);
1216 * if owner draw, tell the owner to draw
1218 if ( (lStyle
& TCS_OWNERDRAWFIXED
) && GetParent(hwnd
) )
1225 * get the control id
1227 pwndPtr
= WIN_FindWndPtr( hwnd
);
1228 id
= pwndPtr
->wIDmenu
;
1229 WIN_ReleaseWndPtr(pwndPtr
);
1232 * put together the DRAWITEMSTRUCT
1234 dis
.CtlType
= ODT_TAB
;
1237 dis
.itemAction
= ODA_DRAWENTIRE
;
1238 if ( iItem
== infoPtr
->iSelected
)
1239 dis
.itemState
= ODS_SELECTED
;
1242 dis
.hwndItem
= hwnd
; /* */
1244 dis
.rcItem
= *drawRect
; /* */
1245 dis
.itemData
= infoPtr
->items
[iItem
].lParam
;
1248 * send the draw message
1250 SendMessageA( GetParent(hwnd
), WM_DRAWITEM
, (WPARAM
)id
, (LPARAM
)&dis
);
1257 * If not owner draw, then do the drawing ourselves.
1261 if (infoPtr
->himl
&& (infoPtr
->items
[iItem
].mask
& TCIF_IMAGE
))
1269 infoPtr
->items
[iItem
].iImage
,
1275 ImageList_GetIconSize(infoPtr
->himl
, &cx
, &cy
);
1276 drawRect
->left
+= (cx
+ HORIZONTAL_ITEM_PADDING
);
1282 if (lStyle
& TCS_RIGHTJUSTIFY
)
1283 uHorizAlign
= DT_CENTER
;
1285 uHorizAlign
= DT_LEFT
;
1290 infoPtr
->items
[iItem
].pszText
,
1291 lstrlenA(infoPtr
->items
[iItem
].pszText
),
1293 uHorizAlign
| DT_SINGLELINE
| DT_VCENTER
1300 SetBkMode(hdc
, oldBkMode
);
1301 SelectObject(hdc
, holdPen
);
1304 /******************************************************************************
1307 * This method is used to draw a single tab into the tab control.
1309 static void TAB_DrawItem(
1314 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1315 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1322 * Get the rectangle for the item.
1324 isVisible
= TAB_InternalGetItemRect(hwnd
,
1332 HBRUSH hbr
= CreateSolidBrush (GetSysColor(COLOR_BTNFACE
));
1333 HPEN hwPen
= GetSysColorPen (COLOR_3DHILIGHT
);
1334 HPEN hbPen
= GetSysColorPen (COLOR_BTNSHADOW
);
1335 HPEN hfocusPen
= CreatePen(PS_DOT
, 1, GetSysColor(COLOR_BTNTEXT
));
1338 BOOL deleteBrush
= TRUE
;
1340 if (lStyle
& TCS_BUTTONS
)
1343 * Get item rectangle.
1347 holdPen
= SelectObject (hdc
, hwPen
);
1349 if (iItem
== infoPtr
->iSelected
)
1354 if (!((lStyle
& TCS_OWNERDRAWFIXED
) && infoPtr
->fSizeSet
))
1356 COLORREF bk
= GetSysColor(COLOR_3DHILIGHT
);
1358 hbr
= GetSysColorBrush(COLOR_SCROLLBAR
);
1359 SetTextColor(hdc
, GetSysColor(COLOR_3DFACE
));
1360 SetBkColor(hdc
, bk
);
1362 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1363 * we better use 0x55aa bitmap brush to make scrollbar's background
1364 * look different from the window background.
1366 if (bk
== GetSysColor(COLOR_WINDOW
))
1367 hbr
= CACHE_GetPattern55AABrush();
1369 deleteBrush
= FALSE
;
1373 * Erase the background.
1375 FillRect(hdc
, &r
, hbr
);
1379 * The rectangles calculated exclude the right and bottom
1380 * borders of the rectangle. To simply the following code, those
1381 * borders are shaved-off beforehand.
1387 MoveToEx (hdc
, r
.left
, r
.bottom
, NULL
);
1388 LineTo (hdc
, r
.right
, r
.bottom
);
1389 LineTo (hdc
, r
.right
, r
.top
);
1392 SelectObject(hdc
, hbPen
);
1393 LineTo (hdc
, r
.left
, r
.top
);
1394 LineTo (hdc
, r
.left
, r
.bottom
);
1399 * Erase the background.
1401 FillRect(hdc
, &r
, hbr
);
1404 MoveToEx (hdc
, r
.left
, r
.bottom
, NULL
);
1405 LineTo (hdc
, r
.left
, r
.top
);
1406 LineTo (hdc
, r
.right
, r
.top
);
1409 SelectObject(hdc
, hbPen
);
1410 LineTo (hdc
, r
.right
, r
.bottom
);
1411 LineTo (hdc
, r
.left
, r
.bottom
);
1420 hbr
= CreateSolidBrush(GetSysColor(COLOR_BTNFACE
));
1423 * We draw a rectangle of different sizes depending on the selection
1426 if (iItem
== infoPtr
->iSelected
)
1432 * Erase the background.
1433 * This is necessary when drawing the selected item since it is larger
1434 * than the others, it might overlap with stuff already drawn by the
1437 FillRect(hdc
, &r
, hbr
);
1441 * The rectangles calculated exclude the right and bottom
1442 * borders of the rectangle. To simply the following code, those
1443 * borders are shaved-off beforehand.
1448 holdPen
= SelectObject (hdc
, hwPen
);
1450 if (lStyle
& TCS_BOTTOM
)
1453 MoveToEx (hdc
, r
.left
, r
.top
, NULL
);
1454 LineTo (hdc
, r
.left
, r
.bottom
- ROUND_CORNER_SIZE
);
1455 LineTo (hdc
, r
.left
+ ROUND_CORNER_SIZE
, r
.bottom
);
1458 SelectObject(hdc
, hbPen
);
1459 LineTo (hdc
, r
.right
- ROUND_CORNER_SIZE
, r
.bottom
);
1460 LineTo (hdc
, r
.right
, r
.bottom
- ROUND_CORNER_SIZE
);
1461 LineTo (hdc
, r
.right
, r
.top
);
1466 MoveToEx (hdc
, r
.left
, r
.bottom
, NULL
);
1467 LineTo (hdc
, r
.left
, r
.top
+ ROUND_CORNER_SIZE
);
1468 LineTo (hdc
, r
.left
+ ROUND_CORNER_SIZE
, r
.top
);
1469 LineTo (hdc
, r
.right
- ROUND_CORNER_SIZE
, r
.top
);
1472 SelectObject(hdc
, hbPen
);
1473 LineTo (hdc
, r
.right
, r
.top
+ ROUND_CORNER_SIZE
);
1474 LineTo (hdc
, r
.right
, r
.bottom
);
1478 oldBkMode
= SetBkMode(hdc
, TRANSPARENT
);
1480 /* This modifies r to be the text rectangle. */
1481 TAB_DrawItemInterior(hwnd
, hdc
, iItem
, &r
);
1484 * Draw the focus rectangle
1486 if (((lStyle
& TCS_FOCUSNEVER
) == 0) &&
1487 (GetFocus() == hwnd
) &&
1488 (iItem
== infoPtr
->uFocus
) )
1490 InflateRect(&r
, FOCUS_RECT_HOFFSET
, FOCUS_RECT_VOFFSET
);
1492 SelectObject(hdc
, hfocusPen
);
1494 MoveToEx (hdc
, r
.left
, r
.top
, NULL
);
1495 LineTo (hdc
, r
.right
-1, r
.top
);
1496 LineTo (hdc
, r
.right
-1, r
.bottom
-1);
1497 LineTo (hdc
, r
.left
, r
.bottom
-1);
1498 LineTo (hdc
, r
.left
, r
.top
);
1504 SetBkMode(hdc
, oldBkMode
);
1505 SelectObject(hdc
, holdPen
);
1506 DeleteObject(hfocusPen
);
1507 if (deleteBrush
) DeleteObject(hbr
);
1511 /******************************************************************************
1514 * This method is used to draw the raised border around the tab control
1517 static void TAB_DrawBorder (HWND hwnd
, HDC hdc
)
1519 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1521 HPEN hwPen
= GetSysColorPen (COLOR_3DHILIGHT
);
1522 HPEN hbPen
= GetSysColorPen (COLOR_3DDKSHADOW
);
1523 HPEN hShade
= GetSysColorPen (COLOR_BTNSHADOW
);
1526 GetClientRect (hwnd
, &rect
);
1529 * Adjust for the style
1531 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
1533 rect
.bottom
-= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
1537 rect
.top
+= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
1541 * Shave-off the right and bottom margins (exluded in the
1548 htmPen
= SelectObject (hdc
, hwPen
);
1550 MoveToEx (hdc
, rect
.left
, rect
.bottom
, NULL
);
1551 LineTo (hdc
, rect
.left
, rect
.top
);
1552 LineTo (hdc
, rect
.right
, rect
.top
);
1555 SelectObject (hdc
, hbPen
);
1556 LineTo (hdc
, rect
.right
, rect
.bottom
);
1557 LineTo (hdc
, rect
.left
, rect
.bottom
);
1560 SelectObject (hdc
, hShade
);
1561 MoveToEx (hdc
, rect
.right
-1, rect
.top
, NULL
);
1562 LineTo (hdc
, rect
.right
-1, rect
.bottom
-1);
1563 LineTo (hdc
, rect
.left
, rect
.bottom
-1);
1565 SelectObject(hdc
, htmPen
);
1568 /******************************************************************************
1571 * This method repaints the tab control..
1573 static void TAB_Refresh (HWND hwnd
, HDC hdc
)
1575 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1579 if (!infoPtr
->DoRedraw
)
1582 hOldFont
= SelectObject (hdc
, infoPtr
->hFont
);
1584 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BUTTONS
)
1586 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
1588 TAB_DrawItem (hwnd
, hdc
, i
);
1594 * Draw all the non selected item first.
1596 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
1598 if (i
!= infoPtr
->iSelected
)
1599 TAB_DrawItem (hwnd
, hdc
, i
);
1603 * Now, draw the border, draw it before the selected item
1604 * since the selected item overwrites part of the border.
1606 TAB_DrawBorder (hwnd
, hdc
);
1609 * Then, draw the selected item
1611 TAB_DrawItem (hwnd
, hdc
, infoPtr
->iSelected
);
1614 * If we haven't set the current focus yet, set it now.
1615 * Only happens when we first paint the tab controls.
1617 if (infoPtr
->uFocus
== -1)
1618 TAB_SetCurFocus(hwnd
, infoPtr
->iSelected
);
1621 SelectObject (hdc
, hOldFont
);
1625 TAB_SetRedraw (HWND hwnd
, WPARAM wParam
)
1627 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1629 infoPtr
->DoRedraw
=(BOOL
) wParam
;
1633 static LRESULT
TAB_EraseBackground(
1640 HBRUSH brush
= CreateSolidBrush(GetSysColor(COLOR_BTNFACE
));
1642 hdc
= givenDC
? givenDC
: GetDC(hwnd
);
1644 GetClientRect(hwnd
, &clientRect
);
1646 FillRect(hdc
, &clientRect
, brush
);
1649 ReleaseDC(hwnd
, hdc
);
1651 DeleteObject(brush
);
1656 /******************************************************************************
1657 * TAB_EnsureSelectionVisible
1659 * This method will make sure that the current selection is completely
1660 * visible by scrolling until it is.
1662 static void TAB_EnsureSelectionVisible(
1666 INT iSelected
= infoPtr
->iSelected
;
1668 INT iOrigLeftmostVisible
= infoPtr
->leftmostVisible
;
1671 * set the items row to the bottommost row or topmost row depending on
1675 if (infoPtr
->uNumRows
> 0)
1677 INT newselected
=infoPtr
->items
[iSelected
].rect
.top
;
1679 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1681 if (lStyle
& TCS_BOTTOM
)
1684 iTargetRow
= infoPtr
->uNumRows
;
1686 if (newselected
!= iTargetRow
)
1689 for (i
=0; i
< infoPtr
->uNumItem
; i
++)
1690 if (infoPtr
->items
[i
].rect
.top
== newselected
)
1691 infoPtr
->items
[i
].rect
.top
= iTargetRow
;
1692 else if (lStyle
&TCS_BOTTOM
)
1694 if (infoPtr
->items
[i
].rect
.top
< newselected
)
1695 infoPtr
->items
[i
].rect
.top
+=1;
1699 if (infoPtr
->items
[i
].rect
.top
> newselected
)
1700 infoPtr
->items
[i
].rect
.top
-=1;
1703 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
1708 * Do the trivial cases first.
1710 if ( (!infoPtr
->needsScrolling
) ||
1711 (infoPtr
->hwndUpDown
==0) )
1714 if (infoPtr
->leftmostVisible
>= iSelected
)
1716 infoPtr
->leftmostVisible
= iSelected
;
1723 * Calculate the part of the client area that is visible.
1725 GetClientRect(hwnd
, &r
);
1728 GetClientRect(infoPtr
->hwndUpDown
, &r
);
1731 if ((infoPtr
->items
[iSelected
].rect
.right
-
1732 infoPtr
->items
[iSelected
].rect
.left
) >= width
)
1734 /* Special case: width of selected item is greater than visible
1737 infoPtr
->leftmostVisible
= iSelected
;
1741 for (i
= infoPtr
->leftmostVisible
; i
< infoPtr
->uNumItem
; i
++)
1743 if ((infoPtr
->items
[iSelected
].rect
.right
-
1744 infoPtr
->items
[i
].rect
.left
) < width
)
1747 infoPtr
->leftmostVisible
= i
;
1751 if (infoPtr
->leftmostVisible
!= iOrigLeftmostVisible
)
1752 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
1754 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
1755 MAKELONG(infoPtr
->leftmostVisible
, 0));
1758 /******************************************************************************
1759 * TAB_InvalidateTabArea
1761 * This method will invalidate the portion of the control that contains the
1762 * tabs. It is called when the state of the control changes and needs
1765 static void TAB_InvalidateTabArea(
1771 GetClientRect(hwnd
, &clientRect
);
1773 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
1775 clientRect
.top
= clientRect
.bottom
- (infoPtr
->tabHeight
*
1776 (infoPtr
->uNumRows
+ 1) + 3);
1780 clientRect
.bottom
= clientRect
.top
+ (infoPtr
->tabHeight
*
1781 (infoPtr
->uNumRows
+ 1) + 1);
1784 InvalidateRect(hwnd
, &clientRect
, TRUE
);
1788 TAB_Paint (HWND hwnd
, WPARAM wParam
)
1793 hdc
= wParam
== 0 ? BeginPaint (hwnd
, &ps
) : (HDC
)wParam
;
1794 TAB_Refresh (hwnd
, hdc
);
1797 EndPaint (hwnd
, &ps
);
1803 TAB_InsertItem (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1805 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1810 GetClientRect (hwnd
, &rect
);
1811 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd
,
1812 rect
.top
, rect
.left
, rect
.bottom
, rect
.right
);
1814 pti
= (TCITEMA
*)lParam
;
1815 iItem
= (INT
)wParam
;
1817 if (iItem
< 0) return -1;
1818 if (iItem
> infoPtr
->uNumItem
)
1819 iItem
= infoPtr
->uNumItem
;
1821 if (infoPtr
->uNumItem
== 0) {
1822 infoPtr
->items
= COMCTL32_Alloc (sizeof (TAB_ITEM
));
1823 infoPtr
->uNumItem
++;
1824 infoPtr
->iSelected
= 0;
1827 TAB_ITEM
*oldItems
= infoPtr
->items
;
1829 infoPtr
->uNumItem
++;
1830 infoPtr
->items
= COMCTL32_Alloc (sizeof (TAB_ITEM
) * infoPtr
->uNumItem
);
1832 /* pre insert copy */
1834 memcpy (&infoPtr
->items
[0], &oldItems
[0],
1835 iItem
* sizeof(TAB_ITEM
));
1838 /* post insert copy */
1839 if (iItem
< infoPtr
->uNumItem
- 1) {
1840 memcpy (&infoPtr
->items
[iItem
+1], &oldItems
[iItem
],
1841 (infoPtr
->uNumItem
- iItem
- 1) * sizeof(TAB_ITEM
));
1845 if (iItem
<= infoPtr
->iSelected
)
1846 infoPtr
->iSelected
++;
1848 COMCTL32_Free (oldItems
);
1851 infoPtr
->items
[iItem
].mask
= pti
->mask
;
1852 if (pti
->mask
& TCIF_TEXT
) {
1853 len
= lstrlenA (pti
->pszText
);
1854 infoPtr
->items
[iItem
].pszText
= COMCTL32_Alloc (len
+1);
1855 lstrcpyA (infoPtr
->items
[iItem
].pszText
, pti
->pszText
);
1856 infoPtr
->items
[iItem
].cchTextMax
= pti
->cchTextMax
;
1859 if (pti
->mask
& TCIF_IMAGE
)
1860 infoPtr
->items
[iItem
].iImage
= pti
->iImage
;
1862 if (pti
->mask
& TCIF_PARAM
)
1863 infoPtr
->items
[iItem
].lParam
= pti
->lParam
;
1865 TAB_SetItemBounds(hwnd
);
1866 TAB_InvalidateTabArea(hwnd
, infoPtr
);
1868 TRACE("[%04x]: added item %d '%s'\n",
1869 hwnd
, iItem
, infoPtr
->items
[iItem
].pszText
);
1875 TAB_SetItemSize (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1877 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1878 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1881 if ((lStyle
& TCS_FIXEDWIDTH
) || (lStyle
& TCS_OWNERDRAWFIXED
))
1883 lResult
= MAKELONG(infoPtr
->tabWidth
, infoPtr
->tabHeight
);
1884 infoPtr
->tabWidth
= (INT
)LOWORD(lParam
);
1885 infoPtr
->tabHeight
= (INT
)HIWORD(lParam
);
1887 infoPtr
->fSizeSet
= TRUE
;
1893 TAB_SetItemA (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1895 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1901 tabItem
=(LPTCITEMA
) lParam
;
1902 TRACE("%d %p\n",iItem
, tabItem
);
1903 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
)) return FALSE
;
1905 wineItem
=& infoPtr
->items
[iItem
];
1907 if (tabItem
->mask
& TCIF_IMAGE
)
1908 wineItem
->iImage
=tabItem
->iImage
;
1910 if (tabItem
->mask
& TCIF_PARAM
)
1911 wineItem
->lParam
=tabItem
->lParam
;
1913 if (tabItem
->mask
& TCIF_RTLREADING
)
1914 FIXME("TCIF_RTLREADING\n");
1916 if (tabItem
->mask
& TCIF_STATE
)
1917 wineItem
->dwState
=tabItem
->dwState
;
1919 if (tabItem
->mask
& TCIF_TEXT
) {
1920 len
=lstrlenA (tabItem
->pszText
);
1921 if (len
>wineItem
->cchTextMax
)
1922 wineItem
->pszText
= COMCTL32_ReAlloc (wineItem
->pszText
, len
+1);
1923 lstrcpyA (wineItem
->pszText
, tabItem
->pszText
);
1927 * Update and repaint tabs.
1929 TAB_SetItemBounds(hwnd
);
1930 TAB_InvalidateTabArea(hwnd
,infoPtr
);
1936 TAB_GetItemCount (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1938 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1940 return infoPtr
->uNumItem
;
1945 TAB_GetItemA (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1947 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1953 tabItem
=(LPTCITEMA
) lParam
;
1955 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
)) return FALSE
;
1957 wineItem
=& infoPtr
->items
[iItem
];
1959 if (tabItem
->mask
& TCIF_IMAGE
)
1960 tabItem
->iImage
=wineItem
->iImage
;
1962 if (tabItem
->mask
& TCIF_PARAM
)
1963 tabItem
->lParam
=wineItem
->lParam
;
1965 if (tabItem
->mask
& TCIF_RTLREADING
)
1966 FIXME("TCIF_RTLREADING\n");
1968 if (tabItem
->mask
& TCIF_STATE
)
1969 tabItem
->dwState
=wineItem
->dwState
;
1971 if (tabItem
->mask
& TCIF_TEXT
)
1972 lstrcpynA (tabItem
->pszText
, wineItem
->pszText
, tabItem
->cchTextMax
);
1978 TAB_DeleteItem (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1980 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1981 INT iItem
= (INT
) wParam
;
1982 BOOL bResult
= FALSE
;
1984 if ((iItem
>= 0) && (iItem
< infoPtr
->uNumItem
))
1986 TAB_ITEM
*oldItems
= infoPtr
->items
;
1988 infoPtr
->uNumItem
--;
1989 infoPtr
->items
= COMCTL32_Alloc(sizeof (TAB_ITEM
) * infoPtr
->uNumItem
);
1992 memcpy(&infoPtr
->items
[0], &oldItems
[0], iItem
* sizeof(TAB_ITEM
));
1994 if (iItem
< infoPtr
->uNumItem
)
1995 memcpy(&infoPtr
->items
[iItem
], &oldItems
[iItem
+ 1],
1996 (infoPtr
->uNumItem
- iItem
) * sizeof(TAB_ITEM
));
1998 COMCTL32_Free (oldItems
);
2001 * Readjust the selected index.
2003 if ((iItem
== infoPtr
->iSelected
) && (iItem
> 0))
2004 infoPtr
->iSelected
--;
2006 if (iItem
< infoPtr
->iSelected
)
2007 infoPtr
->iSelected
--;
2009 if (infoPtr
->uNumItem
== 0)
2010 infoPtr
->iSelected
= -1;
2013 * Reposition and repaint tabs.
2015 TAB_SetItemBounds(hwnd
);
2016 TAB_InvalidateTabArea(hwnd
,infoPtr
);
2025 TAB_DeleteAllItems (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2027 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2029 COMCTL32_Free (infoPtr
->items
);
2030 infoPtr
->uNumItem
= 0;
2031 infoPtr
->iSelected
= -1;
2032 if (infoPtr
->iHotTracked
>= 0)
2033 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
2034 infoPtr
->iHotTracked
= -1;
2036 TAB_SetItemBounds(hwnd
);
2037 TAB_InvalidateTabArea(hwnd
,infoPtr
);
2043 TAB_GetFont (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2045 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2048 return (LRESULT
)infoPtr
->hFont
;
2052 TAB_SetFont (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2055 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2057 TRACE("%x %lx\n",wParam
, lParam
);
2059 infoPtr
->hFont
= (HFONT
)wParam
;
2061 TAB_SetItemBounds(hwnd
);
2063 TAB_InvalidateTabArea(hwnd
, infoPtr
);
2070 TAB_GetImageList (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2072 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2075 return (LRESULT
)infoPtr
->himl
;
2079 TAB_SetImageList (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2081 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2082 HIMAGELIST himlPrev
;
2085 himlPrev
= infoPtr
->himl
;
2086 infoPtr
->himl
= (HIMAGELIST
)lParam
;
2087 return (LRESULT
)himlPrev
;
2092 TAB_Size (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2095 /* I'm not really sure what the following code was meant to do.
2096 This is what it is doing:
2097 When WM_SIZE is sent with SIZE_RESTORED, the control
2098 gets positioned in the top left corner.
2102 UINT uPosFlags,cx,cy;
2106 parent = GetParent (hwnd);
2107 GetClientRect(parent, &parent_rect);
2110 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2111 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2113 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2114 cx, cy, uPosFlags | SWP_NOZORDER);
2116 FIXME (tab,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2120 * Recompute the size/position of the tabs.
2122 TAB_SetItemBounds (hwnd
);
2125 * Force a repaint of the control.
2127 InvalidateRect(hwnd
, NULL
, TRUE
);
2134 TAB_Create (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2137 TEXTMETRICA fontMetrics
;
2142 infoPtr
= (TAB_INFO
*)COMCTL32_Alloc (sizeof(TAB_INFO
));
2144 SetWindowLongA(hwnd
, 0, (DWORD
)infoPtr
);
2146 infoPtr
->uNumItem
= 0;
2147 infoPtr
->uNumRows
= 0;
2150 infoPtr
->hcurArrow
= LoadCursorA (0, IDC_ARROWA
);
2151 infoPtr
->iSelected
= -1;
2152 infoPtr
->iHotTracked
= -1;
2153 infoPtr
->uFocus
= -1;
2154 infoPtr
->hwndToolTip
= 0;
2155 infoPtr
->DoRedraw
= TRUE
;
2156 infoPtr
->needsScrolling
= FALSE
;
2157 infoPtr
->hwndUpDown
= 0;
2158 infoPtr
->leftmostVisible
= 0;
2159 infoPtr
->fSizeSet
= FALSE
;
2161 TRACE("Created tab control, hwnd [%04x]\n", hwnd
);
2163 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2164 if you don't specify in CreateWindow. This is necesary in
2165 order for paint to work correctly. This follows windows behaviour. */
2166 dwStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
2167 SetWindowLongA(hwnd
, GWL_STYLE
, dwStyle
|WS_CLIPSIBLINGS
);
2169 if (dwStyle
& TCS_TOOLTIPS
) {
2170 /* Create tooltip control */
2171 infoPtr
->hwndToolTip
=
2172 CreateWindowExA (0, TOOLTIPS_CLASSA
, NULL
, 0,
2173 CW_USEDEFAULT
, CW_USEDEFAULT
,
2174 CW_USEDEFAULT
, CW_USEDEFAULT
,
2177 /* Send NM_TOOLTIPSCREATED notification */
2178 if (infoPtr
->hwndToolTip
) {
2179 NMTOOLTIPSCREATED nmttc
;
2181 nmttc
.hdr
.hwndFrom
= hwnd
;
2182 nmttc
.hdr
.idFrom
= GetWindowLongA(hwnd
, GWL_ID
);
2183 nmttc
.hdr
.code
= NM_TOOLTIPSCREATED
;
2184 nmttc
.hwndToolTips
= infoPtr
->hwndToolTip
;
2186 SendMessageA (GetParent (hwnd
), WM_NOTIFY
,
2187 (WPARAM
)GetWindowLongA(hwnd
, GWL_ID
), (LPARAM
)&nmttc
);
2192 * We need to get text information so we need a DC and we need to select
2196 hOldFont
= SelectObject (hdc
, GetStockObject (SYSTEM_FONT
));
2199 * Use the system font to determine the initial height of a tab.
2201 GetTextMetricsA(hdc
, &fontMetrics
);
2204 * Make sure there is enough space for the letters + growing the
2205 * selected item + extra space for the selected item.
2207 infoPtr
->tabHeight
= fontMetrics
.tmHeight
+ 2*VERTICAL_ITEM_PADDING
+
2208 SELECTED_TAB_OFFSET
;
2211 * Initialize the width of a tab.
2213 infoPtr
->tabWidth
= DEFAULT_TAB_WIDTH
;
2215 SelectObject (hdc
, hOldFont
);
2216 ReleaseDC(hwnd
, hdc
);
2222 TAB_Destroy (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2224 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2230 if (infoPtr
->items
) {
2231 for (iItem
= 0; iItem
< infoPtr
->uNumItem
; iItem
++) {
2232 if (infoPtr
->items
[iItem
].pszText
)
2233 COMCTL32_Free (infoPtr
->items
[iItem
].pszText
);
2235 COMCTL32_Free (infoPtr
->items
);
2238 if (infoPtr
->hwndToolTip
)
2239 DestroyWindow (infoPtr
->hwndToolTip
);
2241 if (infoPtr
->hwndUpDown
)
2242 DestroyWindow(infoPtr
->hwndUpDown
);
2244 if (infoPtr
->iHotTracked
>= 0)
2245 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
2247 COMCTL32_Free (infoPtr
);
2248 SetWindowLongA(hwnd
, 0, 0);
2252 static LRESULT WINAPI
2253 TAB_WindowProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
2256 TRACE("hwnd=%x msg=%x wParam=%x lParam=%lx\n", hwnd
, uMsg
, wParam
, lParam
);
2257 if (!TAB_GetInfoPtr(hwnd
) && (uMsg
!= WM_CREATE
))
2258 return DefWindowProcA (hwnd
, uMsg
, wParam
, lParam
);
2262 case TCM_GETIMAGELIST
:
2263 return TAB_GetImageList (hwnd
, wParam
, lParam
);
2265 case TCM_SETIMAGELIST
:
2266 return TAB_SetImageList (hwnd
, wParam
, lParam
);
2268 case TCM_GETITEMCOUNT
:
2269 return TAB_GetItemCount (hwnd
, wParam
, lParam
);
2272 return TAB_GetItemA (hwnd
, wParam
, lParam
);
2275 FIXME("Unimplemented msg TCM_GETITEMW\n");
2279 return TAB_SetItemA (hwnd
, wParam
, lParam
);
2282 FIXME("Unimplemented msg TCM_SETITEMW\n");
2285 case TCM_DELETEITEM
:
2286 return TAB_DeleteItem (hwnd
, wParam
, lParam
);
2288 case TCM_DELETEALLITEMS
:
2289 return TAB_DeleteAllItems (hwnd
, wParam
, lParam
);
2291 case TCM_GETITEMRECT
:
2292 return TAB_GetItemRect (hwnd
, wParam
, lParam
);
2295 return TAB_GetCurSel (hwnd
);
2298 return TAB_HitTest (hwnd
, wParam
, lParam
);
2301 return TAB_SetCurSel (hwnd
, wParam
);
2303 case TCM_INSERTITEMA
:
2304 return TAB_InsertItem (hwnd
, wParam
, lParam
);
2306 case TCM_INSERTITEMW
:
2307 FIXME("Unimplemented msg TCM_INSERTITEMW\n");
2310 case TCM_SETITEMEXTRA
:
2311 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
2314 case TCM_ADJUSTRECT
:
2315 return TAB_AdjustRect (hwnd
, (BOOL
)wParam
, (LPRECT
)lParam
);
2317 case TCM_SETITEMSIZE
:
2318 return TAB_SetItemSize (hwnd
, wParam
, lParam
);
2320 case TCM_REMOVEIMAGE
:
2321 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
2324 case TCM_SETPADDING
:
2325 FIXME("Unimplemented msg TCM_SETPADDING\n");
2328 case TCM_GETROWCOUNT
:
2329 FIXME("Unimplemented msg TCM_GETROWCOUNT\n");
2332 case TCM_GETUNICODEFORMAT
:
2333 FIXME("Unimplemented msg TCM_GETUNICODEFORMAT\n");
2336 case TCM_SETUNICODEFORMAT
:
2337 FIXME("Unimplemented msg TCM_SETUNICODEFORMAT\n");
2340 case TCM_HIGHLIGHTITEM
:
2341 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
2344 case TCM_GETTOOLTIPS
:
2345 return TAB_GetToolTips (hwnd
, wParam
, lParam
);
2347 case TCM_SETTOOLTIPS
:
2348 return TAB_SetToolTips (hwnd
, wParam
, lParam
);
2350 case TCM_GETCURFOCUS
:
2351 return TAB_GetCurFocus (hwnd
);
2353 case TCM_SETCURFOCUS
:
2354 return TAB_SetCurFocus (hwnd
, wParam
);
2356 case TCM_SETMINTABWIDTH
:
2357 FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
2360 case TCM_DESELECTALL
:
2361 FIXME("Unimplemented msg TCM_DESELECTALL\n");
2364 case TCM_GETEXTENDEDSTYLE
:
2365 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
2368 case TCM_SETEXTENDEDSTYLE
:
2369 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
2373 return TAB_GetFont (hwnd
, wParam
, lParam
);
2376 return TAB_SetFont (hwnd
, wParam
, lParam
);
2379 return TAB_Create (hwnd
, wParam
, lParam
);
2382 return TAB_Destroy (hwnd
, wParam
, lParam
);
2385 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2387 case WM_LBUTTONDOWN
:
2388 return TAB_LButtonDown (hwnd
, wParam
, lParam
);
2391 return TAB_LButtonUp (hwnd
, wParam
, lParam
);
2393 case WM_RBUTTONDOWN
:
2394 return TAB_RButtonDown (hwnd
, wParam
, lParam
);
2397 return TAB_MouseMove (hwnd
, wParam
, lParam
);
2400 return TAB_EraseBackground (hwnd
, (HDC
)wParam
);
2403 return TAB_Paint (hwnd
, wParam
);
2406 return TAB_Size (hwnd
, wParam
, lParam
);
2409 return TAB_SetRedraw (hwnd
, wParam
);
2412 return TAB_OnHScroll(hwnd
, (int)LOWORD(wParam
), (int)HIWORD(wParam
), (HWND
)lParam
);
2414 case WM_STYLECHANGED
:
2415 TAB_SetItemBounds (hwnd
);
2416 InvalidateRect(hwnd
, NULL
, TRUE
);
2421 return TAB_FocusChanging(hwnd
, uMsg
, wParam
, lParam
);
2424 return TAB_KeyUp(hwnd
, wParam
);
2427 if (uMsg
>= WM_USER
)
2428 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
2429 uMsg
, wParam
, lParam
);
2430 return DefWindowProcA (hwnd
, uMsg
, wParam
, lParam
);
2442 ZeroMemory (&wndClass
, sizeof(WNDCLASSA
));
2443 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
;
2444 wndClass
.lpfnWndProc
= (WNDPROC
)TAB_WindowProc
;
2445 wndClass
.cbClsExtra
= 0;
2446 wndClass
.cbWndExtra
= sizeof(TAB_INFO
*);
2447 wndClass
.hCursor
= LoadCursorA (0, IDC_ARROWA
);
2448 wndClass
.hbrBackground
= (HBRUSH
)NULL
;
2449 wndClass
.lpszClassName
= WC_TABCONTROLA
;
2451 RegisterClassA (&wndClass
);
2456 TAB_Unregister (void)
2458 UnregisterClassA (WC_TABCONTROLA
, (HINSTANCE
)NULL
);