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);
1937 static LRESULT WINAPI
1938 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1942 case TCM_GETIMAGELIST:
1943 return TAB_GetImageList (hwnd, wParam, lParam);
1945 case TCM_SETIMAGELIST:
1946 return TAB_SetImageList (hwnd, wParam, lParam);
1948 case TCM_GETITEMCOUNT:
1949 return TAB_GetItemCount (hwnd, wParam, lParam);
1952 return TAB_GetItemA (hwnd, wParam, lParam);
1955 FIXME("Unimplemented msg TCM_GETITEMW\n");
1959 return TAB_SetItemA (hwnd, wParam, lParam);
1962 FIXME("Unimplemented msg TCM_SETITEMW\n");
1965 case TCM_DELETEITEM:
1966 return TAB_DeleteItem (hwnd, wParam, lParam);
1968 case TCM_DELETEALLITEMS:
1969 return TAB_DeleteAllItems (hwnd, wParam, lParam);
1971 case TCM_GETITEMRECT:
1972 return TAB_GetItemRect (hwnd, wParam, lParam);
1975 return TAB_GetCurSel (hwnd);
1978 return TAB_HitTest (hwnd, wParam, lParam);
1981 return TAB_SetCurSel (hwnd, wParam);
1983 case TCM_INSERTITEMA:
1984 return TAB_InsertItem (hwnd, wParam, lParam);
1986 case TCM_INSERTITEMW:
1987 FIXME("Unimplemented msg TCM_INSERTITEMW\n");
1990 case TCM_SETITEMEXTRA:
1991 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
1994 case TCM_ADJUSTRECT:
1995 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
1997 case TCM_SETITEMSIZE:
1998 return TAB_SetItemSize (hwnd, wParam, lParam);
2000 case TCM_REMOVEIMAGE:
2001 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
2004 case TCM_SETPADDING:
2005 FIXME("Unimplemented msg TCM_SETPADDING\n");
2008 case TCM_GETROWCOUNT:
2009 FIXME("Unimplemented msg TCM_GETROWCOUNT\n");
2012 case TCM_GETUNICODEFORMAT:
2013 FIXME("Unimplemented msg TCM_GETUNICODEFORMAT\n");
2016 case TCM_SETUNICODEFORMAT:
2017 FIXME("Unimplemented msg TCM_SETUNICODEFORMAT\n");
2020 case TCM_HIGHLIGHTITEM:
2021 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
2024 case TCM_GETTOOLTIPS:
2025 return TAB_GetToolTips (hwnd, wParam, lParam);
2027 case TCM_SETTOOLTIPS:
2028 return TAB_SetToolTips (hwnd, wParam, lParam);
2030 case TCM_GETCURFOCUS:
2031 return TAB_GetCurFocus (hwnd);
2033 case TCM_SETCURFOCUS:
2034 return TAB_SetCurFocus (hwnd, wParam);
2036 case TCM_SETMINTABWIDTH:
2037 FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
2040 case TCM_DESELECTALL:
2041 FIXME("Unimplemented msg TCM_DESELECTALL\n");
2044 case TCM_GETEXTENDEDSTYLE:
2045 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
2048 case TCM_SETEXTENDEDSTYLE:
2049 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
2053 return TAB_GetFont (hwnd, wParam, lParam);
2056 return TAB_SetFont (hwnd, wParam, lParam);
2059 return TAB_Create (hwnd, wParam, lParam);
2062 return TAB_Destroy (hwnd, wParam, lParam);
2065 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2067 case WM_LBUTTONDOWN:
2068 return TAB_LButtonDown (hwnd, wParam, lParam);
2071 return TAB_LButtonUp (hwnd, wParam, lParam);
2073 case WM_RBUTTONDOWN:
2074 return TAB_RButtonDown (hwnd, wParam, lParam);
2077 return TAB_MouseMove (hwnd, wParam, lParam);
2080 return TAB_EraseBackground (hwnd, (HDC)wParam);
2083 return TAB_Paint (hwnd, wParam);
2086 return TAB_Size (hwnd, wParam, lParam);
2089 return TAB_SetRedraw (hwnd, wParam);
2092 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
2094 case WM_STYLECHANGED:
2095 TAB_SetItemBounds (hwnd);
2096 InvalidateRect(hwnd, NULL, TRUE);
2101 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
2104 return TAB_KeyUp(hwnd, wParam);
2107 if (uMsg >= WM_USER)
2108 ERR("unknown msg %04x wp=%08x lp=%08lx\n",
2109 uMsg, wParam, lParam);
2110 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
2122 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
2123 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
2124 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
2125 wndClass.cbClsExtra = 0;
2126 wndClass.cbWndExtra = sizeof(TAB_INFO *);
2127 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
2128 wndClass.hbrBackground = (HBRUSH)NULL;
2129 wndClass.lpszClassName = WC_TABCONTROLA;
2131 RegisterClassA (&wndClass);
2136 TAB_Unregister (void)
2138 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);