X11DRV_SetFocus: really don't mess with focus for managed windows.
[wine/multimedia.git] / dlls / comctl32 / tab.c
blob383d9f2259e938bf73ad11b7e8cbc235d67ae4c5
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"
21 #include "win.h"
23 DEFAULT_DEBUG_CHANNEL(tab)
25 /******************************************************************************
26 * Positioning constants
28 #define SELECTED_TAB_OFFSET 2
29 #define HORIZONTAL_ITEM_PADDING 5
30 #define VERTICAL_ITEM_PADDING 3
31 #define ROUND_CORNER_SIZE 2
32 #define FOCUS_RECT_HOFFSET 2
33 #define FOCUS_RECT_VOFFSET 1
34 #define DISPLAY_AREA_PADDINGX 2
35 #define DISPLAY_AREA_PADDINGY 2
36 #define CONTROL_BORDER_SIZEX 2
37 #define CONTROL_BORDER_SIZEY 2
38 #define BUTTON_SPACINGX 10
39 #define DEFAULT_TAB_WIDTH 96
41 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
43 /******************************************************************************
44 * Prototypes
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);
50 static BOOL
51 TAB_SendSimpleNotify (HWND hwnd, UINT code)
53 NMHDR nmhdr;
55 nmhdr.hwndFrom = hwnd;
56 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
57 nmhdr.code = code;
59 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
60 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
64 static VOID
65 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
66 WPARAM wParam, LPARAM lParam)
68 MSG msg;
70 msg.hwnd = hwndMsg;
71 msg.message = uMsg;
72 msg.wParam = wParam;
73 msg.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);
83 static LRESULT
84 TAB_GetCurSel (HWND hwnd)
86 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
88 return infoPtr->iSelected;
91 static LRESULT
92 TAB_GetCurFocus (HWND hwnd)
94 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
96 return infoPtr->uFocus;
99 static LRESULT
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;
109 static LRESULT
110 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
112 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
113 INT iItem=(INT) wParam;
114 INT prevItem;
116 prevItem=-1;
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);
123 return prevItem;
126 static LRESULT
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");
136 } else {
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);
148 return 0;
151 static LRESULT
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;
158 return 0;
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(
171 HWND hwnd,
172 TAB_INFO* infoPtr,
173 INT itemIndex,
174 RECT* itemRect,
175 RECT* selectedRect)
177 RECT tmpItemRect;
180 * Perform a sanity check and a trivial visibility check.
182 if ( (infoPtr->uNumItem <=0) ||
183 (itemIndex >= infoPtr->uNumItem) ||
184 (itemIndex < infoPtr->leftmostVisible) )
185 return FALSE;
188 * Avoid special cases in this procedure by assigning the "out"
189 * parameters if the caller didn't supply them
191 if (itemRect==NULL)
192 itemRect = &tmpItemRect;
195 * Retrieve the unmodified item rect.
197 *itemRect = infoPtr->items[itemIndex].rect;
200 * "scroll" it to make sure the item at the very left of the
201 * tab control is the leftmost visible tab.
203 OffsetRect(itemRect,
204 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
208 * Move the rectangle so the first item is slightly offset from
209 * the left of the tab control.
211 OffsetRect(itemRect,
212 SELECTED_TAB_OFFSET,
217 * Now, calculate the position of the item as if it were selected.
219 if (selectedRect!=NULL)
221 CopyRect(selectedRect, itemRect);
224 * The rectangle of a selected item is a bit wider.
226 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
229 * If it also a bit higher.
231 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
233 selectedRect->top -=2; /* the border is thicker on the bottom */
234 selectedRect->bottom +=SELECTED_TAB_OFFSET;
236 else
238 selectedRect->top -=SELECTED_TAB_OFFSET;
239 selectedRect->bottom+=1;
243 return TRUE;
246 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
248 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
249 (LPRECT)lParam, (LPRECT)NULL);
252 /******************************************************************************
253 * TAB_KeyUp
255 * This method is called to handle keyboard input
257 static LRESULT TAB_KeyUp(
258 HWND hwnd,
259 WPARAM keyCode)
261 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
262 int newItem = -1;
264 switch (keyCode)
266 case VK_LEFT:
267 newItem = infoPtr->uFocus-1;
268 break;
269 case VK_RIGHT:
270 newItem = infoPtr->uFocus+1;
271 break;
275 * If we changed to a valid item, change the selection
277 if ( (newItem >= 0) &&
278 (newItem < infoPtr->uNumItem) &&
279 (infoPtr->uFocus != newItem) )
281 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
283 infoPtr->iSelected = newItem;
284 infoPtr->uFocus = newItem;
285 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
287 TAB_EnsureSelectionVisible(hwnd, infoPtr);
288 TAB_InvalidateTabArea(hwnd, infoPtr);
292 return 0;
295 /******************************************************************************
296 * TAB_FocusChanging
298 * This method is called whenever the focus goes in or out of this control
299 * it is used to update the visual state of the control.
301 static LRESULT TAB_FocusChanging(
302 HWND hwnd,
303 UINT uMsg,
304 WPARAM wParam,
305 LPARAM lParam)
307 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
308 RECT selectedRect;
309 BOOL isVisible;
312 * Get the rectangle for the item.
314 isVisible = TAB_InternalGetItemRect(hwnd,
315 infoPtr,
316 infoPtr->uFocus,
317 NULL,
318 &selectedRect);
321 * If the rectangle is not completely invisible, invalidate that
322 * portion of the window.
324 if (isVisible)
326 InvalidateRect(hwnd, &selectedRect, TRUE);
330 * Don't otherwise disturb normal behavior.
332 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
335 static HWND TAB_InternalHitTest (
336 HWND hwnd,
337 TAB_INFO* infoPtr,
338 POINT pt,
339 UINT* flags)
342 RECT rect;
343 int iCount;
345 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
347 TAB_InternalGetItemRect(hwnd,
348 infoPtr,
349 iCount,
350 &rect,
351 NULL);
353 if (PtInRect (&rect, pt))
355 *flags = TCHT_ONITEM;
356 return iCount;
360 *flags=TCHT_NOWHERE;
361 return -1;
364 static LRESULT
365 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
367 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
368 LPTCHITTESTINFO lptest=(LPTCHITTESTINFO) lParam;
370 return TAB_InternalHitTest (hwnd, infoPtr,lptest->pt,&lptest->flags);
374 static LRESULT
375 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
377 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
378 POINT pt;
379 INT newItem,dummy;
381 if (infoPtr->hwndToolTip)
382 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
383 WM_LBUTTONDOWN, wParam, lParam);
385 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
386 SetFocus (hwnd);
389 if (infoPtr->hwndToolTip)
390 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
391 WM_LBUTTONDOWN, wParam, lParam);
393 pt.x = (INT)LOWORD(lParam);
394 pt.y = (INT)HIWORD(lParam);
396 newItem=TAB_InternalHitTest (hwnd, infoPtr,pt,&dummy);
398 TRACE("On Tab, item %d\n", newItem);
400 if ( (newItem!=-1) &&
401 (infoPtr->iSelected != newItem) )
403 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE)
405 infoPtr->iSelected = newItem;
406 infoPtr->uFocus = newItem;
407 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
409 TAB_EnsureSelectionVisible(hwnd, infoPtr);
411 TAB_InvalidateTabArea(hwnd, infoPtr);
414 return 0;
417 static LRESULT
418 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
420 TAB_SendSimpleNotify(hwnd, NM_CLICK);
422 return 0;
425 static LRESULT
426 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
428 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
429 return 0;
432 static LRESULT
433 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
435 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
437 if (infoPtr->hwndToolTip)
438 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
439 WM_LBUTTONDOWN, wParam, lParam);
440 return 0;
443 /******************************************************************************
444 * TAB_AdjustRect
446 * Calculates the tab control's display area given the windows rectangle or
447 * the window rectangle given the requested display rectangle.
449 static LRESULT TAB_AdjustRect(
450 HWND hwnd,
451 WPARAM fLarger,
452 LPRECT prc)
454 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
456 if (fLarger)
459 * Go from display rectangle
463 * Add the height of the tabs.
465 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
466 prc->bottom += infoPtr->tabHeight;
467 else
468 prc->top -= infoPtr->tabHeight;
471 * Inflate the rectangle for the padding
473 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
476 * Inflate for the border
478 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
480 else
483 * Go from window rectangle.
487 * Deflate the rectangle for the border
489 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
492 * Deflate the rectangle for the padding
494 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
497 * Remove the height of the tabs.
499 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
500 prc->bottom -= infoPtr->tabHeight;
501 else
502 prc->top += infoPtr->tabHeight;
506 return 0;
509 /******************************************************************************
510 * TAB_OnHScroll
512 * This method will handle the notification from the scroll control and
513 * perform the scrolling operation on the tab control.
515 static LRESULT TAB_OnHScroll(
516 HWND hwnd,
517 int nScrollCode,
518 int nPos,
519 HWND hwndScroll)
521 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
523 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
525 if(nPos < infoPtr->leftmostVisible)
526 infoPtr->leftmostVisible--;
527 else
528 infoPtr->leftmostVisible++;
530 TAB_InvalidateTabArea(hwnd, infoPtr);
531 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
532 MAKELONG(infoPtr->leftmostVisible, 0));
535 return 0;
538 /******************************************************************************
539 * TAB_SetupScroling
541 * This method will check the current scrolling state and make sure the
542 * scrolling control is displayed (or not).
544 static void TAB_SetupScrolling(
545 HWND hwnd,
546 TAB_INFO* infoPtr,
547 const RECT* clientRect)
549 INT maxRange = 0;
550 if (infoPtr->needsScrolling)
552 RECT controlPos;
553 INT vsize, tabwidth;
556 * Calculate the position of the scroll control.
558 controlPos.right = clientRect->right;
559 controlPos.left = controlPos.right - 2*GetSystemMetrics(SM_CXHSCROLL);
561 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
563 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
564 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
566 else
568 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
569 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
573 * If we don't have a scroll control yet, we want to create one.
574 * If we have one, we want to make sure it's positioned right.
576 if (infoPtr->hwndUpDown==0)
579 * I use a scrollbar since it seems to be more stable than the Updown
580 * control.
582 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
584 WS_VISIBLE | WS_CHILD | UDS_HORZ,
585 controlPos.left, controlPos.top,
586 controlPos.right - controlPos.left,
587 controlPos.bottom - controlPos.top,
588 hwnd,
589 (HMENU)NULL,
590 (HINSTANCE)NULL,
591 NULL);
593 else
595 SetWindowPos(infoPtr->hwndUpDown,
596 (HWND)NULL,
597 controlPos.left, controlPos.top,
598 controlPos.right - controlPos.left,
599 controlPos.bottom - controlPos.top,
600 SWP_SHOWWINDOW | SWP_NOZORDER);
603 /* Now calculate upper limit of the updown control range.
604 * We do this by calculating how many tabs will be offscreen when the
605 * last tab is visible.
607 if(infoPtr->uNumItem)
609 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
610 maxRange = infoPtr->uNumItem;
611 tabwidth = infoPtr->items[maxRange-1].rect.right;
613 for(; maxRange > 0; maxRange--)
615 if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
616 break;
619 if(maxRange == infoPtr->uNumItem)
620 maxRange--;
623 else
626 * If we once had a scroll control... hide it.
628 if (infoPtr->hwndUpDown!=0)
630 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
633 if (infoPtr->hwndUpDown)
634 SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
637 /******************************************************************************
638 * TAB_SetItemBounds
640 * This method will calculate the position rectangles of all the items in the
641 * control. The rectangle calculated starts at 0 for the first item in the
642 * list and ignores scrolling and selection.
643 * It also uses the current font to determine the height of the tab row and
644 * it checks if all the tabs fit in the client area of the window. If they
645 * dont, a scrolling control is added.
647 static void TAB_SetItemBounds (HWND hwnd)
649 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
650 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
651 TEXTMETRICA fontMetrics;
652 INT curItem;
653 INT curItemLeftPos;
654 HFONT hFont, hOldFont;
655 HDC hdc;
656 RECT clientRect;
657 SIZE size;
660 * We need to get text information so we need a DC and we need to select
661 * a font.
663 hdc = GetDC(hwnd);
665 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
666 hOldFont = SelectObject (hdc, hFont);
669 * We will base the rectangle calculations on the client rectangle
670 * of the control.
672 GetClientRect(hwnd, &clientRect);
675 * The leftmost item will be "0" aligned
677 curItemLeftPos = 0;
679 if ( !(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet) )
681 int item_height;
682 int icon_height = 0;
685 * Use the current font to determine the height of a tab.
687 GetTextMetricsA(hdc, &fontMetrics);
690 * Get the icon height
692 if (infoPtr->himl)
693 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
696 * Take the highest between font or icon
698 if (fontMetrics.tmHeight > icon_height)
699 item_height = fontMetrics.tmHeight;
700 else
701 item_height = icon_height;
704 * Make sure there is enough space for the letters + icon + growing the
705 * selected item + extra space for the selected item.
707 infoPtr->tabHeight = item_height + 2*VERTICAL_ITEM_PADDING +
708 SELECTED_TAB_OFFSET;
711 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
714 * Calculate the vertical position of the tab
716 if (lStyle & TCS_BOTTOM)
718 infoPtr->items[curItem].rect.bottom = clientRect.bottom -
719 SELECTED_TAB_OFFSET;
720 infoPtr->items[curItem].rect.top = clientRect.bottom -
721 infoPtr->tabHeight;
723 else
725 infoPtr->items[curItem].rect.top = clientRect.top +
726 SELECTED_TAB_OFFSET;
727 infoPtr->items[curItem].rect.bottom = clientRect.top +
728 infoPtr->tabHeight;
732 * Set the leftmost position of the tab.
734 infoPtr->items[curItem].rect.left = curItemLeftPos;
736 if ( (lStyle & TCS_FIXEDWIDTH) || ((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
738 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
739 infoPtr->tabWidth +
740 2*HORIZONTAL_ITEM_PADDING;
742 else
744 int icon_width = 0;
745 int num = 2;
748 * Calculate how wide the tab is depending on the text it contains
750 GetTextExtentPoint32A(hdc, infoPtr->items[curItem].pszText,
751 lstrlenA(infoPtr->items[curItem].pszText), &size);
754 * Add the icon width
756 if (infoPtr->himl)
758 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
759 num++;
762 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
763 size.cx + icon_width +
764 num*HORIZONTAL_ITEM_PADDING;
767 TRACE("TextSize: %i\n ", size.cx);
768 TRACE("Rect: T %i, L %i, B %i, R %i\n",
769 infoPtr->items[curItem].rect.top,
770 infoPtr->items[curItem].rect.left,
771 infoPtr->items[curItem].rect.bottom,
772 infoPtr->items[curItem].rect.right);
775 * The leftmost position of the next item is the rightmost position
776 * of this one.
778 if (lStyle & TCS_BUTTONS)
779 curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX;
780 else
781 curItemLeftPos = infoPtr->items[curItem].rect.right;
785 * Check if we need a scrolling control.
787 infoPtr->needsScrolling = (curItemLeftPos + (2*SELECTED_TAB_OFFSET) >
788 clientRect.right);
790 /* Don't need scrolling, then update infoPtr->leftmostVisible */
791 if(!infoPtr->needsScrolling)
792 infoPtr->leftmostVisible = 0;
794 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
797 * Cleanup
799 SelectObject (hdc, hOldFont);
800 ReleaseDC (hwnd, hdc);
803 /******************************************************************************
804 * TAB_DrawItem
806 * This method is used to draw a single tab into the tab control.
808 static void TAB_DrawItem(
809 HWND hwnd,
810 HDC hdc,
811 INT iItem)
813 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
814 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
815 RECT itemRect;
816 RECT selectedRect;
817 BOOL isVisible;
818 RECT r;
821 * Get the rectangle for the item.
823 isVisible = TAB_InternalGetItemRect(hwnd,
824 infoPtr,
825 iItem,
826 &itemRect,
827 &selectedRect);
829 if (isVisible)
831 HBRUSH hbr = CreateSolidBrush (GetSysColor(COLOR_BTNFACE));
832 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
833 HPEN hbPen = GetSysColorPen (COLOR_BTNSHADOW);
834 HPEN hsdPen = GetSysColorPen (COLOR_BTNTEXT);
835 HPEN hfocusPen = CreatePen(PS_DOT, 1, GetSysColor(COLOR_BTNTEXT));
836 HPEN holdPen;
837 INT oldBkMode;
838 INT cx,cy;
839 BOOL deleteBrush = TRUE;
841 if (lStyle & TCS_BUTTONS)
844 * Get item rectangle.
846 r = itemRect;
848 holdPen = SelectObject (hdc, hwPen);
850 if (iItem == infoPtr->iSelected)
853 * Background color.
855 if (!((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
857 COLORREF bk = GetSysColor(COLOR_3DHILIGHT);
858 DeleteObject(hbr);
859 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
860 SetTextColor(hdc, GetSysColor(COLOR_3DFACE));
861 SetBkColor(hdc, bk);
863 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
864 * we better use 0x55aa bitmap brush to make scrollbar's background
865 * look different from the window background.
867 if (bk == GetSysColor(COLOR_WINDOW))
868 hbr = CACHE_GetPattern55AABrush();
870 deleteBrush = FALSE;
874 * Erase the background.
876 FillRect(hdc, &r, hbr);
879 * Draw the tab now.
880 * The rectangles calculated exclude the right and bottom
881 * borders of the rectangle. To simply the following code, those
882 * borders are shaved-off beforehand.
884 r.right--;
885 r.bottom--;
887 /* highlight */
888 MoveToEx (hdc, r.left, r.bottom, NULL);
889 LineTo (hdc, r.right, r.bottom);
890 LineTo (hdc, r.right, r.top);
892 /* shadow */
893 SelectObject(hdc, hbPen);
894 LineTo (hdc, r.left, r.top);
895 LineTo (hdc, r.left, r.bottom);
897 else
900 * Erase the background.
902 FillRect(hdc, &r, hbr);
904 /* highlight */
905 MoveToEx (hdc, r.left, r.bottom, NULL);
906 LineTo (hdc, r.left, r.top);
907 LineTo (hdc, r.right, r.top);
909 /* shadow */
910 SelectObject(hdc, hbPen);
911 LineTo (hdc, r.right, r.bottom);
912 LineTo (hdc, r.left, r.bottom);
915 else
918 * Background color.
920 DeleteObject(hbr);
921 hbr = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
924 * We draw a rectangle of different sizes depending on the selection
925 * state.
927 if (iItem == infoPtr->iSelected)
928 r = selectedRect;
929 else
930 r = itemRect;
933 * Erase the background.
934 * This is necessary when drawing the selected item since it is larger
935 * than the others, it might overlap with stuff already drawn by the
936 * other tabs
938 FillRect(hdc, &r, hbr);
941 * Draw the tab now.
942 * The rectangles calculated exclude the right and bottom
943 * borders of the rectangle. To simply the following code, those
944 * borders are shaved-off beforehand.
946 r.right--;
947 r.bottom--;
949 holdPen = SelectObject (hdc, hwPen);
951 if (lStyle & TCS_BOTTOM)
953 /* highlight */
954 MoveToEx (hdc, r.left, r.top, NULL);
955 LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE);
956 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom);
958 /* shadow */
959 SelectObject(hdc, hbPen);
960 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom);
961 LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE);
962 LineTo (hdc, r.right, r.top);
964 else
966 /* highlight */
967 MoveToEx (hdc, r.left, r.bottom, NULL);
968 LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE);
969 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top);
970 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top);
972 /* shadow */
973 SelectObject(hdc, hbPen);
974 LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE);
975 LineTo (hdc, r.right, r.bottom);
980 * Text pen
982 SelectObject(hdc, hsdPen);
984 oldBkMode = SetBkMode(hdc, TRANSPARENT);
985 SetTextColor (hdc, COLOR_BTNTEXT);
988 * Deflate the rectangle to acount for the padding
990 InflateRect(&r, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
993 * if owner draw, tell the owner to draw
995 if ( (lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd) )
997 DRAWITEMSTRUCT dis;
998 WND *pwndPtr;
999 UINT id;
1002 * get the control id
1004 pwndPtr = WIN_FindWndPtr( hwnd );
1005 id = pwndPtr->wIDmenu;
1006 WIN_ReleaseWndPtr(pwndPtr);
1009 * put together the DRAWITEMSTRUCT
1011 dis.CtlType = ODT_TAB;
1012 dis.CtlID = id;
1013 dis.itemID = iItem;
1014 dis.itemAction = ODA_DRAWENTIRE;
1015 if ( iItem == infoPtr->iSelected )
1016 dis.itemState = ODS_SELECTED;
1017 else
1018 dis.itemState = 0;
1019 dis.hwndItem = hwnd; /* */
1020 dis.hDC = hdc;
1021 dis.rcItem = r; /* */
1022 dis.itemData = infoPtr->items[iItem].lParam;
1025 * send the draw message
1027 SendMessageA( GetParent(hwnd), WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1029 else
1032 * If not owner draw, then do the drawing ourselves.
1034 * Draw the icon.
1036 if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE) )
1038 ImageList_Draw (infoPtr->himl, infoPtr->items[iItem].iImage, hdc,
1039 r.left, r.top+1, ILD_NORMAL);
1040 ImageList_GetIconSize (infoPtr->himl, &cx, &cy);
1041 r.left+=(cx + HORIZONTAL_ITEM_PADDING);
1045 * Draw the text;
1047 DrawTextA(hdc,
1048 infoPtr->items[iItem].pszText,
1049 lstrlenA(infoPtr->items[iItem].pszText),
1050 &r,
1051 DT_LEFT|DT_SINGLELINE|DT_VCENTER);
1055 * Draw the focus rectangle
1057 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
1058 (GetFocus() == hwnd) &&
1059 (iItem == infoPtr->uFocus) )
1061 InflateRect(&r, FOCUS_RECT_HOFFSET, FOCUS_RECT_VOFFSET);
1063 SelectObject(hdc, hfocusPen);
1065 MoveToEx (hdc, r.left, r.top, NULL);
1066 LineTo (hdc, r.right-1, r.top);
1067 LineTo (hdc, r.right-1, r.bottom -1);
1068 LineTo (hdc, r.left, r.bottom -1);
1069 LineTo (hdc, r.left, r.top);
1073 * Cleanup
1075 SetBkMode(hdc, oldBkMode);
1076 SelectObject(hdc, holdPen);
1077 DeleteObject(hfocusPen);
1078 if (deleteBrush) DeleteObject(hbr);
1082 /******************************************************************************
1083 * TAB_DrawBorder
1085 * This method is used to draw the raised border around the tab control
1086 * "content" area.
1088 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
1090 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1091 HPEN htmPen;
1092 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
1093 HPEN hbPen = GetSysColorPen (COLOR_3DDKSHADOW);
1094 HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW);
1095 RECT rect;
1097 GetClientRect (hwnd, &rect);
1100 * Adjust for the style
1102 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1104 rect.bottom -= infoPtr->tabHeight;
1106 else
1108 rect.top += infoPtr->tabHeight;
1112 * Shave-off the right and bottom margins (exluded in the
1113 * rect)
1115 rect.right--;
1116 rect.bottom--;
1118 /* highlight */
1119 htmPen = SelectObject (hdc, hwPen);
1121 MoveToEx (hdc, rect.left, rect.bottom, NULL);
1122 LineTo (hdc, rect.left, rect.top);
1123 LineTo (hdc, rect.right, rect.top);
1125 /* Dark Shadow */
1126 SelectObject (hdc, hbPen);
1127 LineTo (hdc, rect.right, rect.bottom );
1128 LineTo (hdc, rect.left, rect.bottom);
1130 /* shade */
1131 SelectObject (hdc, hShade );
1132 MoveToEx (hdc, rect.right-1, rect.top, NULL);
1133 LineTo (hdc, rect.right-1, rect.bottom-1);
1134 LineTo (hdc, rect.left, rect.bottom-1);
1136 SelectObject(hdc, htmPen);
1139 /******************************************************************************
1140 * TAB_Refresh
1142 * This method repaints the tab control..
1144 static void TAB_Refresh (HWND hwnd, HDC hdc)
1146 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1147 HFONT hOldFont;
1148 INT i;
1150 if (!infoPtr->DoRedraw)
1151 return;
1153 hOldFont = SelectObject (hdc, infoPtr->hFont);
1155 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
1157 for (i = 0; i < infoPtr->uNumItem; i++)
1159 TAB_DrawItem (hwnd, hdc, i);
1162 else
1165 * Draw all the non selected item first.
1167 for (i = 0; i < infoPtr->uNumItem; i++)
1169 if (i != infoPtr->iSelected)
1170 TAB_DrawItem (hwnd, hdc, i);
1174 * Now, draw the border, draw it before the selected item
1175 * since the selected item overwrites part of the border.
1177 TAB_DrawBorder (hwnd, hdc);
1180 * Then, draw the selected item
1182 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
1185 * If we haven't set the current focus yet, set it now.
1186 * Only happens when we first paint the tab controls.
1188 if (infoPtr->uFocus == -1)
1189 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
1192 SelectObject (hdc, hOldFont);
1195 static LRESULT
1196 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
1198 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1200 infoPtr->DoRedraw=(BOOL) wParam;
1201 return 0;
1204 static LRESULT TAB_EraseBackground(
1205 HWND hwnd,
1206 HDC givenDC)
1208 HDC hdc;
1209 RECT clientRect;
1211 HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1213 hdc = givenDC ? givenDC : GetDC(hwnd);
1215 GetClientRect(hwnd, &clientRect);
1217 FillRect(hdc, &clientRect, brush);
1219 if (givenDC==0)
1220 ReleaseDC(hwnd, hdc);
1222 DeleteObject(brush);
1224 return 0;
1227 /******************************************************************************
1228 * TAB_EnsureSelectionVisible
1230 * This method will make sure that the current selection is completely
1231 * visible by scrolling until it is.
1233 static void TAB_EnsureSelectionVisible(
1234 HWND hwnd,
1235 TAB_INFO* infoPtr)
1237 INT iSelected = infoPtr->iSelected;
1240 * Do the trivial cases first.
1242 if ( (!infoPtr->needsScrolling) ||
1243 (infoPtr->hwndUpDown==0) )
1244 return;
1246 if (infoPtr->leftmostVisible >= iSelected)
1248 infoPtr->leftmostVisible = iSelected;
1250 else
1252 RECT r;
1253 INT width, i;
1255 * Calculate the part of the client area that is visible.
1257 GetClientRect(hwnd, &r);
1258 width = r.right;
1260 GetClientRect(infoPtr->hwndUpDown, &r);
1261 width -= r.right;
1263 if ((infoPtr->items[iSelected].rect.right -
1264 infoPtr->items[iSelected].rect.left) >= width )
1266 /* Special case: width of selected item is greater than visible
1267 * part of control.
1269 infoPtr->leftmostVisible = iSelected;
1271 else
1273 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
1275 if ((infoPtr->items[iSelected].rect.right -
1276 infoPtr->items[i].rect.left) < width)
1277 break;
1279 infoPtr->leftmostVisible = i;
1283 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
1284 MAKELONG(infoPtr->leftmostVisible, 0));
1287 /******************************************************************************
1288 * TAB_InvalidateTabArea
1290 * This method will invalidate the portion of the control that contains the
1291 * tabs. It is called when the state of the control changes and needs
1292 * to be redisplayed
1294 static void TAB_InvalidateTabArea(
1295 HWND hwnd,
1296 TAB_INFO* infoPtr)
1298 RECT clientRect;
1300 GetClientRect(hwnd, &clientRect);
1302 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1304 clientRect.top = clientRect.bottom - (infoPtr->tabHeight + 3);
1306 else
1308 clientRect.bottom = clientRect.top + (infoPtr->tabHeight + 1);
1311 InvalidateRect(hwnd, &clientRect, TRUE);
1314 static LRESULT
1315 TAB_Paint (HWND hwnd, WPARAM wParam)
1317 HDC hdc;
1318 PAINTSTRUCT ps;
1320 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
1321 TAB_Refresh (hwnd, hdc);
1323 if(!wParam)
1324 EndPaint (hwnd, &ps);
1326 return 0;
1329 static LRESULT
1330 TAB_InsertItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1332 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1333 TCITEMA *pti;
1334 INT iItem, len;
1335 RECT rect;
1337 GetClientRect (hwnd, &rect);
1338 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
1339 rect.top, rect.left, rect.bottom, rect.right);
1341 pti = (TCITEMA *)lParam;
1342 iItem = (INT)wParam;
1344 if (iItem < 0) return -1;
1345 if (iItem > infoPtr->uNumItem)
1346 iItem = infoPtr->uNumItem;
1348 if (infoPtr->uNumItem == 0) {
1349 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
1350 infoPtr->uNumItem++;
1351 infoPtr->iSelected = 0;
1353 else {
1354 TAB_ITEM *oldItems = infoPtr->items;
1356 infoPtr->uNumItem++;
1357 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
1359 /* pre insert copy */
1360 if (iItem > 0) {
1361 memcpy (&infoPtr->items[0], &oldItems[0],
1362 iItem * sizeof(TAB_ITEM));
1365 /* post insert copy */
1366 if (iItem < infoPtr->uNumItem - 1) {
1367 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
1368 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
1372 if (iItem <= infoPtr->iSelected)
1373 infoPtr->iSelected++;
1375 COMCTL32_Free (oldItems);
1378 infoPtr->items[iItem].mask = pti->mask;
1379 if (pti->mask & TCIF_TEXT) {
1380 len = lstrlenA (pti->pszText);
1381 infoPtr->items[iItem].pszText = COMCTL32_Alloc (len+1);
1382 lstrcpyA (infoPtr->items[iItem].pszText, pti->pszText);
1383 infoPtr->items[iItem].cchTextMax = pti->cchTextMax;
1386 if (pti->mask & TCIF_IMAGE)
1387 infoPtr->items[iItem].iImage = pti->iImage;
1389 if (pti->mask & TCIF_PARAM)
1390 infoPtr->items[iItem].lParam = pti->lParam;
1392 TAB_SetItemBounds(hwnd);
1393 TAB_InvalidateTabArea(hwnd, infoPtr);
1395 TRACE("[%04x]: added item %d '%s'\n",
1396 hwnd, iItem, infoPtr->items[iItem].pszText);
1398 return iItem;
1401 static LRESULT
1402 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
1404 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1405 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1406 LONG lResult = 0;
1408 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
1410 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
1411 infoPtr->tabWidth = (INT)LOWORD(lParam);
1412 infoPtr->tabHeight = (INT)HIWORD(lParam);
1414 infoPtr->fSizeSet = TRUE;
1416 return lResult;
1419 static LRESULT
1420 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1422 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1423 TCITEMA *tabItem;
1424 TAB_ITEM *wineItem;
1425 INT iItem,len;
1427 iItem=(INT) wParam;
1428 tabItem=(LPTCITEMA ) lParam;
1429 TRACE("%d %p\n",iItem, tabItem);
1430 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
1432 wineItem=& infoPtr->items[iItem];
1434 if (tabItem->mask & TCIF_IMAGE)
1435 wineItem->iImage=tabItem->iImage;
1437 if (tabItem->mask & TCIF_PARAM)
1438 wineItem->lParam=tabItem->lParam;
1440 if (tabItem->mask & TCIF_RTLREADING)
1441 FIXME("TCIF_RTLREADING\n");
1443 if (tabItem->mask & TCIF_STATE)
1444 wineItem->dwState=tabItem->dwState;
1446 if (tabItem->mask & TCIF_TEXT) {
1447 len=lstrlenA (tabItem->pszText);
1448 if (len>wineItem->cchTextMax)
1449 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
1450 lstrcpyA (wineItem->pszText, tabItem->pszText);
1454 * Update and repaint tabs.
1456 TAB_SetItemBounds(hwnd);
1457 TAB_InvalidateTabArea(hwnd,infoPtr);
1459 return TRUE;
1462 static LRESULT
1463 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1465 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1467 return infoPtr->uNumItem;
1471 static LRESULT
1472 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1474 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1475 TCITEMA *tabItem;
1476 TAB_ITEM *wineItem;
1477 INT iItem;
1479 iItem=(INT) wParam;
1480 tabItem=(LPTCITEMA) lParam;
1481 TRACE("\n");
1482 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
1484 wineItem=& infoPtr->items[iItem];
1486 if (tabItem->mask & TCIF_IMAGE)
1487 tabItem->iImage=wineItem->iImage;
1489 if (tabItem->mask & TCIF_PARAM)
1490 tabItem->lParam=wineItem->lParam;
1492 if (tabItem->mask & TCIF_RTLREADING)
1493 FIXME("TCIF_RTLREADING\n");
1495 if (tabItem->mask & TCIF_STATE)
1496 tabItem->dwState=wineItem->dwState;
1498 if (tabItem->mask & TCIF_TEXT)
1499 lstrcpynA (tabItem->pszText, wineItem->pszText, tabItem->cchTextMax);
1501 return TRUE;
1504 static LRESULT
1505 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1507 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1508 INT iItem = (INT) wParam;
1509 BOOL bResult = FALSE;
1511 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
1513 TAB_ITEM *oldItems = infoPtr->items;
1515 infoPtr->uNumItem--;
1516 infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
1518 if (iItem > 0)
1519 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
1521 if (iItem < infoPtr->uNumItem)
1522 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
1523 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
1525 COMCTL32_Free (oldItems);
1528 * Readjust the selected index.
1530 if ((iItem == infoPtr->iSelected) && (iItem > 0))
1531 infoPtr->iSelected--;
1533 if (iItem < infoPtr->iSelected)
1534 infoPtr->iSelected--;
1536 if (infoPtr->uNumItem == 0)
1537 infoPtr->iSelected = -1;
1540 * Reposition and repaint tabs.
1542 TAB_SetItemBounds(hwnd);
1543 TAB_InvalidateTabArea(hwnd,infoPtr);
1545 bResult = TRUE;
1548 return bResult;
1551 static LRESULT
1552 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
1554 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1556 COMCTL32_Free (infoPtr->items);
1557 infoPtr->uNumItem = 0;
1558 infoPtr->iSelected = -1;
1560 TAB_SetItemBounds(hwnd);
1561 TAB_InvalidateTabArea(hwnd,infoPtr);
1562 return TRUE;
1566 static LRESULT
1567 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1569 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1571 TRACE("\n");
1572 return (LRESULT)infoPtr->hFont;
1575 static LRESULT
1576 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1579 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1581 TRACE("%x %lx\n",wParam, lParam);
1583 infoPtr->hFont = (HFONT)wParam;
1585 TAB_SetItemBounds(hwnd);
1587 TAB_InvalidateTabArea(hwnd, infoPtr);
1589 return 0;
1593 static LRESULT
1594 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1596 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1598 TRACE("\n");
1599 return (LRESULT)infoPtr->himl;
1602 static LRESULT
1603 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1605 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1606 HIMAGELIST himlPrev;
1608 TRACE("\n");
1609 himlPrev = infoPtr->himl;
1610 infoPtr->himl= (HIMAGELIST)lParam;
1611 return (LRESULT)himlPrev;
1615 static LRESULT
1616 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1619 /* I'm not really sure what the following code was meant to do.
1620 This is what it is doing:
1621 When WM_SIZE is sent with SIZE_RESTORED, the control
1622 gets positioned in the top left corner.
1624 RECT parent_rect;
1625 HWND parent;
1626 UINT uPosFlags,cx,cy;
1628 uPosFlags=0;
1629 if (!wParam) {
1630 parent = GetParent (hwnd);
1631 GetClientRect(parent, &parent_rect);
1632 cx=LOWORD (lParam);
1633 cy=HIWORD (lParam);
1634 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
1635 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
1637 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
1638 cx, cy, uPosFlags | SWP_NOZORDER);
1639 } else {
1640 FIXME (tab,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
1641 } */
1644 * Recompute the size/position of the tabs.
1646 TAB_SetItemBounds (hwnd);
1649 * Force a repaint of the control.
1651 InvalidateRect(hwnd, NULL, TRUE);
1653 return 0;
1657 static LRESULT
1658 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
1660 TAB_INFO *infoPtr;
1661 TEXTMETRICA fontMetrics;
1662 HDC hdc;
1663 HFONT hOldFont;
1664 DWORD dwStyle;
1666 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
1668 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
1670 infoPtr->uNumItem = 0;
1671 infoPtr->hFont = 0;
1672 infoPtr->items = 0;
1673 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
1674 infoPtr->iSelected = -1;
1675 infoPtr->uFocus = -1;
1676 infoPtr->hwndToolTip = 0;
1677 infoPtr->DoRedraw = TRUE;
1678 infoPtr->needsScrolling = FALSE;
1679 infoPtr->hwndUpDown = 0;
1680 infoPtr->leftmostVisible = 0;
1681 infoPtr->fSizeSet = FALSE;
1683 TRACE("Created tab control, hwnd [%04x]\n", hwnd);
1685 /* The tab control always has the WS_CLIPSIBLINGS style. Even
1686 if you don't specify in CreateWindow. This is necesary in
1687 order for paint to work correctly. This follows windows behaviour. */
1688 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1689 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
1691 if (dwStyle & TCS_TOOLTIPS) {
1692 /* Create tooltip control */
1693 infoPtr->hwndToolTip =
1694 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
1695 CW_USEDEFAULT, CW_USEDEFAULT,
1696 CW_USEDEFAULT, CW_USEDEFAULT,
1697 hwnd, 0, 0, 0);
1699 /* Send NM_TOOLTIPSCREATED notification */
1700 if (infoPtr->hwndToolTip) {
1701 NMTOOLTIPSCREATED nmttc;
1703 nmttc.hdr.hwndFrom = hwnd;
1704 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1705 nmttc.hdr.code = NM_TOOLTIPSCREATED;
1706 nmttc.hwndToolTips = infoPtr->hwndToolTip;
1708 SendMessageA (GetParent (hwnd), WM_NOTIFY,
1709 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
1714 * We need to get text information so we need a DC and we need to select
1715 * a font.
1717 hdc = GetDC(hwnd);
1718 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
1721 * Use the system font to determine the initial height of a tab.
1723 GetTextMetricsA(hdc, &fontMetrics);
1726 * Make sure there is enough space for the letters + growing the
1727 * selected item + extra space for the selected item.
1729 infoPtr->tabHeight = fontMetrics.tmHeight + 2*VERTICAL_ITEM_PADDING +
1730 SELECTED_TAB_OFFSET;
1733 * Initialize the width of a tab.
1735 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
1737 SelectObject (hdc, hOldFont);
1738 ReleaseDC(hwnd, hdc);
1740 return 0;
1743 static LRESULT
1744 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
1746 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1747 INT iItem;
1749 if (!infoPtr)
1750 return 0;
1752 if (infoPtr->items) {
1753 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
1754 if (infoPtr->items[iItem].pszText)
1755 COMCTL32_Free (infoPtr->items[iItem].pszText);
1757 COMCTL32_Free (infoPtr->items);
1760 if (infoPtr->hwndToolTip)
1761 DestroyWindow (infoPtr->hwndToolTip);
1763 if (infoPtr->hwndUpDown)
1764 DestroyWindow(infoPtr->hwndUpDown);
1766 COMCTL32_Free (infoPtr);
1767 return 0;
1770 static LRESULT WINAPI
1771 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1773 switch (uMsg)
1775 case TCM_GETIMAGELIST:
1776 return TAB_GetImageList (hwnd, wParam, lParam);
1778 case TCM_SETIMAGELIST:
1779 return TAB_SetImageList (hwnd, wParam, lParam);
1781 case TCM_GETITEMCOUNT:
1782 return TAB_GetItemCount (hwnd, wParam, lParam);
1784 case TCM_GETITEMA:
1785 return TAB_GetItemA (hwnd, wParam, lParam);
1787 case TCM_GETITEMW:
1788 FIXME("Unimplemented msg TCM_GETITEMW\n");
1789 return 0;
1791 case TCM_SETITEMA:
1792 return TAB_SetItemA (hwnd, wParam, lParam);
1794 case TCM_SETITEMW:
1795 FIXME("Unimplemented msg TCM_SETITEMW\n");
1796 return 0;
1798 case TCM_DELETEITEM:
1799 return TAB_DeleteItem (hwnd, wParam, lParam);
1801 case TCM_DELETEALLITEMS:
1802 return TAB_DeleteAllItems (hwnd, wParam, lParam);
1804 case TCM_GETITEMRECT:
1805 return TAB_GetItemRect (hwnd, wParam, lParam);
1807 case TCM_GETCURSEL:
1808 return TAB_GetCurSel (hwnd);
1810 case TCM_HITTEST:
1811 return TAB_HitTest (hwnd, wParam, lParam);
1813 case TCM_SETCURSEL:
1814 return TAB_SetCurSel (hwnd, wParam);
1816 case TCM_INSERTITEMA:
1817 return TAB_InsertItem (hwnd, wParam, lParam);
1819 case TCM_INSERTITEMW:
1820 FIXME("Unimplemented msg TCM_INSERTITEMW\n");
1821 return 0;
1823 case TCM_SETITEMEXTRA:
1824 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
1825 return 0;
1827 case TCM_ADJUSTRECT:
1828 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
1830 case TCM_SETITEMSIZE:
1831 return TAB_SetItemSize (hwnd, wParam, lParam);
1833 case TCM_REMOVEIMAGE:
1834 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
1835 return 0;
1837 case TCM_SETPADDING:
1838 FIXME("Unimplemented msg TCM_SETPADDING\n");
1839 return 0;
1841 case TCM_GETROWCOUNT:
1842 FIXME("Unimplemented msg TCM_GETROWCOUNT\n");
1843 return 0;
1845 case TCM_GETUNICODEFORMAT:
1846 FIXME("Unimplemented msg TCM_GETUNICODEFORMAT\n");
1847 return 0;
1849 case TCM_SETUNICODEFORMAT:
1850 FIXME("Unimplemented msg TCM_SETUNICODEFORMAT\n");
1851 return 0;
1853 case TCM_HIGHLIGHTITEM:
1854 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
1855 return 0;
1857 case TCM_GETTOOLTIPS:
1858 return TAB_GetToolTips (hwnd, wParam, lParam);
1860 case TCM_SETTOOLTIPS:
1861 return TAB_SetToolTips (hwnd, wParam, lParam);
1863 case TCM_GETCURFOCUS:
1864 return TAB_GetCurFocus (hwnd);
1866 case TCM_SETCURFOCUS:
1867 return TAB_SetCurFocus (hwnd, wParam);
1869 case TCM_SETMINTABWIDTH:
1870 FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
1871 return 0;
1873 case TCM_DESELECTALL:
1874 FIXME("Unimplemented msg TCM_DESELECTALL\n");
1875 return 0;
1877 case TCM_GETEXTENDEDSTYLE:
1878 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
1879 return 0;
1881 case TCM_SETEXTENDEDSTYLE:
1882 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
1883 return 0;
1885 case WM_GETFONT:
1886 return TAB_GetFont (hwnd, wParam, lParam);
1888 case WM_SETFONT:
1889 return TAB_SetFont (hwnd, wParam, lParam);
1891 case WM_CREATE:
1892 return TAB_Create (hwnd, wParam, lParam);
1894 case WM_NCDESTROY:
1895 return TAB_Destroy (hwnd, wParam, lParam);
1897 case WM_GETDLGCODE:
1898 return DLGC_WANTARROWS | DLGC_WANTCHARS;
1900 case WM_LBUTTONDOWN:
1901 return TAB_LButtonDown (hwnd, wParam, lParam);
1903 case WM_LBUTTONUP:
1904 return TAB_LButtonUp (hwnd, wParam, lParam);
1906 case WM_RBUTTONDOWN:
1907 return TAB_RButtonDown (hwnd, wParam, lParam);
1909 case WM_MOUSEMOVE:
1910 return TAB_MouseMove (hwnd, wParam, lParam);
1912 case WM_ERASEBKGND:
1913 return TAB_EraseBackground (hwnd, (HDC)wParam);
1915 case WM_PAINT:
1916 return TAB_Paint (hwnd, wParam);
1918 case WM_SIZE:
1919 return TAB_Size (hwnd, wParam, lParam);
1921 case WM_SETREDRAW:
1922 return TAB_SetRedraw (hwnd, wParam);
1924 case WM_HSCROLL:
1925 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
1927 case WM_STYLECHANGED:
1928 TAB_SetItemBounds (hwnd);
1929 InvalidateRect(hwnd, NULL, TRUE);
1930 return 0;
1932 case WM_KILLFOCUS:
1933 case WM_SETFOCUS:
1934 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
1936 case WM_KEYUP:
1937 return TAB_KeyUp(hwnd, wParam);
1939 default:
1940 if (uMsg >= WM_USER)
1941 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
1942 uMsg, wParam, lParam);
1943 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1946 return 0;
1950 VOID
1951 TAB_Register (void)
1953 WNDCLASSA wndClass;
1955 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1956 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
1957 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
1958 wndClass.cbClsExtra = 0;
1959 wndClass.cbWndExtra = sizeof(TAB_INFO *);
1960 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
1961 wndClass.hbrBackground = (HBRUSH)NULL;
1962 wndClass.lpszClassName = WC_TABCONTROLA;
1964 RegisterClassA (&wndClass);
1968 VOID
1969 TAB_Unregister (void)
1971 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);