Spelling fixes.
[wine/multimedia.git] / dlls / comctl32 / tab.c
blobbe8cce66663d77a57f91b8819e5e388a871c62e1
1 /*
2 * Tab control
4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
8 * TODO:
9 * Image list support
10 * Multiline support
11 * Unicode support
14 #include <string.h>
16 #include "winbase.h"
17 #include "commctrl.h"
18 #include "tab.h"
19 #include "debugtools.h"
20 #include "cache.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 /******************************************************************************
43 * Prototypes
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);
49 static BOOL
50 TAB_SendSimpleNotify (HWND hwnd, UINT code)
52 NMHDR nmhdr;
54 nmhdr.hwndFrom = hwnd;
55 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
56 nmhdr.code = code;
58 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
59 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
63 static VOID
64 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
65 WPARAM wParam, LPARAM lParam)
67 MSG msg;
69 msg.hwnd = hwndMsg;
70 msg.message = uMsg;
71 msg.wParam = wParam;
72 msg.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);
82 static LRESULT
83 TAB_GetCurSel (HWND hwnd)
85 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
87 return infoPtr->iSelected;
90 static LRESULT
91 TAB_GetCurFocus (HWND hwnd)
93 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
95 return infoPtr->uFocus;
98 static LRESULT
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;
108 static LRESULT
109 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
111 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
112 INT iItem=(INT) wParam;
113 INT prevItem;
115 prevItem=-1;
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);
122 return prevItem;
125 static LRESULT
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");
136 } else {
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);
147 return 0;
150 static LRESULT
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;
157 return 0;
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(
170 HWND hwnd,
171 TAB_INFO* infoPtr,
172 INT itemIndex,
173 RECT* itemRect,
174 RECT* selectedRect)
176 RECT tmpItemRect;
179 * Perform a sanity check and a trivial visibility check.
181 if ( (infoPtr->uNumItem <=0) ||
182 (itemIndex >= infoPtr->uNumItem) ||
183 (itemIndex < infoPtr->leftmostVisible) )
184 return FALSE;
187 * Avoid special cases in this procedure by assigning the "out"
188 * parameters if the caller didn't supply them
190 if (itemRect==NULL)
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.
202 OffsetRect(itemRect,
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.
210 OffsetRect(itemRect,
211 SELECTED_TAB_OFFSET,
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;
235 else
237 selectedRect->top -=SELECTED_TAB_OFFSET;
238 selectedRect->bottom+=1;
242 return TRUE;
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 /******************************************************************************
252 * TAB_KeyUp
254 * This method is called to handle keyboard input
256 static LRESULT TAB_KeyUp(
257 HWND hwnd,
258 WPARAM keyCode)
260 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
261 int newItem = -1;
263 switch (keyCode)
265 case VK_LEFT:
266 newItem = infoPtr->uFocus-1;
267 break;
268 case VK_RIGHT:
269 newItem = infoPtr->uFocus+1;
270 break;
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);
291 return 0;
294 /******************************************************************************
295 * TAB_FocusChanging
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(
301 HWND hwnd,
302 UINT uMsg,
303 WPARAM wParam,
304 LPARAM lParam)
306 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
307 RECT selectedRect;
308 BOOL isVisible;
311 * Get the rectangle for the item.
313 isVisible = TAB_InternalGetItemRect(hwnd,
314 infoPtr,
315 infoPtr->uFocus,
316 NULL,
317 &selectedRect);
320 * If the rectangle is not completely invisible, invalidate that
321 * portion of the window.
323 if (isVisible)
325 InvalidateRect(hwnd, &selectedRect, TRUE);
329 * Don't otherwise disturb normal behavior.
331 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
334 static HWND TAB_InternalHitTest (
335 HWND hwnd,
336 TAB_INFO* infoPtr,
337 POINT pt,
338 UINT* flags)
341 RECT rect;
342 int iCount;
344 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
346 TAB_InternalGetItemRect(hwnd,
347 infoPtr,
348 iCount,
349 &rect,
350 NULL);
352 if (PtInRect (&rect, pt))
354 *flags = TCHT_ONITEM;
355 return iCount;
359 *flags=TCHT_NOWHERE;
360 return -1;
363 static LRESULT
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);
373 static LRESULT
374 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
376 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
377 POINT pt;
378 INT newItem,dummy;
380 if (infoPtr->hwndToolTip)
381 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
382 WM_LBUTTONDOWN, wParam, lParam);
384 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
385 SetFocus (hwnd);
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);
413 return 0;
416 static LRESULT
417 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
419 TAB_SendSimpleNotify(hwnd, NM_CLICK);
421 return 0;
424 static LRESULT
425 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
427 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
428 return 0;
431 static LRESULT
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);
439 return 0;
442 /******************************************************************************
443 * TAB_AdjustRect
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(
449 HWND hwnd,
450 WPARAM fLarger,
451 LPRECT prc)
453 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
455 if (fLarger)
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;
466 else
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);
479 else
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;
500 else
501 prc->top += infoPtr->tabHeight;
505 return 0;
508 /******************************************************************************
509 * TAB_OnHScroll
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(
515 HWND hwnd,
516 int nScrollCode,
517 int nPos,
518 HWND hwndScroll)
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);
541 return 0;
544 /******************************************************************************
545 * TAB_SetupScroling
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(
551 HWND hwnd,
552 TAB_INFO* infoPtr,
553 const RECT* clientRect)
555 if (infoPtr->needsScrolling)
557 RECT controlPos;
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);
570 else
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
584 * control.
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,
592 hwnd,
593 (HMENU)NULL,
594 (HINSTANCE)NULL,
595 NULL);
597 else
599 SetWindowPos(infoPtr->hwndUpDown,
600 (HWND)NULL,
601 controlPos.left, controlPos.top,
602 controlPos.right - controlPos.left,
603 controlPos.bottom - controlPos.top,
604 SWP_SHOWWINDOW | SWP_NOZORDER);
607 else
610 * If we once had a scroll control... hide it.
612 if (infoPtr->hwndUpDown!=0)
614 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
619 /******************************************************************************
620 * TAB_SetItemBounds
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;
634 INT curItem;
635 INT curItemLeftPos;
636 HFONT hFont, hOldFont;
637 HDC hdc;
638 RECT clientRect;
639 SIZE size;
642 * We need to get text information so we need a DC and we need to select
643 * a font.
645 hdc = GetDC(hwnd);
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
652 * of the control.
654 GetClientRect(hwnd, &clientRect);
657 * The leftmost item will be "0" aligned
659 curItemLeftPos = 0;
661 if (!((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED)))
663 int item_height;
664 int icon_height = 0;
667 * Use the current font to determine the height of a tab.
669 GetTextMetricsA(hdc, &fontMetrics);
672 * Get the icon height
674 if (infoPtr->himl)
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;
682 else
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 +
690 SELECTED_TAB_OFFSET;
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 -
701 SELECTED_TAB_OFFSET;
702 infoPtr->items[curItem].rect.top = clientRect.bottom -
703 infoPtr->tabHeight;
705 else
707 infoPtr->items[curItem].rect.top = clientRect.top +
708 SELECTED_TAB_OFFSET;
709 infoPtr->items[curItem].rect.bottom = clientRect.top +
710 infoPtr->tabHeight;
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 +
721 infoPtr->tabWidth +
722 2*HORIZONTAL_ITEM_PADDING;
724 else
726 int icon_width = 0;
727 int num = 2;
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);
736 * Add the icon width
738 if (infoPtr->himl)
740 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
741 num++;
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
758 * of this one.
760 if (lStyle & TCS_BUTTONS)
761 curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX;
762 else
763 curItemLeftPos = infoPtr->items[curItem].rect.right;
767 * Check if we need a scrolling control.
769 infoPtr->needsScrolling = (curItemLeftPos + (2*SELECTED_TAB_OFFSET) >
770 clientRect.right);
772 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
775 * Cleanup
777 SelectObject (hdc, hOldFont);
778 ReleaseDC (hwnd, hdc);
781 /******************************************************************************
782 * TAB_DrawItem
784 * This method is used to draw a single tab into the tab control.
786 static void TAB_DrawItem(
787 HWND hwnd,
788 HDC hdc,
789 INT iItem)
791 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
792 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
793 RECT itemRect;
794 RECT selectedRect;
795 BOOL isVisible;
796 RECT r;
799 * Get the rectangle for the item.
801 isVisible = TAB_InternalGetItemRect(hwnd,
802 infoPtr,
803 iItem,
804 &itemRect,
805 &selectedRect);
807 if (isVisible)
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));
814 HPEN holdPen;
815 INT oldBkMode;
816 INT cx,cy;
817 BOOL deleteBrush = TRUE;
819 if (lStyle & TCS_BUTTONS)
822 * Get item rectangle.
824 r = itemRect;
826 holdPen = SelectObject (hdc, hwPen);
828 if (iItem == infoPtr->iSelected)
831 * Background color.
833 if (!(lStyle & TCS_OWNERDRAWFIXED))
835 COLORREF bk = GetSysColor(COLOR_3DHILIGHT);
836 DeleteObject(hbr);
837 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
838 SetTextColor(hdc, GetSysColor(COLOR_3DFACE));
839 SetBkColor(hdc, bk);
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();
848 deleteBrush = FALSE;
852 * Erase the background.
854 FillRect(hdc, &r, hbr);
857 * Draw the tab now.
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.
862 r.right--;
863 r.bottom--;
865 /* highlight */
866 MoveToEx (hdc, r.left, r.bottom, NULL);
867 LineTo (hdc, r.right, r.bottom);
868 LineTo (hdc, r.right, r.top);
870 /* shadow */
871 SelectObject(hdc, hbPen);
872 LineTo (hdc, r.left, r.top);
873 LineTo (hdc, r.left, r.bottom);
875 else
878 * Erase the background.
880 FillRect(hdc, &r, hbr);
882 /* highlight */
883 MoveToEx (hdc, r.left, r.bottom, NULL);
884 LineTo (hdc, r.left, r.top);
885 LineTo (hdc, r.right, r.top);
887 /* shadow */
888 SelectObject(hdc, hbPen);
889 LineTo (hdc, r.right, r.bottom);
890 LineTo (hdc, r.left, r.bottom);
893 else
896 * Background color.
898 DeleteObject(hbr);
899 hbr = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
902 * We draw a rectangle of different sizes depending on the selection
903 * state.
905 if (iItem == infoPtr->iSelected)
906 r = selectedRect;
907 else
908 r = itemRect;
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
914 * other tabs
916 FillRect(hdc, &r, hbr);
919 * Draw the tab now.
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.
924 r.right--;
925 r.bottom--;
927 holdPen = SelectObject (hdc, hwPen);
929 if (lStyle & TCS_BOTTOM)
931 /* highlight */
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);
936 /* shadow */
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);
942 else
944 /* highlight */
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);
950 /* shadow */
951 SelectObject(hdc, hbPen);
952 LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE);
953 LineTo (hdc, r.right, r.bottom);
958 * Text pen
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);
971 * Draw the icon.
973 if (infoPtr->himl)
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);
982 * Draw the text;
984 DrawTextA(hdc,
985 infoPtr->items[iItem].pszText,
986 lstrlenA(infoPtr->items[iItem].pszText),
987 &r,
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);
1009 * Cleanup
1011 SetBkMode(hdc, oldBkMode);
1012 SelectObject(hdc, holdPen);
1013 DeleteObject(hfocusPen);
1014 if (deleteBrush) DeleteObject(hbr);
1018 /******************************************************************************
1019 * TAB_DrawBorder
1021 * This method is used to draw the raised border around the tab control
1022 * "content" area.
1024 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
1026 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1027 HPEN htmPen;
1028 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
1029 HPEN hbPen = GetSysColorPen (COLOR_3DDKSHADOW);
1030 HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW);
1031 RECT rect;
1033 GetClientRect (hwnd, &rect);
1036 * Adjust for the style
1038 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1040 rect.bottom -= infoPtr->tabHeight;
1042 else
1044 rect.top += infoPtr->tabHeight;
1048 * Shave-off the right and bottom margins (exluded in the
1049 * rect)
1051 rect.right--;
1052 rect.bottom--;
1054 /* highlight */
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);
1061 /* Dark Shadow */
1062 SelectObject (hdc, hbPen);
1063 LineTo (hdc, rect.right, rect.bottom );
1064 LineTo (hdc, rect.left, rect.bottom);
1066 /* shade */
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 /******************************************************************************
1076 * TAB_Refresh
1078 * This method repaints the tab control..
1080 static void TAB_Refresh (HWND hwnd, HDC hdc)
1082 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1083 HFONT hOldFont;
1084 INT i;
1086 if (!infoPtr->DoRedraw)
1087 return;
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);
1098 else
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);
1124 static LRESULT
1125 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
1127 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1129 infoPtr->DoRedraw=(BOOL) wParam;
1130 return 0;
1133 static LRESULT TAB_EraseBackground(
1134 HWND hwnd,
1135 HDC givenDC)
1137 HDC hdc;
1138 RECT clientRect;
1140 HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1142 hdc = givenDC ? givenDC : GetDC(hwnd);
1144 GetClientRect(hwnd, &clientRect);
1146 FillRect(hdc, &clientRect, brush);
1148 if (givenDC==0)
1149 ReleaseDC(hwnd, hdc);
1151 DeleteObject(brush);
1153 return 0;
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(
1163 HWND hwnd,
1164 TAB_INFO* infoPtr)
1166 RECT selectedRect;
1167 RECT visibleRect;
1168 RECT scrollerRect;
1169 BOOL isVisible;
1172 * Do the trivial cases first.
1174 if ( (!infoPtr->needsScrolling) ||
1175 (infoPtr->hwndUpDown==0) )
1176 return;
1178 if (infoPtr->leftmostVisible > infoPtr->iSelected)
1180 infoPtr->leftmostVisible = infoPtr->iSelected;
1181 return;
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,
1195 infoPtr,
1196 infoPtr->iSelected,
1197 NULL,
1198 &selectedRect);
1201 * If this function can't say it's completely invisible, maybe it
1202 * is partially visible. Let's check.
1204 if (isVisible)
1206 POINT pt1;
1207 POINT pt2;
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) &&
1218 !isVisible)
1220 infoPtr->leftmostVisible++;
1223 * Get the rectangle for the item
1225 isVisible = TAB_InternalGetItemRect(hwnd,
1226 infoPtr,
1227 infoPtr->iSelected,
1228 NULL,
1229 &selectedRect);
1232 * If this function can't say it's completely invisible, maybe it
1233 * is partially visible. Let's check.
1235 if (isVisible)
1237 POINT pt1;
1238 POINT pt2;
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
1255 * to be redisplayed
1257 static void TAB_InvalidateTabArea(
1258 HWND hwnd,
1259 TAB_INFO* infoPtr)
1261 RECT clientRect;
1263 GetClientRect(hwnd, &clientRect);
1265 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1267 clientRect.top = clientRect.bottom - (infoPtr->tabHeight + 1);
1269 else
1271 clientRect.bottom = clientRect.top + (infoPtr->tabHeight + 1);
1274 InvalidateRect(hwnd, &clientRect, TRUE);
1277 static LRESULT
1278 TAB_Paint (HWND hwnd, WPARAM wParam)
1280 HDC hdc;
1281 PAINTSTRUCT ps;
1283 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
1284 TAB_Refresh (hwnd, hdc);
1286 if(!wParam)
1287 EndPaint (hwnd, &ps);
1289 return 0;
1292 static LRESULT
1293 TAB_InsertItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1295 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1296 TCITEMA *pti;
1297 INT iItem, len;
1298 RECT rect;
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;
1316 else {
1317 TAB_ITEM *oldItems = infoPtr->items;
1319 infoPtr->uNumItem++;
1320 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
1322 /* pre insert copy */
1323 if (iItem > 0) {
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);
1361 return iItem;
1364 static LRESULT
1365 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
1367 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1368 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1369 LONG lResult = 0;
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);
1378 return lResult;
1381 static LRESULT
1382 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1384 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1385 TCITEMA *tabItem;
1386 TAB_ITEM *wineItem;
1387 INT iItem,len;
1389 iItem=(INT) wParam;
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);
1421 return TRUE;
1424 static LRESULT
1425 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1427 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1429 return infoPtr->uNumItem;
1433 static LRESULT
1434 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1436 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1437 TCITEMA *tabItem;
1438 TAB_ITEM *wineItem;
1439 INT iItem;
1441 iItem=(INT) wParam;
1442 tabItem=(LPTCITEMA) lParam;
1443 TRACE("\n");
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);
1463 return TRUE;
1466 static LRESULT
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);
1480 if (iItem > 0)
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);
1507 bResult = TRUE;
1510 return bResult;
1513 static LRESULT
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;
1522 return TRUE;
1526 static LRESULT
1527 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1529 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1531 TRACE("\n");
1532 return (LRESULT)infoPtr->hFont;
1535 static LRESULT
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);
1549 return 0;
1553 static LRESULT
1554 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1556 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1558 TRACE("\n");
1559 return (LRESULT)infoPtr->himl;
1562 static LRESULT
1563 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1565 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1566 HIMAGELIST himlPrev;
1568 TRACE("\n");
1569 himlPrev = infoPtr->himl;
1570 infoPtr->himl= (HIMAGELIST)lParam;
1571 return (LRESULT)himlPrev;
1575 static LRESULT
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.
1584 RECT parent_rect;
1585 HWND parent;
1586 UINT uPosFlags,cx,cy;
1588 uPosFlags=0;
1589 if (!wParam) {
1590 parent = GetParent (hwnd);
1591 GetClientRect(parent, &parent_rect);
1592 cx=LOWORD (lParam);
1593 cy=HIWORD (lParam);
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);
1599 } else {
1600 FIXME (tab,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
1601 } */
1604 * Recompute the size/position of the tabs.
1606 TAB_SetItemBounds (hwnd);
1609 * Force a repaint of the control.
1611 InvalidateRect(hwnd, NULL, TRUE);
1613 return 0;
1617 static LRESULT
1618 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
1620 TAB_INFO *infoPtr;
1621 TEXTMETRICA fontMetrics;
1622 HDC hdc;
1623 HFONT hOldFont;
1625 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
1627 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
1629 infoPtr->uNumItem = 0;
1630 infoPtr->hFont = 0;
1631 infoPtr->items = 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,
1648 hwnd, 0, 0, 0);
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
1666 * a font.
1668 hdc = GetDC(hwnd);
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);
1691 return 0;
1694 static LRESULT
1695 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
1697 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1698 INT iItem;
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);
1715 return 0;
1718 static LRESULT WINAPI
1719 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1721 switch (uMsg)
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);
1732 case TCM_GETITEMA:
1733 return TAB_GetItemA (hwnd, wParam, lParam);
1735 case TCM_GETITEMW:
1736 FIXME("Unimplemented msg TCM_GETITEMW\n");
1737 return 0;
1739 case TCM_SETITEMA:
1740 return TAB_SetItemA (hwnd, wParam, lParam);
1742 case TCM_SETITEMW:
1743 FIXME("Unimplemented msg TCM_SETITEMW\n");
1744 return 0;
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);
1755 case TCM_GETCURSEL:
1756 return TAB_GetCurSel (hwnd);
1758 case TCM_HITTEST:
1759 return TAB_HitTest (hwnd, wParam, lParam);
1761 case TCM_SETCURSEL:
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_INSERTITEMW\n");
1769 return 0;
1771 case TCM_SETITEMEXTRA:
1772 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
1773 return 0;
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");
1783 return 0;
1785 case TCM_SETPADDING:
1786 FIXME("Unimplemented msg TCM_SETPADDING\n");
1787 return 0;
1789 case TCM_GETROWCOUNT:
1790 FIXME("Unimplemented msg TCM_GETROWCOUNT\n");
1791 return 0;
1793 case TCM_GETUNICODEFORMAT:
1794 FIXME("Unimplemented msg TCM_GETUNICODEFORMAT\n");
1795 return 0;
1797 case TCM_SETUNICODEFORMAT:
1798 FIXME("Unimplemented msg TCM_SETUNICODEFORMAT\n");
1799 return 0;
1801 case TCM_HIGHLIGHTITEM:
1802 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
1803 return 0;
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");
1819 return 0;
1821 case TCM_DESELECTALL:
1822 FIXME("Unimplemented msg TCM_DESELECTALL\n");
1823 return 0;
1825 case TCM_GETEXTENDEDSTYLE:
1826 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
1827 return 0;
1829 case TCM_SETEXTENDEDSTYLE:
1830 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
1831 return 0;
1833 case WM_GETFONT:
1834 return TAB_GetFont (hwnd, wParam, lParam);
1836 case WM_SETFONT:
1837 return TAB_SetFont (hwnd, wParam, lParam);
1839 case WM_CREATE:
1840 return TAB_Create (hwnd, wParam, lParam);
1842 case WM_NCDESTROY:
1843 return TAB_Destroy (hwnd, wParam, lParam);
1845 case WM_GETDLGCODE:
1846 return DLGC_WANTARROWS | DLGC_WANTCHARS;
1848 case WM_LBUTTONDOWN:
1849 return TAB_LButtonDown (hwnd, wParam, lParam);
1851 case WM_LBUTTONUP:
1852 return TAB_LButtonUp (hwnd, wParam, lParam);
1854 case WM_RBUTTONDOWN:
1855 return TAB_RButtonDown (hwnd, wParam, lParam);
1857 case WM_MOUSEMOVE:
1858 return TAB_MouseMove (hwnd, wParam, lParam);
1860 case WM_ERASEBKGND:
1861 return TAB_EraseBackground (hwnd, (HDC)wParam);
1863 case WM_PAINT:
1864 return TAB_Paint (hwnd, wParam);
1866 case WM_SIZE:
1867 return TAB_Size (hwnd, wParam, lParam);
1869 case WM_SETREDRAW:
1870 return TAB_SetRedraw (hwnd, wParam);
1872 case WM_HSCROLL:
1873 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
1875 case WM_KILLFOCUS:
1876 case WM_SETFOCUS:
1877 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
1879 case WM_KEYUP:
1880 return TAB_KeyUp(hwnd, wParam);
1882 default:
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);
1889 return 0;
1893 VOID
1894 TAB_Register (void)
1896 WNDCLASSA wndClass;
1898 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1899 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
1900 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
1901 wndClass.cbClsExtra = 0;
1902 wndClass.cbWndExtra = sizeof(TAB_INFO *);
1903 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
1904 wndClass.hbrBackground = (HBRUSH)NULL;
1905 wndClass.lpszClassName = WC_TABCONTROLA;
1907 RegisterClassA (&wndClass);
1911 VOID
1912 TAB_Unregister (void)
1914 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);