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 /******************************************************************************
46 static void TAB_Refresh (HWND hwnd
, HDC hdc
);
47 static void TAB_InvalidateTabArea(HWND hwnd
, TAB_INFO
* infoPtr
);
48 static void TAB_EnsureSelectionVisible(HWND hwnd
, TAB_INFO
* infoPtr
);
51 TAB_SendSimpleNotify (HWND hwnd
, UINT code
)
55 nmhdr
.hwndFrom
= hwnd
;
56 nmhdr
.idFrom
= GetWindowLongA(hwnd
, GWL_ID
);
59 return (BOOL
) SendMessageA (GetParent (hwnd
), WM_NOTIFY
,
60 (WPARAM
) nmhdr
.idFrom
, (LPARAM
) &nmhdr
);
65 TAB_RelayEvent (HWND hwndTip
, HWND hwndMsg
, UINT uMsg
,
66 WPARAM wParam
, LPARAM lParam
)
74 msg
.time
= GetMessageTime ();
75 msg
.pt
.x
= LOWORD(GetMessagePos ());
76 msg
.pt
.y
= HIWORD(GetMessagePos ());
78 SendMessageA (hwndTip
, TTM_RELAYEVENT
, 0, (LPARAM
)&msg
);
84 TAB_GetCurSel (HWND hwnd
)
86 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
88 return infoPtr
->iSelected
;
92 TAB_GetCurFocus (HWND hwnd
)
94 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
96 return infoPtr
->uFocus
;
100 TAB_GetToolTips (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
102 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
104 if (infoPtr
== NULL
) return 0;
105 return infoPtr
->hwndToolTip
;
110 TAB_SetCurSel (HWND hwnd
,WPARAM wParam
)
112 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
113 INT iItem
=(INT
) wParam
;
117 if ((iItem
>= 0) && (iItem
< infoPtr
->uNumItem
)) {
118 prevItem
=infoPtr
->iSelected
;
119 infoPtr
->iSelected
=iItem
;
120 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
121 TAB_InvalidateTabArea(hwnd
, infoPtr
);
127 TAB_SetCurFocus (HWND hwnd
,WPARAM wParam
)
129 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
130 INT iItem
=(INT
) wParam
;
132 if ((iItem
< 0) || (iItem
>= infoPtr
->uNumItem
)) return 0;
134 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BUTTONS
) {
135 FIXME("Should set input focus\n");
137 if (infoPtr
->iSelected
!= iItem
|| infoPtr
->uFocus
== -1 ) {
138 infoPtr
->uFocus
=iItem
;
139 if (TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
)!=TRUE
) {
140 infoPtr
->iSelected
= iItem
;
141 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
143 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
144 TAB_InvalidateTabArea(hwnd
, infoPtr
);
152 TAB_SetToolTips (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
154 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
156 if (infoPtr
== NULL
) return 0;
157 infoPtr
->hwndToolTip
= (HWND
)wParam
;
161 /******************************************************************************
162 * TAB_InternalGetItemRect
164 * This method will calculate the rectangle representing a given tab item in
165 * client coordinates. This method takes scrolling into account.
167 * This method returns TRUE if the item is visible in the window and FALSE
168 * if it is completely outside the client area.
170 static BOOL
TAB_InternalGetItemRect(
177 RECT tmpItemRect
,clientRect
;
178 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
181 * Perform a sanity check and a trivial visibility check.
183 if ( (infoPtr
->uNumItem
<=0) ||
184 (itemIndex
>= infoPtr
->uNumItem
) ||
185 (!(lStyle
&TCS_MULTILINE
) && (itemIndex
< infoPtr
->leftmostVisible
)) )
189 * Avoid special cases in this procedure by assigning the "out"
190 * parameters if the caller didn't supply them
193 itemRect
= &tmpItemRect
;
196 * Retrieve the unmodified item rect.
198 *itemRect
= infoPtr
->items
[itemIndex
].rect
;
201 * calculate the times bottom and top based on the row
203 GetClientRect(hwnd
, &clientRect
);
205 if (lStyle
& TCS_BOTTOM
)
207 itemRect
->bottom
= clientRect
.bottom
-
208 SELECTED_TAB_OFFSET
-
209 itemRect
->top
* (infoPtr
->tabHeight
- 2);
211 itemRect
->top
= clientRect
.bottom
-
213 itemRect
->top
* ( infoPtr
->tabHeight
- 2);
217 itemRect
->bottom
= clientRect
.top
+
219 itemRect
->top
* (infoPtr
->tabHeight
- 2);
220 itemRect
->top
= clientRect
.top
+
222 itemRect
->top
* (infoPtr
->tabHeight
- 2);
226 * "scroll" it to make sure the item at the very left of the
227 * tab control is the leftmost visible tab.
230 -infoPtr
->items
[infoPtr
->leftmostVisible
].rect
.left
,
234 * Move the rectangle so the first item is slightly offset from
235 * the left of the tab control.
243 * Now, calculate the position of the item as if it were selected.
245 if (selectedRect
!=NULL
)
247 CopyRect(selectedRect
, itemRect
);
250 * The rectangle of a selected item is a bit wider.
252 InflateRect(selectedRect
, SELECTED_TAB_OFFSET
, 0);
255 * If it also a bit higher.
257 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
259 selectedRect
->top
-=2; /* the border is thicker on the bottom */
260 selectedRect
->bottom
+=SELECTED_TAB_OFFSET
;
264 selectedRect
->top
-=SELECTED_TAB_OFFSET
;
265 selectedRect
->bottom
+=1;
272 static BOOL
TAB_GetItemRect(HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
274 return TAB_InternalGetItemRect(hwnd
, TAB_GetInfoPtr(hwnd
), (INT
)wParam
,
275 (LPRECT
)lParam
, (LPRECT
)NULL
);
278 /******************************************************************************
281 * This method is called to handle keyboard input
283 static LRESULT
TAB_KeyUp(
287 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
293 newItem
= infoPtr
->uFocus
-1;
296 newItem
= infoPtr
->uFocus
+1;
301 * If we changed to a valid item, change the selection
303 if ( (newItem
>= 0) &&
304 (newItem
< infoPtr
->uNumItem
) &&
305 (infoPtr
->uFocus
!= newItem
) )
307 if (!TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
))
309 infoPtr
->iSelected
= newItem
;
310 infoPtr
->uFocus
= newItem
;
311 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
313 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
314 TAB_InvalidateTabArea(hwnd
, infoPtr
);
321 /******************************************************************************
324 * This method is called whenever the focus goes in or out of this control
325 * it is used to update the visual state of the control.
327 static LRESULT
TAB_FocusChanging(
333 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
338 * Get the rectangle for the item.
340 isVisible
= TAB_InternalGetItemRect(hwnd
,
347 * If the rectangle is not completely invisible, invalidate that
348 * portion of the window.
352 InvalidateRect(hwnd
, &selectedRect
, TRUE
);
356 * Don't otherwise disturb normal behavior.
358 return DefWindowProcA (hwnd
, uMsg
, wParam
, lParam
);
361 static HWND
TAB_InternalHitTest (
371 for (iCount
= 0; iCount
< infoPtr
->uNumItem
; iCount
++)
373 TAB_InternalGetItemRect(hwnd
,
379 if (PtInRect (&rect
, pt
))
381 *flags
= TCHT_ONITEM
;
391 TAB_HitTest (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
393 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
394 LPTCHITTESTINFO lptest
=(LPTCHITTESTINFO
) lParam
;
396 return TAB_InternalHitTest (hwnd
, infoPtr
,lptest
->pt
,&lptest
->flags
);
401 TAB_LButtonDown (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
403 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
407 if (infoPtr
->hwndToolTip
)
408 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
409 WM_LBUTTONDOWN
, wParam
, lParam
);
411 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_FOCUSONBUTTONDOWN
) {
415 if (infoPtr
->hwndToolTip
)
416 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
417 WM_LBUTTONDOWN
, wParam
, lParam
);
419 pt
.x
= (INT
)LOWORD(lParam
);
420 pt
.y
= (INT
)HIWORD(lParam
);
422 newItem
=TAB_InternalHitTest (hwnd
, infoPtr
,pt
,&dummy
);
424 TRACE("On Tab, item %d\n", newItem
);
426 if ( (newItem
!=-1) &&
427 (infoPtr
->iSelected
!= newItem
) )
429 if (TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
)!=TRUE
)
431 infoPtr
->iSelected
= newItem
;
432 infoPtr
->uFocus
= newItem
;
433 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
435 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
437 TAB_InvalidateTabArea(hwnd
, infoPtr
);
444 TAB_LButtonUp (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
446 TAB_SendSimpleNotify(hwnd
, NM_CLICK
);
452 TAB_RButtonDown (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
454 TAB_SendSimpleNotify(hwnd
, NM_RCLICK
);
459 TAB_MouseMove (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
461 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
463 if (infoPtr
->hwndToolTip
)
464 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
465 WM_LBUTTONDOWN
, wParam
, lParam
);
469 /******************************************************************************
472 * Calculates the tab control's display area given the windows rectangle or
473 * the window rectangle given the requested display rectangle.
475 static LRESULT
TAB_AdjustRect(
480 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
485 * Go from display rectangle
489 * Add the height of the tabs.
491 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
492 prc
->bottom
+= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
494 prc
->top
-= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
497 * Inflate the rectangle for the padding
499 InflateRect(prc
, DISPLAY_AREA_PADDINGX
, DISPLAY_AREA_PADDINGY
);
502 * Inflate for the border
504 InflateRect(prc
, CONTROL_BORDER_SIZEX
, CONTROL_BORDER_SIZEX
);
509 * Go from window rectangle.
513 * Deflate the rectangle for the border
515 InflateRect(prc
, -CONTROL_BORDER_SIZEX
, -CONTROL_BORDER_SIZEX
);
518 * Deflate the rectangle for the padding
520 InflateRect(prc
, -DISPLAY_AREA_PADDINGX
, -DISPLAY_AREA_PADDINGY
);
523 * Remove the height of the tabs.
525 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
526 prc
->bottom
-= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
528 prc
->top
+= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
535 /******************************************************************************
538 * This method will handle the notification from the scroll control and
539 * perform the scrolling operation on the tab control.
541 static LRESULT
TAB_OnHScroll(
547 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
549 if(nScrollCode
== SB_THUMBPOSITION
&& nPos
!= infoPtr
->leftmostVisible
)
551 if(nPos
< infoPtr
->leftmostVisible
)
552 infoPtr
->leftmostVisible
--;
554 infoPtr
->leftmostVisible
++;
556 TAB_InvalidateTabArea(hwnd
, infoPtr
);
557 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
558 MAKELONG(infoPtr
->leftmostVisible
, 0));
564 /******************************************************************************
567 * This method will check the current scrolling state and make sure the
568 * scrolling control is displayed (or not).
570 static void TAB_SetupScrolling(
573 const RECT
* clientRect
)
576 if (infoPtr
->needsScrolling
)
582 * Calculate the position of the scroll control.
584 controlPos
.right
= clientRect
->right
;
585 controlPos
.left
= controlPos
.right
- 2*GetSystemMetrics(SM_CXHSCROLL
);
587 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
589 controlPos
.top
= clientRect
->bottom
- infoPtr
->tabHeight
;
590 controlPos
.bottom
= controlPos
.top
+ GetSystemMetrics(SM_CYHSCROLL
);
594 controlPos
.bottom
= clientRect
->top
+ infoPtr
->tabHeight
;
595 controlPos
.top
= controlPos
.bottom
- GetSystemMetrics(SM_CYHSCROLL
);
599 * If we don't have a scroll control yet, we want to create one.
600 * If we have one, we want to make sure it's positioned right.
602 if (infoPtr
->hwndUpDown
==0)
604 infoPtr
->hwndUpDown
= CreateWindowA("msctls_updown32",
606 WS_VISIBLE
| WS_CHILD
| UDS_HORZ
,
607 controlPos
.left
, controlPos
.top
,
608 controlPos
.right
- controlPos
.left
,
609 controlPos
.bottom
- controlPos
.top
,
617 SetWindowPos(infoPtr
->hwndUpDown
,
619 controlPos
.left
, controlPos
.top
,
620 controlPos
.right
- controlPos
.left
,
621 controlPos
.bottom
- controlPos
.top
,
622 SWP_SHOWWINDOW
| SWP_NOZORDER
);
625 /* Now calculate upper limit of the updown control range.
626 * We do this by calculating how many tabs will be offscreen when the
627 * last tab is visible.
629 if(infoPtr
->uNumItem
)
631 vsize
= clientRect
->right
- (controlPos
.right
- controlPos
.left
+ 1);
632 maxRange
= infoPtr
->uNumItem
;
633 tabwidth
= infoPtr
->items
[maxRange
-1].rect
.right
;
635 for(; maxRange
> 0; maxRange
--)
637 if(tabwidth
- infoPtr
->items
[maxRange
- 1].rect
.left
> vsize
)
641 if(maxRange
== infoPtr
->uNumItem
)
648 * If we once had a scroll control... hide it.
650 if (infoPtr
->hwndUpDown
!=0)
652 ShowWindow(infoPtr
->hwndUpDown
, SW_HIDE
);
655 if (infoPtr
->hwndUpDown
)
656 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETRANGE32
, 0, maxRange
);
659 /******************************************************************************
662 * This method will calculate the position rectangles of all the items in the
663 * control. The rectangle calculated starts at 0 for the first item in the
664 * list and ignores scrolling and selection.
665 * It also uses the current font to determine the height of the tab row and
666 * it checks if all the tabs fit in the client area of the window. If they
667 * dont, a scrolling control is added.
669 static void TAB_SetItemBounds (HWND hwnd
)
671 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
672 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
673 TEXTMETRICA fontMetrics
;
677 HFONT hFont
, hOldFont
;
683 * We need to get text information so we need a DC and we need to select
688 hFont
= infoPtr
->hFont
? infoPtr
->hFont
: GetStockObject (SYSTEM_FONT
);
689 hOldFont
= SelectObject (hdc
, hFont
);
692 * We will base the rectangle calculations on the client rectangle
695 GetClientRect(hwnd
, &clientRect
);
698 * The leftmost item will be "0" aligned
703 if ( !(lStyle
& TCS_FIXEDWIDTH
) && !((lStyle
& TCS_OWNERDRAWFIXED
) && infoPtr
->fSizeSet
) )
709 * Use the current font to determine the height of a tab.
711 GetTextMetricsA(hdc
, &fontMetrics
);
714 * Get the icon height
717 ImageList_GetIconSize(infoPtr
->himl
, 0, &icon_height
);
720 * Take the highest between font or icon
722 if (fontMetrics
.tmHeight
> icon_height
)
723 item_height
= fontMetrics
.tmHeight
;
725 item_height
= icon_height
;
728 * Make sure there is enough space for the letters + icon + growing the
729 * selected item + extra space for the selected item.
731 infoPtr
->tabHeight
= item_height
+ 2*VERTICAL_ITEM_PADDING
+
735 for (curItem
= 0; curItem
< infoPtr
->uNumItem
; curItem
++)
738 * Set the leftmost position of the tab.
740 infoPtr
->items
[curItem
].rect
.left
= curItemLeftPos
;
742 if ( (lStyle
& TCS_FIXEDWIDTH
) || ((lStyle
& TCS_OWNERDRAWFIXED
) && infoPtr
->fSizeSet
))
744 infoPtr
->items
[curItem
].rect
.right
= infoPtr
->items
[curItem
].rect
.left
+
746 2*HORIZONTAL_ITEM_PADDING
;
754 * Calculate how wide the tab is depending on the text it contains
756 GetTextExtentPoint32A(hdc
, infoPtr
->items
[curItem
].pszText
,
757 lstrlenA(infoPtr
->items
[curItem
].pszText
), &size
);
764 ImageList_GetIconSize(infoPtr
->himl
, &icon_width
, 0);
768 infoPtr
->items
[curItem
].rect
.right
= infoPtr
->items
[curItem
].rect
.left
+
769 size
.cx
+ icon_width
+
770 num
*HORIZONTAL_ITEM_PADDING
;
774 * Check if this is a multiline tab control and if so
775 * check to see if we should wrap the tabs
777 * Because we are going to arange all these tabs evenly
778 * really we are basically just counting rows at this point
782 if ((lStyle
& TCS_MULTILINE
)&&
783 (infoPtr
->items
[curItem
].rect
.right
> clientRect
.right
))
785 infoPtr
->items
[curItem
].rect
.right
-=
786 infoPtr
->items
[curItem
].rect
.left
;
787 infoPtr
->items
[curItem
].rect
.left
= 0;
791 infoPtr
->items
[curItem
].rect
.bottom
= 0;
792 infoPtr
->items
[curItem
].rect
.top
= curItemRowCount
;
794 TRACE("TextSize: %i\n ", size
.cx
);
795 TRACE("Rect: T %i, L %i, B %i, R %i\n",
796 infoPtr
->items
[curItem
].rect
.top
,
797 infoPtr
->items
[curItem
].rect
.left
,
798 infoPtr
->items
[curItem
].rect
.bottom
,
799 infoPtr
->items
[curItem
].rect
.right
);
802 * The leftmost position of the next item is the rightmost position
805 if (lStyle
& TCS_BUTTONS
)
806 curItemLeftPos
= infoPtr
->items
[curItem
].rect
.right
+ BUTTON_SPACINGX
;
808 curItemLeftPos
= infoPtr
->items
[curItem
].rect
.right
;
811 if (!(lStyle
& TCS_MULTILINE
))
814 * Check if we need a scrolling control.
816 infoPtr
->needsScrolling
= (curItemLeftPos
+ (2*SELECTED_TAB_OFFSET
) >
819 TAB_SetupScrolling(hwnd
, infoPtr
, &clientRect
);
823 * Set the number of rows
826 infoPtr
->uNumRows
= curItemRowCount
;
828 if ((lStyle
& TCS_MULTILINE
)&&(infoPtr
->uNumItem
> 0))
830 INT widthDiff
,remainder
;
831 INT tabPerRow
,remTab
;
833 INT iIndexStart
=0,iIndexEnd
=0, iCount
=0;
836 * Ok Microsoft trys to even out the rows. place the same
837 * number of tabs in each row. So lets give that a shot
841 tabPerRow
= infoPtr
->uNumItem
/ (infoPtr
->uNumRows
+ 1);
842 remTab
= infoPtr
->uNumItem
% (infoPtr
->uNumRows
+ 1);
844 for (iItm
=0,iRow
=0,iCount
=0,curItemLeftPos
=0;
845 iItm
<infoPtr
->uNumItem
;
848 if (iCount
>= ((iRow
<remTab
)?tabPerRow
+1:tabPerRow
))
855 * normalize the current rect
857 infoPtr
->items
[iItm
].rect
.right
-=
858 infoPtr
->items
[iItm
].rect
.left
;
859 infoPtr
->items
[iItm
].rect
.left
= 0;
861 infoPtr
->items
[iItm
].rect
.top
= iRow
;
862 infoPtr
->items
[iItm
].rect
.left
+=curItemLeftPos
;
863 infoPtr
->items
[iItm
].rect
.right
+=curItemLeftPos
;
864 if (lStyle
& TCS_BUTTONS
)
865 curItemLeftPos
= infoPtr
->items
[iItm
].rect
.right
+
868 curItemLeftPos
= infoPtr
->items
[iItm
].rect
.right
;
876 while(iIndexStart
< infoPtr
->uNumItem
)
879 * find the indexs of the row
881 for (iIndexEnd
=iIndexStart
;
882 (iIndexEnd
< infoPtr
->uNumItem
) &&
883 (infoPtr
->items
[iIndexEnd
].rect
.top
==
884 infoPtr
->items
[iIndexStart
].rect
.top
) ;
886 /* intentionaly blank */;
889 * we need to justify these tabs so they fill the whole given
893 widthDiff
= clientRect
.right
- (2*SELECTED_TAB_OFFSET
) -
894 infoPtr
->items
[iIndexEnd
-1].rect
.right
;
896 iCount
= iIndexEnd
-iIndexStart
;
901 remainder
= widthDiff
% iCount
;
902 widthDiff
= widthDiff
/ iCount
;
903 for (iIndex
=iIndexStart
,iCount
=0; iIndex
< iIndexEnd
;
906 infoPtr
->items
[iIndex
].rect
.left
+=iCount
*widthDiff
;
907 infoPtr
->items
[iIndex
].rect
.right
+=(iCount
+1)*widthDiff
;
909 infoPtr
->items
[iIndex
-1].rect
.right
+= remainder
;
912 iIndexStart
=iIndexEnd
;
917 TAB_EnsureSelectionVisible(hwnd
,infoPtr
);
921 SelectObject (hdc
, hOldFont
);
922 ReleaseDC (hwnd
, hdc
);
925 /******************************************************************************
928 * This method is used to draw a single tab into the tab control.
930 static void TAB_DrawItem(
935 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
936 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
943 * Get the rectangle for the item.
945 isVisible
= TAB_InternalGetItemRect(hwnd
,
953 HBRUSH hbr
= CreateSolidBrush (GetSysColor(COLOR_BTNFACE
));
954 HPEN hwPen
= GetSysColorPen (COLOR_3DHILIGHT
);
955 HPEN hbPen
= GetSysColorPen (COLOR_BTNSHADOW
);
956 HPEN hsdPen
= GetSysColorPen (COLOR_BTNTEXT
);
957 HPEN hfocusPen
= CreatePen(PS_DOT
, 1, GetSysColor(COLOR_BTNTEXT
));
961 BOOL deleteBrush
= TRUE
;
963 if (lStyle
& TCS_BUTTONS
)
966 * Get item rectangle.
970 holdPen
= SelectObject (hdc
, hwPen
);
972 if (iItem
== infoPtr
->iSelected
)
977 if (!((lStyle
& TCS_OWNERDRAWFIXED
) && infoPtr
->fSizeSet
))
979 COLORREF bk
= GetSysColor(COLOR_3DHILIGHT
);
981 hbr
= GetSysColorBrush(COLOR_SCROLLBAR
);
982 SetTextColor(hdc
, GetSysColor(COLOR_3DFACE
));
985 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
986 * we better use 0x55aa bitmap brush to make scrollbar's background
987 * look different from the window background.
989 if (bk
== GetSysColor(COLOR_WINDOW
))
990 hbr
= CACHE_GetPattern55AABrush();
996 * Erase the background.
998 FillRect(hdc
, &r
, hbr
);
1002 * The rectangles calculated exclude the right and bottom
1003 * borders of the rectangle. To simply the following code, those
1004 * borders are shaved-off beforehand.
1010 MoveToEx (hdc
, r
.left
, r
.bottom
, NULL
);
1011 LineTo (hdc
, r
.right
, r
.bottom
);
1012 LineTo (hdc
, r
.right
, r
.top
);
1015 SelectObject(hdc
, hbPen
);
1016 LineTo (hdc
, r
.left
, r
.top
);
1017 LineTo (hdc
, r
.left
, r
.bottom
);
1022 * Erase the background.
1024 FillRect(hdc
, &r
, hbr
);
1027 MoveToEx (hdc
, r
.left
, r
.bottom
, NULL
);
1028 LineTo (hdc
, r
.left
, r
.top
);
1029 LineTo (hdc
, r
.right
, r
.top
);
1032 SelectObject(hdc
, hbPen
);
1033 LineTo (hdc
, r
.right
, r
.bottom
);
1034 LineTo (hdc
, r
.left
, r
.bottom
);
1043 hbr
= CreateSolidBrush(GetSysColor(COLOR_BTNFACE
));
1046 * We draw a rectangle of different sizes depending on the selection
1049 if (iItem
== infoPtr
->iSelected
)
1055 * Erase the background.
1056 * This is necessary when drawing the selected item since it is larger
1057 * than the others, it might overlap with stuff already drawn by the
1060 FillRect(hdc
, &r
, hbr
);
1064 * The rectangles calculated exclude the right and bottom
1065 * borders of the rectangle. To simply the following code, those
1066 * borders are shaved-off beforehand.
1071 holdPen
= SelectObject (hdc
, hwPen
);
1073 if (lStyle
& TCS_BOTTOM
)
1076 MoveToEx (hdc
, r
.left
, r
.top
, NULL
);
1077 LineTo (hdc
, r
.left
, r
.bottom
- ROUND_CORNER_SIZE
);
1078 LineTo (hdc
, r
.left
+ ROUND_CORNER_SIZE
, r
.bottom
);
1081 SelectObject(hdc
, hbPen
);
1082 LineTo (hdc
, r
.right
- ROUND_CORNER_SIZE
, r
.bottom
);
1083 LineTo (hdc
, r
.right
, r
.bottom
- ROUND_CORNER_SIZE
);
1084 LineTo (hdc
, r
.right
, r
.top
);
1089 MoveToEx (hdc
, r
.left
, r
.bottom
, NULL
);
1090 LineTo (hdc
, r
.left
, r
.top
+ ROUND_CORNER_SIZE
);
1091 LineTo (hdc
, r
.left
+ ROUND_CORNER_SIZE
, r
.top
);
1092 LineTo (hdc
, r
.right
- ROUND_CORNER_SIZE
, r
.top
);
1095 SelectObject(hdc
, hbPen
);
1096 LineTo (hdc
, r
.right
, r
.top
+ ROUND_CORNER_SIZE
);
1097 LineTo (hdc
, r
.right
, r
.bottom
);
1104 SelectObject(hdc
, hsdPen
);
1106 oldBkMode
= SetBkMode(hdc
, TRANSPARENT
);
1107 SetTextColor (hdc
, COLOR_BTNTEXT
);
1110 * Deflate the rectangle to acount for the padding
1112 InflateRect(&r
, -HORIZONTAL_ITEM_PADDING
, -VERTICAL_ITEM_PADDING
);
1115 * if owner draw, tell the owner to draw
1117 if ( (lStyle
& TCS_OWNERDRAWFIXED
) && GetParent(hwnd
) )
1124 * get the control id
1126 pwndPtr
= WIN_FindWndPtr( hwnd
);
1127 id
= pwndPtr
->wIDmenu
;
1128 WIN_ReleaseWndPtr(pwndPtr
);
1131 * put together the DRAWITEMSTRUCT
1133 dis
.CtlType
= ODT_TAB
;
1136 dis
.itemAction
= ODA_DRAWENTIRE
;
1137 if ( iItem
== infoPtr
->iSelected
)
1138 dis
.itemState
= ODS_SELECTED
;
1141 dis
.hwndItem
= hwnd
; /* */
1143 dis
.rcItem
= r
; /* */
1144 dis
.itemData
= infoPtr
->items
[iItem
].lParam
;
1147 * send the draw message
1149 SendMessageA( GetParent(hwnd
), WM_DRAWITEM
, (WPARAM
)id
, (LPARAM
)&dis
);
1154 * If not owner draw, then do the drawing ourselves.
1158 if (infoPtr
->himl
&& (infoPtr
->items
[iItem
].mask
& TCIF_IMAGE
) )
1160 ImageList_Draw (infoPtr
->himl
, infoPtr
->items
[iItem
].iImage
, hdc
,
1161 r
.left
, r
.top
+1, ILD_NORMAL
);
1162 ImageList_GetIconSize (infoPtr
->himl
, &cx
, &cy
);
1163 r
.left
+=(cx
+ HORIZONTAL_ITEM_PADDING
);
1169 if (lStyle
& TCS_RIGHTJUSTIFY
)
1171 infoPtr
->items
[iItem
].pszText
,
1172 lstrlenA(infoPtr
->items
[iItem
].pszText
),
1174 DT_CENTER
|DT_SINGLELINE
|DT_VCENTER
);
1177 infoPtr
->items
[iItem
].pszText
,
1178 lstrlenA(infoPtr
->items
[iItem
].pszText
),
1180 DT_LEFT
|DT_SINGLELINE
|DT_VCENTER
);
1184 * Draw the focus rectangle
1186 if (((lStyle
& TCS_FOCUSNEVER
) == 0) &&
1187 (GetFocus() == hwnd
) &&
1188 (iItem
== infoPtr
->uFocus
) )
1190 InflateRect(&r
, FOCUS_RECT_HOFFSET
, FOCUS_RECT_VOFFSET
);
1192 SelectObject(hdc
, hfocusPen
);
1194 MoveToEx (hdc
, r
.left
, r
.top
, NULL
);
1195 LineTo (hdc
, r
.right
-1, r
.top
);
1196 LineTo (hdc
, r
.right
-1, r
.bottom
-1);
1197 LineTo (hdc
, r
.left
, r
.bottom
-1);
1198 LineTo (hdc
, r
.left
, r
.top
);
1204 SetBkMode(hdc
, oldBkMode
);
1205 SelectObject(hdc
, holdPen
);
1206 DeleteObject(hfocusPen
);
1207 if (deleteBrush
) DeleteObject(hbr
);
1211 /******************************************************************************
1214 * This method is used to draw the raised border around the tab control
1217 static void TAB_DrawBorder (HWND hwnd
, HDC hdc
)
1219 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1221 HPEN hwPen
= GetSysColorPen (COLOR_3DHILIGHT
);
1222 HPEN hbPen
= GetSysColorPen (COLOR_3DDKSHADOW
);
1223 HPEN hShade
= GetSysColorPen (COLOR_BTNSHADOW
);
1226 GetClientRect (hwnd
, &rect
);
1229 * Adjust for the style
1231 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
1233 rect
.bottom
-= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
1237 rect
.top
+= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
1241 * Shave-off the right and bottom margins (exluded in the
1248 htmPen
= SelectObject (hdc
, hwPen
);
1250 MoveToEx (hdc
, rect
.left
, rect
.bottom
, NULL
);
1251 LineTo (hdc
, rect
.left
, rect
.top
);
1252 LineTo (hdc
, rect
.right
, rect
.top
);
1255 SelectObject (hdc
, hbPen
);
1256 LineTo (hdc
, rect
.right
, rect
.bottom
);
1257 LineTo (hdc
, rect
.left
, rect
.bottom
);
1260 SelectObject (hdc
, hShade
);
1261 MoveToEx (hdc
, rect
.right
-1, rect
.top
, NULL
);
1262 LineTo (hdc
, rect
.right
-1, rect
.bottom
-1);
1263 LineTo (hdc
, rect
.left
, rect
.bottom
-1);
1265 SelectObject(hdc
, htmPen
);
1268 /******************************************************************************
1271 * This method repaints the tab control..
1273 static void TAB_Refresh (HWND hwnd
, HDC hdc
)
1275 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1279 if (!infoPtr
->DoRedraw
)
1282 hOldFont
= SelectObject (hdc
, infoPtr
->hFont
);
1284 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BUTTONS
)
1286 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
1288 TAB_DrawItem (hwnd
, hdc
, i
);
1294 * Draw all the non selected item first.
1296 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
1298 if (i
!= infoPtr
->iSelected
)
1299 TAB_DrawItem (hwnd
, hdc
, i
);
1303 * Now, draw the border, draw it before the selected item
1304 * since the selected item overwrites part of the border.
1306 TAB_DrawBorder (hwnd
, hdc
);
1309 * Then, draw the selected item
1311 TAB_DrawItem (hwnd
, hdc
, infoPtr
->iSelected
);
1314 * If we haven't set the current focus yet, set it now.
1315 * Only happens when we first paint the tab controls.
1317 if (infoPtr
->uFocus
== -1)
1318 TAB_SetCurFocus(hwnd
, infoPtr
->iSelected
);
1321 SelectObject (hdc
, hOldFont
);
1325 TAB_SetRedraw (HWND hwnd
, WPARAM wParam
)
1327 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1329 infoPtr
->DoRedraw
=(BOOL
) wParam
;
1333 static LRESULT
TAB_EraseBackground(
1340 HBRUSH brush
= CreateSolidBrush(GetSysColor(COLOR_BTNFACE
));
1342 hdc
= givenDC
? givenDC
: GetDC(hwnd
);
1344 GetClientRect(hwnd
, &clientRect
);
1346 FillRect(hdc
, &clientRect
, brush
);
1349 ReleaseDC(hwnd
, hdc
);
1351 DeleteObject(brush
);
1356 /******************************************************************************
1357 * TAB_EnsureSelectionVisible
1359 * This method will make sure that the current selection is completely
1360 * visible by scrolling until it is.
1362 static void TAB_EnsureSelectionVisible(
1366 INT iSelected
= infoPtr
->iSelected
;
1369 * set the items row to the bottommost row or topmost row depending on
1373 if (infoPtr
->uNumRows
> 0)
1375 INT newselected
=infoPtr
->items
[iSelected
].rect
.top
;
1377 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1379 if (lStyle
& TCS_BOTTOM
)
1382 iTargetRow
= infoPtr
->uNumRows
;
1384 if (newselected
!= iTargetRow
)
1387 for (i
=0; i
< infoPtr
->uNumItem
; i
++)
1388 if (infoPtr
->items
[i
].rect
.top
== newselected
)
1389 infoPtr
->items
[i
].rect
.top
= iTargetRow
;
1390 else if (lStyle
&TCS_BOTTOM
)
1392 if (infoPtr
->items
[i
].rect
.top
< newselected
)
1393 infoPtr
->items
[i
].rect
.top
+=1;
1397 if (infoPtr
->items
[i
].rect
.top
> newselected
)
1398 infoPtr
->items
[i
].rect
.top
-=1;
1404 * Do the trivial cases first.
1406 if ( (!infoPtr
->needsScrolling
) ||
1407 (infoPtr
->hwndUpDown
==0) )
1410 if (infoPtr
->leftmostVisible
>= iSelected
)
1412 infoPtr
->leftmostVisible
= iSelected
;
1419 * Calculate the part of the client area that is visible.
1421 GetClientRect(hwnd
, &r
);
1424 GetClientRect(infoPtr
->hwndUpDown
, &r
);
1427 if ((infoPtr
->items
[iSelected
].rect
.right
-
1428 infoPtr
->items
[iSelected
].rect
.left
) >= width
)
1430 /* Special case: width of selected item is greater than visible
1433 infoPtr
->leftmostVisible
= iSelected
;
1437 for (i
= infoPtr
->leftmostVisible
; i
< infoPtr
->uNumItem
; i
++)
1439 if ((infoPtr
->items
[iSelected
].rect
.right
-
1440 infoPtr
->items
[i
].rect
.left
) < width
)
1443 infoPtr
->leftmostVisible
= i
;
1447 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
1448 MAKELONG(infoPtr
->leftmostVisible
, 0));
1451 /******************************************************************************
1452 * TAB_InvalidateTabArea
1454 * This method will invalidate the portion of the control that contains the
1455 * tabs. It is called when the state of the control changes and needs
1458 static void TAB_InvalidateTabArea(
1464 GetClientRect(hwnd
, &clientRect
);
1466 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
1468 clientRect
.top
= clientRect
.bottom
- (infoPtr
->tabHeight
*
1469 (infoPtr
->uNumRows
+ 1) + 3);
1473 clientRect
.bottom
= clientRect
.top
+ (infoPtr
->tabHeight
*
1474 (infoPtr
->uNumRows
+ 1) + 1);
1477 InvalidateRect(hwnd
, &clientRect
, TRUE
);
1481 TAB_Paint (HWND hwnd
, WPARAM wParam
)
1486 hdc
= wParam
== 0 ? BeginPaint (hwnd
, &ps
) : (HDC
)wParam
;
1487 TAB_Refresh (hwnd
, hdc
);
1490 EndPaint (hwnd
, &ps
);
1496 TAB_InsertItem (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1498 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1503 GetClientRect (hwnd
, &rect
);
1504 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd
,
1505 rect
.top
, rect
.left
, rect
.bottom
, rect
.right
);
1507 pti
= (TCITEMA
*)lParam
;
1508 iItem
= (INT
)wParam
;
1510 if (iItem
< 0) return -1;
1511 if (iItem
> infoPtr
->uNumItem
)
1512 iItem
= infoPtr
->uNumItem
;
1514 if (infoPtr
->uNumItem
== 0) {
1515 infoPtr
->items
= COMCTL32_Alloc (sizeof (TAB_ITEM
));
1516 infoPtr
->uNumItem
++;
1517 infoPtr
->iSelected
= 0;
1520 TAB_ITEM
*oldItems
= infoPtr
->items
;
1522 infoPtr
->uNumItem
++;
1523 infoPtr
->items
= COMCTL32_Alloc (sizeof (TAB_ITEM
) * infoPtr
->uNumItem
);
1525 /* pre insert copy */
1527 memcpy (&infoPtr
->items
[0], &oldItems
[0],
1528 iItem
* sizeof(TAB_ITEM
));
1531 /* post insert copy */
1532 if (iItem
< infoPtr
->uNumItem
- 1) {
1533 memcpy (&infoPtr
->items
[iItem
+1], &oldItems
[iItem
],
1534 (infoPtr
->uNumItem
- iItem
- 1) * sizeof(TAB_ITEM
));
1538 if (iItem
<= infoPtr
->iSelected
)
1539 infoPtr
->iSelected
++;
1541 COMCTL32_Free (oldItems
);
1544 infoPtr
->items
[iItem
].mask
= pti
->mask
;
1545 if (pti
->mask
& TCIF_TEXT
) {
1546 len
= lstrlenA (pti
->pszText
);
1547 infoPtr
->items
[iItem
].pszText
= COMCTL32_Alloc (len
+1);
1548 lstrcpyA (infoPtr
->items
[iItem
].pszText
, pti
->pszText
);
1549 infoPtr
->items
[iItem
].cchTextMax
= pti
->cchTextMax
;
1552 if (pti
->mask
& TCIF_IMAGE
)
1553 infoPtr
->items
[iItem
].iImage
= pti
->iImage
;
1555 if (pti
->mask
& TCIF_PARAM
)
1556 infoPtr
->items
[iItem
].lParam
= pti
->lParam
;
1558 TAB_InvalidateTabArea(hwnd
, infoPtr
);
1560 TRACE("[%04x]: added item %d '%s'\n",
1561 hwnd
, iItem
, infoPtr
->items
[iItem
].pszText
);
1563 TAB_SetItemBounds(hwnd
);
1568 TAB_SetItemSize (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1570 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1571 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1574 if ((lStyle
& TCS_FIXEDWIDTH
) || (lStyle
& TCS_OWNERDRAWFIXED
))
1576 lResult
= MAKELONG(infoPtr
->tabWidth
, infoPtr
->tabHeight
);
1577 infoPtr
->tabWidth
= (INT
)LOWORD(lParam
);
1578 infoPtr
->tabHeight
= (INT
)HIWORD(lParam
);
1580 infoPtr
->fSizeSet
= TRUE
;
1586 TAB_SetItemA (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1588 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1594 tabItem
=(LPTCITEMA
) lParam
;
1595 TRACE("%d %p\n",iItem
, tabItem
);
1596 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
)) return FALSE
;
1598 wineItem
=& infoPtr
->items
[iItem
];
1600 if (tabItem
->mask
& TCIF_IMAGE
)
1601 wineItem
->iImage
=tabItem
->iImage
;
1603 if (tabItem
->mask
& TCIF_PARAM
)
1604 wineItem
->lParam
=tabItem
->lParam
;
1606 if (tabItem
->mask
& TCIF_RTLREADING
)
1607 FIXME("TCIF_RTLREADING\n");
1609 if (tabItem
->mask
& TCIF_STATE
)
1610 wineItem
->dwState
=tabItem
->dwState
;
1612 if (tabItem
->mask
& TCIF_TEXT
) {
1613 len
=lstrlenA (tabItem
->pszText
);
1614 if (len
>wineItem
->cchTextMax
)
1615 wineItem
->pszText
= COMCTL32_ReAlloc (wineItem
->pszText
, len
+1);
1616 lstrcpyA (wineItem
->pszText
, tabItem
->pszText
);
1620 * Update and repaint tabs.
1622 TAB_SetItemBounds(hwnd
);
1623 TAB_InvalidateTabArea(hwnd
,infoPtr
);
1629 TAB_GetItemCount (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1631 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1633 return infoPtr
->uNumItem
;
1638 TAB_GetItemA (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1640 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1646 tabItem
=(LPTCITEMA
) lParam
;
1648 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
)) return FALSE
;
1650 wineItem
=& infoPtr
->items
[iItem
];
1652 if (tabItem
->mask
& TCIF_IMAGE
)
1653 tabItem
->iImage
=wineItem
->iImage
;
1655 if (tabItem
->mask
& TCIF_PARAM
)
1656 tabItem
->lParam
=wineItem
->lParam
;
1658 if (tabItem
->mask
& TCIF_RTLREADING
)
1659 FIXME("TCIF_RTLREADING\n");
1661 if (tabItem
->mask
& TCIF_STATE
)
1662 tabItem
->dwState
=wineItem
->dwState
;
1664 if (tabItem
->mask
& TCIF_TEXT
)
1665 lstrcpynA (tabItem
->pszText
, wineItem
->pszText
, tabItem
->cchTextMax
);
1671 TAB_DeleteItem (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1673 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1674 INT iItem
= (INT
) wParam
;
1675 BOOL bResult
= FALSE
;
1677 if ((iItem
>= 0) && (iItem
< infoPtr
->uNumItem
))
1679 TAB_ITEM
*oldItems
= infoPtr
->items
;
1681 infoPtr
->uNumItem
--;
1682 infoPtr
->items
= COMCTL32_Alloc(sizeof (TAB_ITEM
) * infoPtr
->uNumItem
);
1685 memcpy(&infoPtr
->items
[0], &oldItems
[0], iItem
* sizeof(TAB_ITEM
));
1687 if (iItem
< infoPtr
->uNumItem
)
1688 memcpy(&infoPtr
->items
[iItem
], &oldItems
[iItem
+ 1],
1689 (infoPtr
->uNumItem
- iItem
) * sizeof(TAB_ITEM
));
1691 COMCTL32_Free (oldItems
);
1694 * Readjust the selected index.
1696 if ((iItem
== infoPtr
->iSelected
) && (iItem
> 0))
1697 infoPtr
->iSelected
--;
1699 if (iItem
< infoPtr
->iSelected
)
1700 infoPtr
->iSelected
--;
1702 if (infoPtr
->uNumItem
== 0)
1703 infoPtr
->iSelected
= -1;
1706 * Reposition and repaint tabs.
1708 TAB_SetItemBounds(hwnd
);
1709 TAB_InvalidateTabArea(hwnd
,infoPtr
);
1718 TAB_DeleteAllItems (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1720 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1722 COMCTL32_Free (infoPtr
->items
);
1723 infoPtr
->uNumItem
= 0;
1724 infoPtr
->iSelected
= -1;
1726 TAB_SetItemBounds(hwnd
);
1727 TAB_InvalidateTabArea(hwnd
,infoPtr
);
1733 TAB_GetFont (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1735 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1738 return (LRESULT
)infoPtr
->hFont
;
1742 TAB_SetFont (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1745 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1747 TRACE("%x %lx\n",wParam
, lParam
);
1749 infoPtr
->hFont
= (HFONT
)wParam
;
1751 TAB_SetItemBounds(hwnd
);
1753 TAB_InvalidateTabArea(hwnd
, infoPtr
);
1760 TAB_GetImageList (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1762 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1765 return (LRESULT
)infoPtr
->himl
;
1769 TAB_SetImageList (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1771 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1772 HIMAGELIST himlPrev
;
1775 himlPrev
= infoPtr
->himl
;
1776 infoPtr
->himl
= (HIMAGELIST
)lParam
;
1777 return (LRESULT
)himlPrev
;
1782 TAB_Size (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1785 /* I'm not really sure what the following code was meant to do.
1786 This is what it is doing:
1787 When WM_SIZE is sent with SIZE_RESTORED, the control
1788 gets positioned in the top left corner.
1792 UINT uPosFlags,cx,cy;
1796 parent = GetParent (hwnd);
1797 GetClientRect(parent, &parent_rect);
1800 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
1801 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
1803 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
1804 cx, cy, uPosFlags | SWP_NOZORDER);
1806 FIXME (tab,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
1810 * Recompute the size/position of the tabs.
1812 TAB_SetItemBounds (hwnd
);
1815 * Force a repaint of the control.
1817 InvalidateRect(hwnd
, NULL
, TRUE
);
1824 TAB_Create (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1827 TEXTMETRICA fontMetrics
;
1832 infoPtr
= (TAB_INFO
*)COMCTL32_Alloc (sizeof(TAB_INFO
));
1834 SetWindowLongA(hwnd
, 0, (DWORD
)infoPtr
);
1836 infoPtr
->uNumItem
= 0;
1837 infoPtr
->uNumRows
= 0;
1840 infoPtr
->hcurArrow
= LoadCursorA (0, IDC_ARROWA
);
1841 infoPtr
->iSelected
= -1;
1842 infoPtr
->uFocus
= -1;
1843 infoPtr
->hwndToolTip
= 0;
1844 infoPtr
->DoRedraw
= TRUE
;
1845 infoPtr
->needsScrolling
= FALSE
;
1846 infoPtr
->hwndUpDown
= 0;
1847 infoPtr
->leftmostVisible
= 0;
1848 infoPtr
->fSizeSet
= FALSE
;
1850 TRACE("Created tab control, hwnd [%04x]\n", hwnd
);
1852 /* The tab control always has the WS_CLIPSIBLINGS style. Even
1853 if you don't specify in CreateWindow. This is necesary in
1854 order for paint to work correctly. This follows windows behaviour. */
1855 dwStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1856 SetWindowLongA(hwnd
, GWL_STYLE
, dwStyle
|WS_CLIPSIBLINGS
);
1858 if (dwStyle
& TCS_TOOLTIPS
) {
1859 /* Create tooltip control */
1860 infoPtr
->hwndToolTip
=
1861 CreateWindowExA (0, TOOLTIPS_CLASSA
, NULL
, 0,
1862 CW_USEDEFAULT
, CW_USEDEFAULT
,
1863 CW_USEDEFAULT
, CW_USEDEFAULT
,
1866 /* Send NM_TOOLTIPSCREATED notification */
1867 if (infoPtr
->hwndToolTip
) {
1868 NMTOOLTIPSCREATED nmttc
;
1870 nmttc
.hdr
.hwndFrom
= hwnd
;
1871 nmttc
.hdr
.idFrom
= GetWindowLongA(hwnd
, GWL_ID
);
1872 nmttc
.hdr
.code
= NM_TOOLTIPSCREATED
;
1873 nmttc
.hwndToolTips
= infoPtr
->hwndToolTip
;
1875 SendMessageA (GetParent (hwnd
), WM_NOTIFY
,
1876 (WPARAM
)GetWindowLongA(hwnd
, GWL_ID
), (LPARAM
)&nmttc
);
1881 * We need to get text information so we need a DC and we need to select
1885 hOldFont
= SelectObject (hdc
, GetStockObject (SYSTEM_FONT
));
1888 * Use the system font to determine the initial height of a tab.
1890 GetTextMetricsA(hdc
, &fontMetrics
);
1893 * Make sure there is enough space for the letters + growing the
1894 * selected item + extra space for the selected item.
1896 infoPtr
->tabHeight
= fontMetrics
.tmHeight
+ 2*VERTICAL_ITEM_PADDING
+
1897 SELECTED_TAB_OFFSET
;
1900 * Initialize the width of a tab.
1902 infoPtr
->tabWidth
= DEFAULT_TAB_WIDTH
;
1904 SelectObject (hdc
, hOldFont
);
1905 ReleaseDC(hwnd
, hdc
);
1911 TAB_Destroy (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1913 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1919 if (infoPtr
->items
) {
1920 for (iItem
= 0; iItem
< infoPtr
->uNumItem
; iItem
++) {
1921 if (infoPtr
->items
[iItem
].pszText
)
1922 COMCTL32_Free (infoPtr
->items
[iItem
].pszText
);
1924 COMCTL32_Free (infoPtr
->items
);
1927 if (infoPtr
->hwndToolTip
)
1928 DestroyWindow (infoPtr
->hwndToolTip
);
1930 if (infoPtr
->hwndUpDown
)
1931 DestroyWindow(infoPtr
->hwndUpDown
);
1933 COMCTL32_Free (infoPtr
);
1934 SetWindowLongA(hwnd
, 0, 0);
1938 static LRESULT WINAPI
1939 TAB_WindowProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1942 TRACE("hwnd=%x msg=%x wParam=%x lParam=%lx\n", hwnd
, uMsg
, wParam
, lParam
);
1943 if (!TAB_GetInfoPtr(hwnd
) && (uMsg
!= WM_CREATE
))
1944 return DefWindowProcA (hwnd
, uMsg
, wParam
, lParam
);
1948 case TCM_GETIMAGELIST
:
1949 return TAB_GetImageList (hwnd
, wParam
, lParam
);
1951 case TCM_SETIMAGELIST
:
1952 return TAB_SetImageList (hwnd
, wParam
, lParam
);
1954 case TCM_GETITEMCOUNT
:
1955 return TAB_GetItemCount (hwnd
, wParam
, lParam
);
1958 return TAB_GetItemA (hwnd
, wParam
, lParam
);
1961 FIXME("Unimplemented msg TCM_GETITEMW\n");
1965 return TAB_SetItemA (hwnd
, wParam
, lParam
);
1968 FIXME("Unimplemented msg TCM_SETITEMW\n");
1971 case TCM_DELETEITEM
:
1972 return TAB_DeleteItem (hwnd
, wParam
, lParam
);
1974 case TCM_DELETEALLITEMS
:
1975 return TAB_DeleteAllItems (hwnd
, wParam
, lParam
);
1977 case TCM_GETITEMRECT
:
1978 return TAB_GetItemRect (hwnd
, wParam
, lParam
);
1981 return TAB_GetCurSel (hwnd
);
1984 return TAB_HitTest (hwnd
, wParam
, lParam
);
1987 return TAB_SetCurSel (hwnd
, wParam
);
1989 case TCM_INSERTITEMA
:
1990 return TAB_InsertItem (hwnd
, wParam
, lParam
);
1992 case TCM_INSERTITEMW
:
1993 FIXME("Unimplemented msg TCM_INSERTITEMW\n");
1996 case TCM_SETITEMEXTRA
:
1997 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
2000 case TCM_ADJUSTRECT
:
2001 return TAB_AdjustRect (hwnd
, (BOOL
)wParam
, (LPRECT
)lParam
);
2003 case TCM_SETITEMSIZE
:
2004 return TAB_SetItemSize (hwnd
, wParam
, lParam
);
2006 case TCM_REMOVEIMAGE
:
2007 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
2010 case TCM_SETPADDING
:
2011 FIXME("Unimplemented msg TCM_SETPADDING\n");
2014 case TCM_GETROWCOUNT
:
2015 FIXME("Unimplemented msg TCM_GETROWCOUNT\n");
2018 case TCM_GETUNICODEFORMAT
:
2019 FIXME("Unimplemented msg TCM_GETUNICODEFORMAT\n");
2022 case TCM_SETUNICODEFORMAT
:
2023 FIXME("Unimplemented msg TCM_SETUNICODEFORMAT\n");
2026 case TCM_HIGHLIGHTITEM
:
2027 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
2030 case TCM_GETTOOLTIPS
:
2031 return TAB_GetToolTips (hwnd
, wParam
, lParam
);
2033 case TCM_SETTOOLTIPS
:
2034 return TAB_SetToolTips (hwnd
, wParam
, lParam
);
2036 case TCM_GETCURFOCUS
:
2037 return TAB_GetCurFocus (hwnd
);
2039 case TCM_SETCURFOCUS
:
2040 return TAB_SetCurFocus (hwnd
, wParam
);
2042 case TCM_SETMINTABWIDTH
:
2043 FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
2046 case TCM_DESELECTALL
:
2047 FIXME("Unimplemented msg TCM_DESELECTALL\n");
2050 case TCM_GETEXTENDEDSTYLE
:
2051 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
2054 case TCM_SETEXTENDEDSTYLE
:
2055 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
2059 return TAB_GetFont (hwnd
, wParam
, lParam
);
2062 return TAB_SetFont (hwnd
, wParam
, lParam
);
2065 return TAB_Create (hwnd
, wParam
, lParam
);
2068 return TAB_Destroy (hwnd
, wParam
, lParam
);
2071 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2073 case WM_LBUTTONDOWN
:
2074 return TAB_LButtonDown (hwnd
, wParam
, lParam
);
2077 return TAB_LButtonUp (hwnd
, wParam
, lParam
);
2079 case WM_RBUTTONDOWN
:
2080 return TAB_RButtonDown (hwnd
, wParam
, lParam
);
2083 return TAB_MouseMove (hwnd
, wParam
, lParam
);
2086 return TAB_EraseBackground (hwnd
, (HDC
)wParam
);
2089 return TAB_Paint (hwnd
, wParam
);
2092 return TAB_Size (hwnd
, wParam
, lParam
);
2095 return TAB_SetRedraw (hwnd
, wParam
);
2098 return TAB_OnHScroll(hwnd
, (int)LOWORD(wParam
), (int)HIWORD(wParam
), (HWND
)lParam
);
2100 case WM_STYLECHANGED
:
2101 TAB_SetItemBounds (hwnd
);
2102 InvalidateRect(hwnd
, NULL
, TRUE
);
2107 return TAB_FocusChanging(hwnd
, uMsg
, wParam
, lParam
);
2110 return TAB_KeyUp(hwnd
, wParam
);
2113 if (uMsg
>= WM_USER
)
2114 ERR("unknown msg %04x wp=%08x lp=%08lx\n",
2115 uMsg
, wParam
, lParam
);
2116 return DefWindowProcA (hwnd
, uMsg
, wParam
, lParam
);
2128 ZeroMemory (&wndClass
, sizeof(WNDCLASSA
));
2129 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
;
2130 wndClass
.lpfnWndProc
= (WNDPROC
)TAB_WindowProc
;
2131 wndClass
.cbClsExtra
= 0;
2132 wndClass
.cbWndExtra
= sizeof(TAB_INFO
*);
2133 wndClass
.hCursor
= LoadCursorA (0, IDC_ARROWA
);
2134 wndClass
.hbrBackground
= (HBRUSH
)NULL
;
2135 wndClass
.lpszClassName
= WC_TABCONTROLA
;
2137 RegisterClassA (&wndClass
);
2142 TAB_Unregister (void)
2144 UnregisterClassA (WC_TABCONTROLA
, (HINSTANCE
)NULL
);