4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
19 #include "debugtools.h"
22 DEFAULT_DEBUG_CHANNEL(tab)
24 /******************************************************************************
25 * Positioning constants
27 #define SELECTED_TAB_OFFSET 2
28 #define HORIZONTAL_ITEM_PADDING 5
29 #define VERTICAL_ITEM_PADDING 3
30 #define ROUND_CORNER_SIZE 2
31 #define FOCUS_RECT_HOFFSET 2
32 #define FOCUS_RECT_VOFFSET 1
33 #define DISPLAY_AREA_PADDINGX 5
34 #define DISPLAY_AREA_PADDINGY 5
35 #define CONTROL_BORDER_SIZEX 2
36 #define CONTROL_BORDER_SIZEY 2
37 #define BUTTON_SPACINGX 10
38 #define DEFAULT_TAB_WIDTH 96
40 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
42 /******************************************************************************
45 static void TAB_Refresh (HWND hwnd, HDC hdc);
46 static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
47 static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
50 TAB_SendSimpleNotify (HWND hwnd, UINT code)
54 nmhdr.hwndFrom = hwnd;
55 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
58 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
59 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
64 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
65 WPARAM wParam, LPARAM lParam)
73 msg.time = GetMessageTime ();
74 msg.pt.x = LOWORD(GetMessagePos ());
75 msg.pt.y = HIWORD(GetMessagePos ());
77 SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
83 TAB_GetCurSel (HWND hwnd)
85 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
87 return infoPtr->iSelected;
91 TAB_GetCurFocus (HWND hwnd)
93 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
95 return infoPtr->uFocus;
99 TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
101 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
103 if (infoPtr == NULL) return 0;
104 return infoPtr->hwndToolTip;
109 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
111 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
112 INT iItem=(INT) wParam;
116 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
117 prevItem=infoPtr->iSelected;
118 infoPtr->iSelected=iItem;
119 TAB_EnsureSelectionVisible(hwnd, infoPtr);
120 TAB_InvalidateTabArea(hwnd, infoPtr);
126 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
128 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
129 INT iItem=(INT) wParam;
131 if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
133 infoPtr->uFocus=iItem;
134 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
135 FIXME("Should set input focus\n");
137 if (infoPtr->iSelected != iItem) {
138 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
139 infoPtr->iSelected = iItem;
140 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
142 TAB_EnsureSelectionVisible(hwnd, infoPtr);
143 TAB_InvalidateTabArea(hwnd, infoPtr);
151 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
153 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
155 if (infoPtr == NULL) return 0;
156 infoPtr->hwndToolTip = (HWND)wParam;
160 /******************************************************************************
161 * TAB_InternalGetItemRect
163 * This method will calculate the rectangle representing a given tab item in
164 * client coordinates. This method takes scrolling into account.
166 * This method returns TRUE if the item is visible in the window and FALSE
167 * if it is completely outside the client area.
169 static BOOL TAB_InternalGetItemRect(
179 * Perform a sanity check and a trivial visibility check.
181 if ( (infoPtr->uNumItem <=0) ||
182 (itemIndex >= infoPtr->uNumItem) ||
183 (itemIndex < infoPtr->leftmostVisible) )
187 * Avoid special cases in this procedure by assigning the "out"
188 * parameters if the caller didn't supply them
191 itemRect = &tmpItemRect;
194 * Retrieve the unmodified item rect.
196 *itemRect = infoPtr->items[itemIndex].rect;
199 * "scroll" it to make sure the item at the very left of the
200 * tab control is the leftmost visible tab.
203 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
207 * Move the rectangle so the first item is slightly offset from
208 * the left of the tab control.
216 * Now, calculate the position of the item as if it were selected.
218 if (selectedRect!=NULL)
220 CopyRect(selectedRect, itemRect);
223 * The rectangle of a selected item is a bit wider.
225 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
228 * If it also a bit higher.
230 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
232 selectedRect->top -=2; /* the border is thicker on the bottom */
233 selectedRect->bottom +=SELECTED_TAB_OFFSET;
237 selectedRect->top -=SELECTED_TAB_OFFSET;
238 selectedRect->bottom+=1;
245 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
247 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
248 (LPRECT)lParam, (LPRECT)NULL);
251 /******************************************************************************
254 * This method is called to handle keyboard input
256 static LRESULT TAB_KeyUp(
260 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
266 newItem = infoPtr->uFocus-1;
269 newItem = infoPtr->uFocus+1;
274 * If we changed to a valid item, change the selection
276 if ( (newItem >= 0) &&
277 (newItem < infoPtr->uNumItem) &&
278 (infoPtr->uFocus != newItem) )
280 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
282 infoPtr->iSelected = newItem;
283 infoPtr->uFocus = newItem;
284 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
286 TAB_EnsureSelectionVisible(hwnd, infoPtr);
287 TAB_InvalidateTabArea(hwnd, infoPtr);
294 /******************************************************************************
297 * This method is called whenever the focus goes in or out of this control
298 * it is used to update the visual state of the control.
300 static LRESULT TAB_FocusChanging(
306 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
311 * Get the rectangle for the item.
313 isVisible = TAB_InternalGetItemRect(hwnd,
320 * If the rectangle is not completely invisible, invalidate that
321 * portion of the window.
325 InvalidateRect(hwnd, &selectedRect, TRUE);
329 * Don't otherwise disturb normal behavior.
331 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
334 static HWND TAB_InternalHitTest (
344 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
346 TAB_InternalGetItemRect(hwnd,
352 if (PtInRect (&rect, pt))
354 *flags = TCHT_ONITEM;
364 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
366 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
367 LPTCHITTESTINFO lptest=(LPTCHITTESTINFO) lParam;
369 return TAB_InternalHitTest (hwnd, infoPtr,lptest->pt,&lptest->flags);
374 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
376 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
380 if (infoPtr->hwndToolTip)
381 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
382 WM_LBUTTONDOWN, wParam, lParam);
384 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
388 if (infoPtr->hwndToolTip)
389 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
390 WM_LBUTTONDOWN, wParam, lParam);
392 pt.x = (INT)LOWORD(lParam);
393 pt.y = (INT)HIWORD(lParam);
395 newItem=TAB_InternalHitTest (hwnd, infoPtr,pt,&dummy);
397 TRACE("On Tab, item %d\n", newItem);
399 if ( (newItem!=-1) &&
400 (infoPtr->iSelected != newItem) )
402 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE)
404 infoPtr->iSelected = newItem;
405 infoPtr->uFocus = newItem;
406 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
408 TAB_EnsureSelectionVisible(hwnd, infoPtr);
410 TAB_InvalidateTabArea(hwnd, infoPtr);
417 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
419 TAB_SendSimpleNotify(hwnd, NM_CLICK);
425 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
427 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
432 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
434 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
436 if (infoPtr->hwndToolTip)
437 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
438 WM_LBUTTONDOWN, wParam, lParam);
442 /******************************************************************************
445 * Calculates the tab control's display area given the windows rectangle or
446 * the window rectangle given the requested display rectangle.
448 static LRESULT TAB_AdjustRect(
453 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
458 * Go from display rectangle
462 * Add the height of the tabs.
464 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
465 prc->bottom += infoPtr->tabHeight;
467 prc->top -= infoPtr->tabHeight;
470 * Inflate the rectangle for the padding
472 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
475 * Inflate for the border
477 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
482 * Go from window rectangle.
486 * Deflate the rectangle for the border
488 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
491 * Deflate the rectangle for the padding
493 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
496 * Remove the height of the tabs.
498 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
499 prc->bottom -= infoPtr->tabHeight;
501 prc->top += infoPtr->tabHeight;
508 /******************************************************************************
511 * This method will handle the notification from the scroll control and
512 * perform the scrolling operation on the tab control.
514 static LRESULT TAB_OnHScroll(
520 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
522 if (nScrollCode == SB_LINELEFT)
524 if (infoPtr->leftmostVisible>0)
526 infoPtr->leftmostVisible--;
528 TAB_InvalidateTabArea(hwnd, infoPtr);
531 else if (nScrollCode == SB_LINERIGHT)
533 if (infoPtr->leftmostVisible< (infoPtr->uNumItem-1))
535 infoPtr->leftmostVisible++;
537 TAB_InvalidateTabArea(hwnd, infoPtr);
544 /******************************************************************************
547 * This method will check the current scrolling state and make sure the
548 * scrolling control is displayed (or not).
550 static void TAB_SetupScrolling(
553 const RECT* clientRect)
555 if (infoPtr->needsScrolling)
560 * Calculate the position of the scroll control.
562 controlPos.right = clientRect->right;
563 controlPos.left = controlPos.right - 2*GetSystemMetrics(SM_CXHSCROLL);
565 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
567 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
568 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
572 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
573 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
577 * If we don't have a scroll control yet, we want to create one.
578 * If we have one, we want to make sure it's positioned right.
580 if (infoPtr->hwndUpDown==0)
583 * I use a scrollbar since it seems to be more stable than the Updown
586 infoPtr->hwndUpDown = CreateWindowA("ScrollBar",
588 WS_VISIBLE | WS_CHILD | WS_OVERLAPPED | SBS_HORZ,
589 controlPos.left, controlPos.top,
590 controlPos.right - controlPos.left,
591 controlPos.bottom - controlPos.top,
599 SetWindowPos(infoPtr->hwndUpDown,
601 controlPos.left, controlPos.top,
602 controlPos.right - controlPos.left,
603 controlPos.bottom - controlPos.top,
604 SWP_SHOWWINDOW | SWP_NOZORDER);
610 * If we once had a scroll control... hide it.
612 if (infoPtr->hwndUpDown!=0)
614 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
619 /******************************************************************************
622 * This method will calculate the position rectangles of all the items in the
623 * control. The rectangle calculated starts at 0 for the first item in the
624 * list and ignores scrolling and selection.
625 * It also uses the current font to determine the height of the tab row and
626 * it checks if all the tabs fit in the client area of the window. If they
627 * dont, a scrolling control is added.
629 static void TAB_SetItemBounds (HWND hwnd)
631 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
632 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
633 TEXTMETRICA fontMetrics;
636 HFONT hFont, hOldFont;
642 * We need to get text information so we need a DC and we need to select
647 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
648 hOldFont = SelectObject (hdc, hFont);
651 * We will base the rectangle calculations on the client rectangle
654 GetClientRect(hwnd, &clientRect);
657 * The leftmost item will be "0" aligned
661 if (!((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED)))
667 * Use the current font to determine the height of a tab.
669 GetTextMetricsA(hdc, &fontMetrics);
672 * Get the icon height
675 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
678 * Take the highest between font or icon
680 if (fontMetrics.tmHeight > icon_height)
681 item_height = fontMetrics.tmHeight;
683 item_height = icon_height;
686 * Make sure there is enough space for the letters + icon + growing the
687 * selected item + extra space for the selected item.
689 infoPtr->tabHeight = item_height + 2*VERTICAL_ITEM_PADDING +
693 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
696 * Calculate the vertical position of the tab
698 if (lStyle & TCS_BOTTOM)
700 infoPtr->items[curItem].rect.bottom = clientRect.bottom -
702 infoPtr->items[curItem].rect.top = clientRect.bottom -
707 infoPtr->items[curItem].rect.top = clientRect.top +
709 infoPtr->items[curItem].rect.bottom = clientRect.top +
714 * Set the leftmost position of the tab.
716 infoPtr->items[curItem].rect.left = curItemLeftPos;
718 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
720 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
722 2*HORIZONTAL_ITEM_PADDING;
730 * Calculate how wide the tab is depending on the text it contains
732 GetTextExtentPoint32A(hdc, infoPtr->items[curItem].pszText,
733 lstrlenA(infoPtr->items[curItem].pszText), &size);
740 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
744 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
745 size.cx + icon_width +
746 num*HORIZONTAL_ITEM_PADDING;
749 TRACE("TextSize: %i\n ", size.cx);
750 TRACE("Rect: T %i, L %i, B %i, R %i\n",
751 infoPtr->items[curItem].rect.top,
752 infoPtr->items[curItem].rect.left,
753 infoPtr->items[curItem].rect.bottom,
754 infoPtr->items[curItem].rect.right);
757 * The leftmost position of the next item is the rightmost position
760 if (lStyle & TCS_BUTTONS)
761 curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX;
763 curItemLeftPos = infoPtr->items[curItem].rect.right;
767 * Check if we need a scrolling control.
769 infoPtr->needsScrolling = (curItemLeftPos + (2*SELECTED_TAB_OFFSET) >
772 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
777 SelectObject (hdc, hOldFont);
778 ReleaseDC (hwnd, hdc);
781 /******************************************************************************
784 * This method is used to draw a single tab into the tab control.
786 static void TAB_DrawItem(
791 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
792 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
799 * Get the rectangle for the item.
801 isVisible = TAB_InternalGetItemRect(hwnd,
809 HBRUSH hbr = CreateSolidBrush (GetSysColor(COLOR_BTNFACE));
810 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
811 HPEN hbPen = GetSysColorPen (COLOR_BTNSHADOW);
812 HPEN hsdPen = GetSysColorPen (COLOR_BTNTEXT);
813 HPEN hfocusPen = CreatePen(PS_DOT, 1, GetSysColor(COLOR_BTNTEXT));
817 BOOL deleteBrush = TRUE;
819 if (lStyle & TCS_BUTTONS)
822 * Get item rectangle.
826 holdPen = SelectObject (hdc, hwPen);
828 if (iItem == infoPtr->iSelected)
833 if (!(lStyle & TCS_OWNERDRAWFIXED))
835 COLORREF bk = GetSysColor(COLOR_3DHILIGHT);
837 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
838 SetTextColor(hdc, GetSysColor(COLOR_3DFACE));
841 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
842 * we better use 0x55aa bitmap brush to make scrollbar's background
843 * look different from the window background.
845 if (bk == GetSysColor(COLOR_WINDOW))
846 hbr = CACHE_GetPattern55AABrush();
852 * Erase the background.
854 FillRect(hdc, &r, hbr);
858 * The rectangles calculated exclude the right and bottom
859 * borders of the rectangle. To simply the following code, those
860 * borders are shaved-off beforehand.
866 MoveToEx (hdc, r.left, r.bottom, NULL);
867 LineTo (hdc, r.right, r.bottom);
868 LineTo (hdc, r.right, r.top);
871 SelectObject(hdc, hbPen);
872 LineTo (hdc, r.left, r.top);
873 LineTo (hdc, r.left, r.bottom);
878 * Erase the background.
880 FillRect(hdc, &r, hbr);
883 MoveToEx (hdc, r.left, r.bottom, NULL);
884 LineTo (hdc, r.left, r.top);
885 LineTo (hdc, r.right, r.top);
888 SelectObject(hdc, hbPen);
889 LineTo (hdc, r.right, r.bottom);
890 LineTo (hdc, r.left, r.bottom);
899 hbr = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
902 * We draw a rectangle of different sizes depending on the selection
905 if (iItem == infoPtr->iSelected)
911 * Erase the background.
912 * This is necessary when drawing the selected item since it is larger
913 * than the others, it might overlap with stuff already drawn by the
916 FillRect(hdc, &r, hbr);
920 * The rectangles calculated exclude the right and bottom
921 * borders of the rectangle. To simply the following code, those
922 * borders are shaved-off beforehand.
927 holdPen = SelectObject (hdc, hwPen);
929 if (lStyle & TCS_BOTTOM)
932 MoveToEx (hdc, r.left, r.top, NULL);
933 LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE);
934 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom);
937 SelectObject(hdc, hbPen);
938 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom);
939 LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE);
940 LineTo (hdc, r.right, r.top);
945 MoveToEx (hdc, r.left, r.bottom, NULL);
946 LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE);
947 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top);
948 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top);
951 SelectObject(hdc, hbPen);
952 LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE);
953 LineTo (hdc, r.right, r.bottom);
960 SelectObject(hdc, hsdPen);
962 oldBkMode = SetBkMode(hdc, TRANSPARENT);
963 SetTextColor (hdc, COLOR_BTNTEXT);
966 * Deflate the rectangle to acount for the padding
968 InflateRect(&r, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
975 ImageList_Draw (infoPtr->himl, iItem, hdc,
976 r.left, r.top+1, ILD_NORMAL);
977 ImageList_GetIconSize (infoPtr->himl, &cx, &cy);
978 r.left+=(cx + HORIZONTAL_ITEM_PADDING);
985 infoPtr->items[iItem].pszText,
986 lstrlenA(infoPtr->items[iItem].pszText),
988 DT_LEFT|DT_SINGLELINE|DT_VCENTER);
991 * Draw the focus rectangle
993 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
994 (GetFocus() == hwnd) &&
995 (iItem == infoPtr->uFocus) )
997 InflateRect(&r, FOCUS_RECT_HOFFSET, FOCUS_RECT_VOFFSET);
999 SelectObject(hdc, hfocusPen);
1001 MoveToEx (hdc, r.left, r.top, NULL);
1002 LineTo (hdc, r.right-1, r.top);
1003 LineTo (hdc, r.right-1, r.bottom -1);
1004 LineTo (hdc, r.left, r.bottom -1);
1005 LineTo (hdc, r.left, r.top);
1011 SetBkMode(hdc, oldBkMode);
1012 SelectObject(hdc, holdPen);
1013 DeleteObject(hfocusPen);
1014 if (deleteBrush) DeleteObject(hbr);
1018 /******************************************************************************
1021 * This method is used to draw the raised border around the tab control
1024 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
1026 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1028 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
1029 HPEN hbPen = GetSysColorPen (COLOR_3DDKSHADOW);
1030 HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW);
1033 GetClientRect (hwnd, &rect);
1036 * Adjust for the style
1038 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1040 rect.bottom -= infoPtr->tabHeight;
1044 rect.top += infoPtr->tabHeight;
1048 * Shave-off the right and bottom margins (exluded in the
1055 htmPen = SelectObject (hdc, hwPen);
1057 MoveToEx (hdc, rect.left, rect.bottom, NULL);
1058 LineTo (hdc, rect.left, rect.top);
1059 LineTo (hdc, rect.right, rect.top);
1062 SelectObject (hdc, hbPen);
1063 LineTo (hdc, rect.right, rect.bottom );
1064 LineTo (hdc, rect.left, rect.bottom);
1067 SelectObject (hdc, hShade );
1068 MoveToEx (hdc, rect.right-1, rect.top, NULL);
1069 LineTo (hdc, rect.right-1, rect.bottom-1);
1070 LineTo (hdc, rect.left, rect.bottom-1);
1072 SelectObject(hdc, htmPen);
1075 /******************************************************************************
1078 * This method repaints the tab control..
1080 static void TAB_Refresh (HWND hwnd, HDC hdc)
1082 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1086 if (!infoPtr->DoRedraw)
1089 hOldFont = SelectObject (hdc, infoPtr->hFont);
1091 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
1093 for (i = 0; i < infoPtr->uNumItem; i++)
1095 TAB_DrawItem (hwnd, hdc, i);
1101 * Draw all the non selected item first.
1103 for (i = 0; i < infoPtr->uNumItem; i++)
1105 if (i != infoPtr->iSelected)
1106 TAB_DrawItem (hwnd, hdc, i);
1110 * Now, draw the border, draw it before the selected item
1111 * since the selected item overwrites part of the border.
1113 TAB_DrawBorder (hwnd, hdc);
1116 * Then, draw the selected item
1118 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
1121 SelectObject (hdc, hOldFont);
1125 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
1127 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1129 infoPtr->DoRedraw=(BOOL) wParam;
1133 static LRESULT TAB_EraseBackground(
1140 HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1142 hdc = givenDC ? givenDC : GetDC(hwnd);
1144 GetClientRect(hwnd, &clientRect);
1146 FillRect(hdc, &clientRect, brush);
1149 ReleaseDC(hwnd, hdc);
1151 DeleteObject(brush);
1156 /******************************************************************************
1157 * TAB_EnsureSelectionVisible
1159 * This method will make sure that the current selection is completely
1160 * visible by scrolling until it is.
1162 static void TAB_EnsureSelectionVisible(
1172 * Do the trivial cases first.
1174 if ( (!infoPtr->needsScrolling) ||
1175 (infoPtr->hwndUpDown==0) )
1178 if (infoPtr->leftmostVisible > infoPtr->iSelected)
1180 infoPtr->leftmostVisible = infoPtr->iSelected;
1185 * Calculate the part of the client area that is visible.
1187 GetClientRect(hwnd, &visibleRect);
1188 GetClientRect(infoPtr->hwndUpDown, &scrollerRect);
1189 visibleRect.right -= scrollerRect.right;
1192 * Get the rectangle for the item
1194 isVisible = TAB_InternalGetItemRect(hwnd,
1201 * If this function can't say it's completely invisible, maybe it
1202 * is partially visible. Let's check.
1209 pt1.x = selectedRect.left;
1210 pt1.y = selectedRect.top;
1211 pt2.x = selectedRect.right - 1;
1212 pt2.y = selectedRect.bottom - 1;
1214 isVisible = PtInRect(&visibleRect, pt1) && PtInRect(&visibleRect, pt2);
1217 while ( (infoPtr->leftmostVisible < infoPtr->iSelected) &&
1220 infoPtr->leftmostVisible++;
1223 * Get the rectangle for the item
1225 isVisible = TAB_InternalGetItemRect(hwnd,
1232 * If this function can't say it's completely invisible, maybe it
1233 * is partially visible. Let's check.
1240 pt1.x = selectedRect.left;
1241 pt1.y = selectedRect.top;
1242 pt2.x = selectedRect.right - 1;
1243 pt2.y = selectedRect.bottom - 1;
1245 isVisible = PtInRect(&visibleRect, pt1) && PtInRect(&visibleRect, pt2);
1250 /******************************************************************************
1251 * TAB_InvalidateTabArea
1253 * This method will invalidate the portion of the control that contains the
1254 * tabs. It is called when the state of the control changes and needs
1257 static void TAB_InvalidateTabArea(
1263 GetClientRect(hwnd, &clientRect);
1265 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1267 clientRect.top = clientRect.bottom - (infoPtr->tabHeight + 1);
1271 clientRect.bottom = clientRect.top + (infoPtr->tabHeight + 1);
1274 InvalidateRect(hwnd, &clientRect, TRUE);
1278 TAB_Paint (HWND hwnd, WPARAM wParam)
1283 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
1284 TAB_Refresh (hwnd, hdc);
1287 EndPaint (hwnd, &ps);
1293 TAB_InsertItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1295 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1300 GetClientRect (hwnd, &rect);
1301 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
1302 rect.top, rect.left, rect.bottom, rect.right);
1304 pti = (TCITEMA *)lParam;
1305 iItem = (INT)wParam;
1307 if (iItem < 0) return -1;
1308 if (iItem > infoPtr->uNumItem)
1309 iItem = infoPtr->uNumItem;
1311 if (infoPtr->uNumItem == 0) {
1312 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
1313 infoPtr->uNumItem++;
1314 infoPtr->iSelected = 0;
1317 TAB_ITEM *oldItems = infoPtr->items;
1319 infoPtr->uNumItem++;
1320 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
1322 /* pre insert copy */
1324 memcpy (&infoPtr->items[0], &oldItems[0],
1325 iItem * sizeof(TAB_ITEM));
1328 /* post insert copy */
1329 if (iItem < infoPtr->uNumItem - 1) {
1330 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
1331 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
1335 if (iItem <= infoPtr->iSelected)
1336 infoPtr->iSelected++;
1338 COMCTL32_Free (oldItems);
1341 infoPtr->items[iItem].mask = pti->mask;
1342 if (pti->mask & TCIF_TEXT) {
1343 len = lstrlenA (pti->pszText);
1344 infoPtr->items[iItem].pszText = COMCTL32_Alloc (len+1);
1345 lstrcpyA (infoPtr->items[iItem].pszText, pti->pszText);
1346 infoPtr->items[iItem].cchTextMax = pti->cchTextMax;
1349 if (pti->mask & TCIF_IMAGE)
1350 infoPtr->items[iItem].iImage = pti->iImage;
1352 if (pti->mask & TCIF_PARAM)
1353 infoPtr->items[iItem].lParam = pti->lParam;
1355 TAB_InvalidateTabArea(hwnd, infoPtr);
1357 TRACE("[%04x]: added item %d '%s'\n",
1358 hwnd, iItem, infoPtr->items[iItem].pszText);
1360 TAB_SetItemBounds(hwnd);
1365 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
1367 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1368 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1371 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
1373 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
1374 infoPtr->tabWidth = (INT)LOWORD(lParam);
1375 infoPtr->tabHeight = (INT)HIWORD(lParam);
1382 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1384 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1390 tabItem=(LPTCITEMA ) lParam;
1391 TRACE("%d %p\n",iItem, tabItem);
1392 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
1394 wineItem=& infoPtr->items[iItem];
1396 if (tabItem->mask & TCIF_IMAGE)
1397 wineItem->iImage=tabItem->iImage;
1399 if (tabItem->mask & TCIF_PARAM)
1400 wineItem->lParam=tabItem->lParam;
1402 if (tabItem->mask & TCIF_RTLREADING)
1403 FIXME("TCIF_RTLREADING\n");
1405 if (tabItem->mask & TCIF_STATE)
1406 wineItem->dwState=tabItem->dwState;
1408 if (tabItem->mask & TCIF_TEXT) {
1409 len=lstrlenA (tabItem->pszText);
1410 if (len>wineItem->cchTextMax)
1411 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
1412 lstrcpyA (wineItem->pszText, tabItem->pszText);
1416 * Update and repaint tabs.
1418 TAB_SetItemBounds(hwnd);
1419 TAB_InvalidateTabArea(hwnd,infoPtr);
1425 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1427 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1429 return infoPtr->uNumItem;
1434 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1436 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1442 tabItem=(LPTCITEMA) lParam;
1444 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
1446 wineItem=& infoPtr->items[iItem];
1448 if (tabItem->mask & TCIF_IMAGE)
1449 tabItem->iImage=wineItem->iImage;
1451 if (tabItem->mask & TCIF_PARAM)
1452 tabItem->lParam=wineItem->lParam;
1454 if (tabItem->mask & TCIF_RTLREADING)
1455 FIXME("TCIF_RTLREADING\n");
1457 if (tabItem->mask & TCIF_STATE)
1458 tabItem->dwState=wineItem->dwState;
1460 if (tabItem->mask & TCIF_TEXT)
1461 lstrcpynA (tabItem->pszText, wineItem->pszText, tabItem->cchTextMax);
1467 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1469 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1470 INT iItem = (INT) wParam;
1471 BOOL bResult = FALSE;
1473 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
1475 TAB_ITEM *oldItems = infoPtr->items;
1477 infoPtr->uNumItem--;
1478 infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
1481 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
1483 if (iItem < infoPtr->uNumItem)
1484 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
1485 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
1487 COMCTL32_Free (oldItems);
1490 * Readjust the selected index.
1492 if ((iItem == infoPtr->iSelected) && (iItem > 0))
1493 infoPtr->iSelected--;
1495 if (iItem < infoPtr->iSelected)
1496 infoPtr->iSelected--;
1498 if (infoPtr->uNumItem == 0)
1499 infoPtr->iSelected = -1;
1502 * Reposition and repaint tabs.
1504 TAB_SetItemBounds(hwnd);
1505 TAB_InvalidateTabArea(hwnd,infoPtr);
1514 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
1516 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1518 COMCTL32_Free (infoPtr->items);
1519 infoPtr->uNumItem = 0;
1520 infoPtr->iSelected = -1;
1527 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1529 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1532 return (LRESULT)infoPtr->hFont;
1536 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1539 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1541 TRACE("%x %lx\n",wParam, lParam);
1543 infoPtr->hFont = (HFONT)wParam;
1545 TAB_SetItemBounds(hwnd);
1547 TAB_InvalidateTabArea(hwnd, infoPtr);
1554 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1556 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1559 return (LRESULT)infoPtr->himl;
1563 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1565 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1566 HIMAGELIST himlPrev;
1569 himlPrev = infoPtr->himl;
1570 infoPtr->himl= (HIMAGELIST)lParam;
1571 return (LRESULT)himlPrev;
1576 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1579 /* I'm not really sure what the following code was meant to do.
1580 This is what it is doing:
1581 When WM_SIZE is sent with SIZE_RESTORED, the control
1582 gets positioned in the top left corner.
1586 UINT uPosFlags,cx,cy;
1590 parent = GetParent (hwnd);
1591 GetClientRect(parent, &parent_rect);
1594 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
1595 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
1597 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
1598 cx, cy, uPosFlags | SWP_NOZORDER);
1600 FIXME (tab,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
1604 * Recompute the size/position of the tabs.
1606 TAB_SetItemBounds (hwnd);
1609 * Force a repaint of the control.
1611 InvalidateRect(hwnd, NULL, TRUE);
1618 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
1621 TEXTMETRICA fontMetrics;
1625 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
1627 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
1629 infoPtr->uNumItem = 0;
1632 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
1633 infoPtr->iSelected = -1;
1634 infoPtr->uFocus = 0;
1635 infoPtr->hwndToolTip = 0;
1636 infoPtr->DoRedraw = TRUE;
1637 infoPtr->needsScrolling = FALSE;
1638 infoPtr->hwndUpDown = 0;
1639 infoPtr->leftmostVisible = 0;
1641 TRACE("Created tab control, hwnd [%04x]\n", hwnd);
1642 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_TOOLTIPS) {
1643 /* Create tooltip control */
1644 infoPtr->hwndToolTip =
1645 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
1646 CW_USEDEFAULT, CW_USEDEFAULT,
1647 CW_USEDEFAULT, CW_USEDEFAULT,
1650 /* Send NM_TOOLTIPSCREATED notification */
1651 if (infoPtr->hwndToolTip) {
1652 NMTOOLTIPSCREATED nmttc;
1654 nmttc.hdr.hwndFrom = hwnd;
1655 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1656 nmttc.hdr.code = NM_TOOLTIPSCREATED;
1657 nmttc.hwndToolTips = infoPtr->hwndToolTip;
1659 SendMessageA (GetParent (hwnd), WM_NOTIFY,
1660 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
1665 * We need to get text information so we need a DC and we need to select
1669 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
1672 * Use the system font to determine the initial height of a tab.
1674 GetTextMetricsA(hdc, &fontMetrics);
1677 * Make sure there is enough space for the letters + growing the
1678 * selected item + extra space for the selected item.
1680 infoPtr->tabHeight = fontMetrics.tmHeight + 2*VERTICAL_ITEM_PADDING +
1681 SELECTED_TAB_OFFSET;
1684 * Initialize the width of a tab.
1686 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
1688 SelectObject (hdc, hOldFont);
1689 ReleaseDC(hwnd, hdc);
1695 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
1697 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1700 if (infoPtr->items) {
1701 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
1702 if (infoPtr->items[iItem].pszText)
1703 COMCTL32_Free (infoPtr->items[iItem].pszText);
1705 COMCTL32_Free (infoPtr->items);
1708 if (infoPtr->hwndToolTip)
1709 DestroyWindow (infoPtr->hwndToolTip);
1711 if (infoPtr->hwndUpDown)
1712 DestroyWindow(infoPtr->hwndUpDown);
1714 COMCTL32_Free (infoPtr);
1718 static LRESULT WINAPI
1719 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1723 case TCM_GETIMAGELIST:
1724 return TAB_GetImageList (hwnd, wParam, lParam);
1726 case TCM_SETIMAGELIST:
1727 return TAB_SetImageList (hwnd, wParam, lParam);
1729 case TCM_GETITEMCOUNT:
1730 return TAB_GetItemCount (hwnd, wParam, lParam);
1733 return TAB_GetItemA (hwnd, wParam, lParam);
1736 FIXME("Unimplemented msg TCM_GETITEMW\n");
1740 return TAB_SetItemA (hwnd, wParam, lParam);
1743 FIXME("Unimplemented msg TCM_SETITEMW\n");
1746 case TCM_DELETEITEM:
1747 return TAB_DeleteItem (hwnd, wParam, lParam);
1749 case TCM_DELETEALLITEMS:
1750 return TAB_DeleteAllItems (hwnd, wParam, lParam);
1752 case TCM_GETITEMRECT:
1753 return TAB_GetItemRect (hwnd, wParam, lParam);
1756 return TAB_GetCurSel (hwnd);
1759 return TAB_HitTest (hwnd, wParam, lParam);
1762 return TAB_SetCurSel (hwnd, wParam);
1764 case TCM_INSERTITEMA:
1765 return TAB_InsertItem (hwnd, wParam, lParam);
1767 case TCM_INSERTITEMW:
1768 FIXME("Unimplemented msg TCM_INSERTITEM32W\n");
1771 case TCM_SETITEMEXTRA:
1772 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
1775 case TCM_ADJUSTRECT:
1776 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
1778 case TCM_SETITEMSIZE:
1779 return TAB_SetItemSize (hwnd, wParam, lParam);
1781 case TCM_REMOVEIMAGE:
1782 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
1785 case TCM_SETPADDING:
1786 FIXME("Unimplemented msg TCM_SETPADDING\n");
1789 case TCM_GETROWCOUNT:
1790 FIXME("Unimplemented msg TCM_GETROWCOUNT\n");
1793 case TCM_GETUNICODEFORMAT:
1794 FIXME("Unimplemented msg TCM_GETUNICODEFORMAT\n");
1797 case TCM_SETUNICODEFORMAT:
1798 FIXME("Unimplemented msg TCM_SETUNICODEFORMAT\n");
1801 case TCM_HIGHLIGHTITEM:
1802 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
1805 case TCM_GETTOOLTIPS:
1806 return TAB_GetToolTips (hwnd, wParam, lParam);
1808 case TCM_SETTOOLTIPS:
1809 return TAB_SetToolTips (hwnd, wParam, lParam);
1811 case TCM_GETCURFOCUS:
1812 return TAB_GetCurFocus (hwnd);
1814 case TCM_SETCURFOCUS:
1815 return TAB_SetCurFocus (hwnd, wParam);
1817 case TCM_SETMINTTABWIDTH:
1818 FIXME("Unimplemented msg TCM_SETMINTTABWIDTH\n");
1821 case TCM_DESELECTALL:
1822 FIXME("Unimplemented msg TCM_DESELECTALL\n");
1825 case TCM_GETEXTENDEDSTYLE:
1826 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
1829 case TCM_SETEXTENDEDSTYLE:
1830 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
1834 return TAB_GetFont (hwnd, wParam, lParam);
1837 return TAB_SetFont (hwnd, wParam, lParam);
1840 return TAB_Create (hwnd, wParam, lParam);
1843 return TAB_Destroy (hwnd, wParam, lParam);
1846 return DLGC_WANTARROWS | DLGC_WANTCHARS;
1848 case WM_LBUTTONDOWN:
1849 return TAB_LButtonDown (hwnd, wParam, lParam);
1852 return TAB_LButtonUp (hwnd, wParam, lParam);
1854 case WM_RBUTTONDOWN:
1855 return TAB_RButtonDown (hwnd, wParam, lParam);
1858 return TAB_MouseMove (hwnd, wParam, lParam);
1861 return TAB_EraseBackground (hwnd, (HDC)wParam);
1864 return TAB_Paint (hwnd, wParam);
1867 return TAB_Size (hwnd, wParam, lParam);
1870 return TAB_SetRedraw (hwnd, wParam);
1873 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
1877 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
1880 return TAB_KeyUp(hwnd, wParam);
1883 if (uMsg >= WM_USER)
1884 ERR("unknown msg %04x wp=%08x lp=%08lx\n",
1885 uMsg, wParam, lParam);
1886 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1898 if (GlobalFindAtomA (WC_TABCONTROLA)) return;
1900 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1901 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS;
1902 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
1903 wndClass.cbClsExtra = 0;
1904 wndClass.cbWndExtra = sizeof(TAB_INFO *);
1905 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
1906 wndClass.hbrBackground = (HBRUSH)NULL;
1907 wndClass.lpszClassName = WC_TABCONTROLA;
1909 RegisterClassA (&wndClass);
1914 TAB_Unregister (void)
1916 if (GlobalFindAtomA (WC_TABCONTROLA))
1917 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);