Added support for adding and removing pages dynamically from a
[wine/testsucceed.git] / dlls / comctl32 / tab.c
blob3223540ca8e7a1dc2ae54a643cc8295ac0627f69
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 "debug.h"
21 DEFAULT_DEBUG_CHANNEL(tab)
23 /******************************************************************************
24 * Positioning constants
26 #define SELECTED_TAB_OFFSET 2
27 #define HORIZONTAL_ITEM_PADDING 5
28 #define VERTICAL_ITEM_PADDING 3
29 #define ROUND_CORNER_SIZE 2
30 #define FOCUS_RECT_HOFFSET 2
31 #define FOCUS_RECT_VOFFSET 1
32 #define DISPLAY_AREA_PADDINGX 5
33 #define DISPLAY_AREA_PADDINGY 5
34 #define CONTROL_BORDER_SIZEX 2
35 #define CONTROL_BORDER_SIZEY 2
36 #define BUTTON_SPACINGX 10
37 #define DEFAULT_TAB_WIDTH 96
39 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
41 /******************************************************************************
42 * Prototypes
44 static void TAB_Refresh (HWND hwnd, HDC hdc);
45 static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
46 static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
48 static BOOL
49 TAB_SendSimpleNotify (HWND hwnd, UINT code)
51 NMHDR nmhdr;
53 nmhdr.hwndFrom = hwnd;
54 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
55 nmhdr.code = code;
57 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
58 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
62 static VOID
63 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
64 WPARAM wParam, LPARAM lParam)
66 MSG msg;
68 msg.hwnd = hwndMsg;
69 msg.message = uMsg;
70 msg.wParam = wParam;
71 msg.lParam = lParam;
72 msg.time = GetMessageTime ();
73 msg.pt.x = LOWORD(GetMessagePos ());
74 msg.pt.y = HIWORD(GetMessagePos ());
76 SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
81 static LRESULT
82 TAB_GetCurSel (HWND hwnd)
84 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
86 return infoPtr->iSelected;
89 static LRESULT
90 TAB_GetCurFocus (HWND hwnd)
92 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
94 return infoPtr->uFocus;
97 static LRESULT
98 TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
100 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
102 if (infoPtr == NULL) return 0;
103 return infoPtr->hwndToolTip;
107 static LRESULT
108 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
110 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
111 INT iItem=(INT) wParam;
112 INT prevItem;
114 prevItem=-1;
115 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
116 prevItem=infoPtr->iSelected;
117 infoPtr->iSelected=iItem;
119 return prevItem;
122 static LRESULT
123 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
125 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
126 INT iItem=(INT) wParam;
128 if ((iItem < 0) || (iItem > infoPtr->uNumItem)) return 0;
130 infoPtr->uFocus=iItem;
131 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
132 FIXME (tab,"Should set input focus\n");
133 } else {
134 if (infoPtr->iSelected != iItem) {
135 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
136 infoPtr->iSelected = iItem;
137 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
139 TAB_EnsureSelectionVisible(hwnd, infoPtr);
140 TAB_InvalidateTabArea(hwnd, infoPtr);
144 return 0;
147 static LRESULT
148 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
150 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
152 if (infoPtr == NULL) return 0;
153 infoPtr->hwndToolTip = (HWND)wParam;
154 return 0;
157 /******************************************************************************
158 * TAB_InternalGetItemRect
160 * This method will calculate the rectangle representing a given tab item in
161 * client coordinates. This method takes scrolling into account.
163 * This method returns TRUE if the item is visible in the window and FALSE
164 * if it is completely outside the client area.
166 static BOOL TAB_InternalGetItemRect(
167 HWND hwnd,
168 TAB_INFO* infoPtr,
169 INT itemIndex,
170 RECT* itemRect,
171 RECT* selectedRect)
173 RECT tmpItemRect;
176 * Perform a sanity check and a trivial visibility check.
178 if ( (infoPtr->uNumItem <=0) ||
179 (itemIndex >= infoPtr->uNumItem) ||
180 (itemIndex < infoPtr->leftmostVisible) )
181 return FALSE;
184 * Avoid special cases in this procedure by assigning the "out"
185 * parameters if the caller didn't supply them
187 if (itemRect==NULL)
188 itemRect = &tmpItemRect;
191 * Retrieve the unmodified item rect.
193 *itemRect = infoPtr->items[itemIndex].rect;
196 * "scroll" it to make sure the item at the very left of the
197 * tab control is the leftmost visible tab.
199 OffsetRect(itemRect,
200 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
204 * Move the rectangle so the first item is slightly offset from
205 * the left of the tab control.
207 OffsetRect(itemRect,
208 SELECTED_TAB_OFFSET,
213 * Now, calculate the position of the item as if it were selected.
215 if (selectedRect!=NULL)
217 CopyRect(selectedRect, itemRect);
220 * The rectangle of a selected item is a bit wider.
222 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
225 * If it also a bit higher.
227 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
229 selectedRect->top -=2; /* the border is thicker on the bottom */
230 selectedRect->bottom +=SELECTED_TAB_OFFSET;
232 else
234 selectedRect->top -=SELECTED_TAB_OFFSET;
235 selectedRect->bottom+=1;
239 return TRUE;
242 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
244 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
245 (LPRECT)lParam, (LPRECT)NULL);
248 /******************************************************************************
249 * TAB_KeyUp
251 * This method is called to handle keyboard input
253 static LRESULT TAB_KeyUp(
254 HWND hwnd,
255 WPARAM keyCode)
257 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
258 int newItem = -1;
260 switch (keyCode)
262 case VK_LEFT:
263 newItem = infoPtr->uFocus-1;
264 break;
265 case VK_RIGHT:
266 newItem = infoPtr->uFocus+1;
267 break;
271 * If we changed to a valid item, change the selection
273 if ( (newItem >= 0) &&
274 (newItem < infoPtr->uNumItem) &&
275 (infoPtr->uFocus != newItem) )
277 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
279 infoPtr->iSelected = newItem;
280 infoPtr->uFocus = newItem;
281 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
283 TAB_EnsureSelectionVisible(hwnd, infoPtr);
284 TAB_InvalidateTabArea(hwnd, infoPtr);
288 return 0;
291 /******************************************************************************
292 * TAB_FocusChanging
294 * This method is called whenever the focus goes in or out of this control
295 * it is used to update the visual state of the control.
297 static LRESULT TAB_FocusChanging(
298 HWND hwnd,
299 UINT uMsg,
300 WPARAM wParam,
301 LPARAM lParam)
303 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
304 RECT selectedRect;
305 BOOL isVisible;
308 * Get the rectangle for the item.
310 isVisible = TAB_InternalGetItemRect(hwnd,
311 infoPtr,
312 infoPtr->uFocus,
313 NULL,
314 &selectedRect);
317 * If the rectangle is not completely invisible, invalidate that
318 * portion of the window.
320 if (isVisible)
322 InvalidateRect(hwnd, &selectedRect, TRUE);
326 * Don't otherwise disturb normal behavior.
328 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
331 static HWND TAB_InternalHitTest (
332 HWND hwnd,
333 TAB_INFO* infoPtr,
334 POINT pt,
335 UINT* flags)
338 RECT rect;
339 int iCount;
341 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
343 TAB_InternalGetItemRect(hwnd,
344 infoPtr,
345 iCount,
346 &rect,
347 NULL);
349 if (PtInRect (&rect, pt))
351 *flags = TCHT_ONITEM;
352 return iCount;
356 *flags=TCHT_NOWHERE;
357 return -1;
360 static LRESULT
361 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
363 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
364 LPTCHITTESTINFO lptest=(LPTCHITTESTINFO) lParam;
366 return TAB_InternalHitTest (hwnd, infoPtr,lptest->pt,&lptest->flags);
370 static LRESULT
371 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
373 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
375 if (infoPtr->hwndToolTip)
376 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
377 WM_LBUTTONDOWN, wParam, lParam);
379 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
380 SetFocus (hwnd);
382 return 0;
385 static LRESULT
386 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
388 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
389 POINT pt;
390 INT newItem,dummy;
392 if (infoPtr->hwndToolTip)
393 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
394 WM_LBUTTONDOWN, wParam, lParam);
396 pt.x = (INT)LOWORD(lParam);
397 pt.y = (INT)HIWORD(lParam);
399 newItem=TAB_InternalHitTest (hwnd, infoPtr,pt,&dummy);
401 TRACE(tab, "On Tab, item %d\n", newItem);
403 if ( (newItem!=-1) &&
404 (infoPtr->iSelected != newItem) )
406 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE)
408 infoPtr->iSelected = newItem;
409 infoPtr->uFocus = newItem;
410 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
412 TAB_EnsureSelectionVisible(hwnd, infoPtr);
414 TAB_InvalidateTabArea(hwnd, infoPtr);
417 TAB_SendSimpleNotify(hwnd, NM_CLICK);
419 return 0;
422 static LRESULT
423 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
425 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
426 return 0;
429 static LRESULT
430 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
432 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
434 if (infoPtr->hwndToolTip)
435 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
436 WM_LBUTTONDOWN, wParam, lParam);
437 return 0;
440 /******************************************************************************
441 * TAB_AdjustRect
443 * Calculates the tab control's display area given the windows rectangle or
444 * the window rectangle given the requested display rectangle.
446 static LRESULT TAB_AdjustRect(
447 HWND hwnd,
448 WPARAM fLarger,
449 LPRECT prc)
451 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
453 if (fLarger)
456 * Go from display rectangle
460 * Add the height of the tabs.
462 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
463 prc->bottom += infoPtr->tabHeight;
464 else
465 prc->top -= infoPtr->tabHeight;
468 * Inflate the rectangle for the padding
470 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
473 * Inflate for the border
475 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
477 else
480 * Go from window rectangle.
484 * Deflate the rectangle for the border
486 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
489 * Deflate the rectangle for the padding
491 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
494 * Remove the height of the tabs.
496 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
497 prc->bottom -= infoPtr->tabHeight;
498 else
499 prc->top += infoPtr->tabHeight;
503 return 0;
506 /******************************************************************************
507 * TAB_OnHScroll
509 * This method will handle the notification from the scroll control and
510 * perform the scrolling operation on the tab control.
512 static LRESULT TAB_OnHScroll(
513 HWND hwnd,
514 int nScrollCode,
515 int nPos,
516 HWND hwndScroll)
518 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
520 if (nScrollCode == SB_LINELEFT)
522 if (infoPtr->leftmostVisible>0)
524 infoPtr->leftmostVisible--;
526 TAB_InvalidateTabArea(hwnd, infoPtr);
529 else if (nScrollCode == SB_LINERIGHT)
531 if (infoPtr->leftmostVisible< (infoPtr->uNumItem-1))
533 infoPtr->leftmostVisible++;
535 TAB_InvalidateTabArea(hwnd, infoPtr);
539 return 0;
542 /******************************************************************************
543 * TAB_SetupScroling
545 * This method will check the current scrolling state and make sure the
546 * scrolling control is displayed (or not).
548 static void TAB_SetupScrolling(
549 HWND hwnd,
550 TAB_INFO* infoPtr,
551 const RECT* clientRect)
553 if (infoPtr->needsScrolling)
555 RECT controlPos;
558 * Calculate the position of the scroll control.
560 controlPos.right = clientRect->right;
561 controlPos.left = controlPos.right - 2*GetSystemMetrics(SM_CXHSCROLL);
563 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
565 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
566 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
568 else
570 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
571 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
575 * If we don't have a scroll control yet, we want to create one.
576 * If we have one, we want to make sure it's positioned right.
578 if (infoPtr->hwndUpDown==0)
581 * I use a scrollbar since it seems to be more stable than the Updown
582 * control.
584 infoPtr->hwndUpDown = CreateWindowA("ScrollBar",
586 WS_VISIBLE | WS_CHILD | WS_OVERLAPPED | SBS_HORZ,
587 controlPos.left, controlPos.top,
588 controlPos.right - controlPos.left,
589 controlPos.bottom - controlPos.top,
590 hwnd,
591 (HMENU)NULL,
592 (HINSTANCE)NULL,
593 NULL);
595 else
597 SetWindowPos(infoPtr->hwndUpDown,
598 (HWND)NULL,
599 controlPos.left, controlPos.top,
600 controlPos.right - controlPos.left,
601 controlPos.bottom - controlPos.top,
602 SWP_SHOWWINDOW | SWP_NOZORDER);
605 else
608 * If we once had a scroll control... hide it.
610 if (infoPtr->hwndUpDown!=0)
612 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
617 /******************************************************************************
618 * TAB_SetItemBounds
620 * This method will calculate the position rectangles of all the items in the
621 * control. The rectangle calculated starts at 0 for the first item in the
622 * list and ignores scrolling and selection.
623 * It also uses the current font to determine the height of the tab row and
624 * it checks if all the tabs fit in the client area of the window. If they
625 * dont, a scrolling control is added.
627 static void TAB_SetItemBounds (HWND hwnd)
629 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
630 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
631 TEXTMETRICA fontMetrics;
632 INT curItem;
633 INT curItemLeftPos;
634 HFONT hFont, hOldFont;
635 HDC hdc;
636 RECT clientRect;
637 SIZE size;
640 * We need to get text information so we need a DC and we need to select
641 * a font.
643 hdc = GetDC(hwnd);
645 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
646 hOldFont = SelectObject (hdc, hFont);
649 * We will base the rectangle calculations on the client rectangle
650 * of the control.
652 GetClientRect(hwnd, &clientRect);
655 * The leftmost item will be "0" aligned
657 curItemLeftPos = 0;
659 if (!((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED)))
662 * Use the current font to determine the height of a tab.
664 GetTextMetricsA(hdc, &fontMetrics);
667 * Make sure there is enough space for the letters + growing the
668 * selected item + extra space for the selected item.
670 infoPtr->tabHeight = fontMetrics.tmHeight + 2*VERTICAL_ITEM_PADDING +
671 SELECTED_TAB_OFFSET;
674 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
677 * Calculate the vertical position of the tab
679 if (lStyle & TCS_BOTTOM)
681 infoPtr->items[curItem].rect.bottom = clientRect.bottom -
682 SELECTED_TAB_OFFSET;
683 infoPtr->items[curItem].rect.top = clientRect.bottom -
684 infoPtr->tabHeight;
686 else
688 infoPtr->items[curItem].rect.top = clientRect.top +
689 SELECTED_TAB_OFFSET;
690 infoPtr->items[curItem].rect.bottom = clientRect.top +
691 infoPtr->tabHeight;
695 * Set the leftmost position of the tab.
697 infoPtr->items[curItem].rect.left = curItemLeftPos;
699 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
701 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
702 infoPtr->tabWidth +
703 2*HORIZONTAL_ITEM_PADDING;
705 else
708 * Calculate how wide the tab is depending on the text it contains
710 GetTextExtentPoint32A(hdc, infoPtr->items[curItem].pszText,
711 lstrlenA(infoPtr->items[curItem].pszText), &size);
713 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
714 size.cx + 2*HORIZONTAL_ITEM_PADDING;
717 TRACE(tab, "TextSize: %i\n ", size.cx);
718 TRACE(tab, "Rect: T %i, L %i, B %i, R %i\n",
719 infoPtr->items[curItem].rect.top,
720 infoPtr->items[curItem].rect.left,
721 infoPtr->items[curItem].rect.bottom,
722 infoPtr->items[curItem].rect.right);
725 * The leftmost position of the next item is the rightmost position
726 * of this one.
728 if (lStyle & TCS_BUTTONS)
729 curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX;
730 else
731 curItemLeftPos = infoPtr->items[curItem].rect.right;
735 * Check if we need a scrolling control.
737 infoPtr->needsScrolling = (curItemLeftPos + (2*SELECTED_TAB_OFFSET) >
738 clientRect.right);
740 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
743 * Cleanup
745 SelectObject (hdc, hOldFont);
746 ReleaseDC (hwnd, hdc);
749 /******************************************************************************
750 * TAB_DrawItem
752 * This method is used to draw a single tab into the tab control.
754 static void TAB_DrawItem(
755 HWND hwnd,
756 HDC hdc,
757 INT iItem)
759 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
760 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
761 RECT itemRect;
762 RECT selectedRect;
763 BOOL isVisible;
764 RECT r;
767 * Get the rectangle for the item.
769 isVisible = TAB_InternalGetItemRect(hwnd,
770 infoPtr,
771 iItem,
772 &itemRect,
773 &selectedRect);
775 if (isVisible)
777 HBRUSH hbr = CreateSolidBrush (GetSysColor(COLOR_BACKGROUND));
778 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
779 HPEN hbPen = GetSysColorPen (COLOR_BTNSHADOW);
780 HPEN hsdPen = GetSysColorPen (COLOR_BTNTEXT);
781 HPEN hfocusPen = CreatePen(PS_DOT, 1, GetSysColor(COLOR_BTNTEXT));
782 HPEN holdPen;
783 INT oldBkMode;
784 INT cx,cy;
786 if (lStyle & TCS_BUTTONS)
789 * Get item rectangle.
791 r = itemRect;
793 holdPen = SelectObject (hdc, hwPen);
795 if (iItem == infoPtr->iSelected)
798 * Background color.
800 if (!(lStyle & TCS_OWNERDRAWFIXED))
801 hbr = CreateSolidBrush(GetSysColor(COLOR_3DHILIGHT));
804 * Erase the background.
806 FillRect(hdc, &r, hbr);
809 * Draw the tab now.
810 * The rectangles calculated exclude the right and bottom
811 * borders of the rectangle. To simply the following code, those
812 * borders are shaved-off beforehand.
814 r.right--;
815 r.bottom--;
817 /* highlight */
818 MoveToEx (hdc, r.left, r.bottom, NULL);
819 LineTo (hdc, r.right, r.bottom);
820 LineTo (hdc, r.right, r.top);
822 /* shadow */
823 SelectObject(hdc, hbPen);
824 LineTo (hdc, r.left, r.top);
825 LineTo (hdc, r.left, r.bottom);
827 else
830 * Erase the background.
832 FillRect(hdc, &r, hbr);
834 /* highlight */
835 MoveToEx (hdc, r.left, r.bottom, NULL);
836 LineTo (hdc, r.left, r.top);
837 LineTo (hdc, r.right, r.top);
839 /* shadow */
840 SelectObject(hdc, hbPen);
841 LineTo (hdc, r.right, r.bottom);
842 LineTo (hdc, r.left, r.bottom);
845 else
848 * Background color.
850 hbr = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
853 * We draw a rectangle of different sizes depending on the selection
854 * state.
856 if (iItem == infoPtr->iSelected)
857 r = selectedRect;
858 else
859 r = itemRect;
862 * Erase the background.
863 * This is necessary when drawing the selected item since it is larger
864 * than the others, it might overlap with stuff already drawn by the
865 * other tabs
867 FillRect(hdc, &r, hbr);
870 * Draw the tab now.
871 * The rectangles calculated exclude the right and bottom
872 * borders of the rectangle. To simply the following code, those
873 * borders are shaved-off beforehand.
875 r.right--;
876 r.bottom--;
878 holdPen = SelectObject (hdc, hwPen);
880 if (lStyle & TCS_BOTTOM)
882 /* highlight */
883 MoveToEx (hdc, r.left, r.top, NULL);
884 LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE);
885 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom);
887 /* shadow */
888 SelectObject(hdc, hbPen);
889 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom);
890 LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE);
891 LineTo (hdc, r.right, r.top);
893 else
895 /* highlight */
896 MoveToEx (hdc, r.left, r.bottom, NULL);
897 LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE);
898 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top);
899 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top);
901 /* shadow */
902 SelectObject(hdc, hbPen);
903 LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE);
904 LineTo (hdc, r.right, r.bottom);
909 * Text pen
911 SelectObject(hdc, hsdPen);
913 oldBkMode = SetBkMode(hdc, TRANSPARENT);
914 SetTextColor (hdc, COLOR_BTNTEXT);
917 * Deflate the rectangle to acount for the padding
919 InflateRect(&r, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
922 * Draw the icon.
924 if (infoPtr->himl)
926 ImageList_Draw (infoPtr->himl, iItem, hdc,
927 r.left, r.top+1, ILD_NORMAL);
928 ImageList_GetIconSize (infoPtr->himl, &cx, &cy);
929 r.left+=cx;
933 * Draw the text;
935 DrawTextA(hdc,
936 infoPtr->items[iItem].pszText,
937 lstrlenA(infoPtr->items[iItem].pszText),
938 &r,
939 DT_LEFT|DT_SINGLELINE|DT_VCENTER);
942 * Draw the focus rectangle
944 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
945 (GetFocus() == hwnd) &&
946 (iItem == infoPtr->uFocus) )
948 InflateRect(&r, FOCUS_RECT_HOFFSET, FOCUS_RECT_VOFFSET);
950 SelectObject(hdc, hfocusPen);
952 MoveToEx (hdc, r.left, r.top, NULL);
953 LineTo (hdc, r.right-1, r.top);
954 LineTo (hdc, r.right-1, r.bottom -1);
955 LineTo (hdc, r.left, r.bottom -1);
956 LineTo (hdc, r.left, r.top);
960 * Cleanup
962 SetBkMode(hdc, oldBkMode);
963 SelectObject(hdc, holdPen);
967 /******************************************************************************
968 * TAB_DrawBorder
970 * This method is used to draw the raised border around the tab control
971 * "content" area.
973 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
975 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
976 HPEN htmPen;
977 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
978 HPEN hbPen = GetSysColorPen (COLOR_3DDKSHADOW);
979 HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW);
980 RECT rect;
982 GetClientRect (hwnd, &rect);
985 * Adjust for the style
987 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
989 rect.bottom -= infoPtr->tabHeight;
991 else
993 rect.top += infoPtr->tabHeight;
997 * Shave-off the right and bottom margins (exluded in the
998 * rect)
1000 rect.right--;
1001 rect.bottom--;
1003 /* highlight */
1004 htmPen = SelectObject (hdc, hwPen);
1006 MoveToEx (hdc, rect.left, rect.bottom, NULL);
1007 LineTo (hdc, rect.left, rect.top);
1008 LineTo (hdc, rect.right, rect.top);
1010 /* Dark Shadow */
1011 SelectObject (hdc, hbPen);
1012 LineTo (hdc, rect.right, rect.bottom );
1013 LineTo (hdc, rect.left, rect.bottom);
1015 /* shade */
1016 SelectObject (hdc, hShade );
1017 MoveToEx (hdc, rect.right-1, rect.top, NULL);
1018 LineTo (hdc, rect.right-1, rect.bottom-1);
1019 LineTo (hdc, rect.left, rect.bottom-1);
1021 SelectObject(hdc, htmPen);
1024 /******************************************************************************
1025 * TAB_Refresh
1027 * This method repaints the tab control..
1029 static void TAB_Refresh (HWND hwnd, HDC hdc)
1031 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1032 HFONT hOldFont;
1033 INT i;
1035 if (!infoPtr->DoRedraw)
1036 return;
1038 hOldFont = SelectObject (hdc, infoPtr->hFont);
1040 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
1042 for (i = 0; i < infoPtr->uNumItem; i++)
1044 TAB_DrawItem (hwnd, hdc, i);
1047 else
1050 * Draw all the non selected item first.
1052 for (i = 0; i < infoPtr->uNumItem; i++)
1054 if (i != infoPtr->iSelected)
1055 TAB_DrawItem (hwnd, hdc, i);
1059 * Now, draw the border, draw it before the selected item
1060 * since the selected item overwrites part of the border.
1062 TAB_DrawBorder (hwnd, hdc);
1065 * Then, draw the selected item
1067 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
1070 SelectObject (hdc, hOldFont);
1073 static LRESULT
1074 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
1076 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1078 infoPtr->DoRedraw=(BOOL) wParam;
1079 return 0;
1082 static LRESULT TAB_EraseBackground(
1083 HWND hwnd,
1084 HDC givenDC)
1086 HDC hdc;
1087 RECT clientRect;
1089 HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
1091 hdc = givenDC ? givenDC : GetDC(hwnd);
1093 GetClientRect(hwnd, &clientRect);
1095 FillRect(hdc, &clientRect, brush);
1097 if (givenDC==0)
1098 ReleaseDC(hwnd, hdc);
1100 return 0;
1103 /******************************************************************************
1104 * TAB_EnsureSelectionVisible
1106 * This method will make sure that the current selection is completely
1107 * visible by scrolling until it is.
1109 static void TAB_EnsureSelectionVisible(
1110 HWND hwnd,
1111 TAB_INFO* infoPtr)
1113 RECT selectedRect;
1114 RECT visibleRect;
1115 RECT scrollerRect;
1116 BOOL isVisible;
1119 * Do the trivial cases first.
1121 if ( (!infoPtr->needsScrolling) ||
1122 (infoPtr->hwndUpDown==0) )
1123 return;
1125 if (infoPtr->leftmostVisible > infoPtr->iSelected)
1127 infoPtr->leftmostVisible = infoPtr->iSelected;
1128 return;
1132 * Calculate the part of the client area that is visible.
1134 GetClientRect(hwnd, &visibleRect);
1135 GetClientRect(infoPtr->hwndUpDown, &scrollerRect);
1136 visibleRect.right -= scrollerRect.right;
1139 * Get the rectangle for the item
1141 isVisible = TAB_InternalGetItemRect(hwnd,
1142 infoPtr,
1143 infoPtr->iSelected,
1144 NULL,
1145 &selectedRect);
1148 * If this function can't say it's completely invisible, maybe it
1149 * is partially visible. Let's check.
1151 if (isVisible)
1153 POINT pt1;
1154 POINT pt2;
1156 pt1.x = selectedRect.left;
1157 pt1.y = selectedRect.top;
1158 pt2.x = selectedRect.right - 1;
1159 pt2.y = selectedRect.bottom - 1;
1161 isVisible = PtInRect(&visibleRect, pt1) && PtInRect(&visibleRect, pt2);
1164 while ( (infoPtr->leftmostVisible < infoPtr->iSelected) &&
1165 !isVisible)
1167 infoPtr->leftmostVisible++;
1170 * Get the rectangle for the item
1172 isVisible = TAB_InternalGetItemRect(hwnd,
1173 infoPtr,
1174 infoPtr->iSelected,
1175 NULL,
1176 &selectedRect);
1179 * If this function can't say it's completely invisible, maybe it
1180 * is partially visible. Let's check.
1182 if (isVisible)
1184 POINT pt1;
1185 POINT pt2;
1187 pt1.x = selectedRect.left;
1188 pt1.y = selectedRect.top;
1189 pt2.x = selectedRect.right - 1;
1190 pt2.y = selectedRect.bottom - 1;
1192 isVisible = PtInRect(&visibleRect, pt1) && PtInRect(&visibleRect, pt2);
1197 /******************************************************************************
1198 * TAB_InvalidateTabArea
1200 * This method will invalidate the portion of the control that contains the
1201 * tabs. It is called when the state of the control changes and needs
1202 * to be redisplayed
1204 static void TAB_InvalidateTabArea(
1205 HWND hwnd,
1206 TAB_INFO* infoPtr)
1208 RECT clientRect;
1210 GetClientRect(hwnd, &clientRect);
1212 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1214 clientRect.top = clientRect.bottom - (infoPtr->tabHeight + 1);
1216 else
1218 clientRect.bottom = clientRect.top + (infoPtr->tabHeight + 1);
1221 InvalidateRect(hwnd, &clientRect, TRUE);
1224 static LRESULT
1225 TAB_Paint (HWND hwnd, WPARAM wParam)
1227 HDC hdc;
1228 PAINTSTRUCT ps;
1230 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
1231 TAB_Refresh (hwnd, hdc);
1233 if(!wParam)
1234 EndPaint (hwnd, &ps);
1236 return 0;
1239 static LRESULT
1240 TAB_InsertItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1242 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1243 TCITEMA *pti;
1244 INT iItem, len;
1245 RECT rect;
1247 GetClientRect (hwnd, &rect);
1248 TRACE(tab, "Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
1249 rect.top, rect.left, rect.bottom, rect.right);
1251 pti = (TCITEMA *)lParam;
1252 iItem = (INT)wParam;
1254 if (iItem < 0) return -1;
1255 if (iItem > infoPtr->uNumItem)
1256 iItem = infoPtr->uNumItem;
1258 if (infoPtr->uNumItem == 0) {
1259 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
1260 infoPtr->uNumItem++;
1262 else {
1263 TAB_ITEM *oldItems = infoPtr->items;
1265 infoPtr->uNumItem++;
1266 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
1268 /* pre insert copy */
1269 if (iItem > 0) {
1270 memcpy (&infoPtr->items[0], &oldItems[0],
1271 iItem * sizeof(TAB_ITEM));
1274 /* post insert copy */
1275 if (iItem < infoPtr->uNumItem - 1) {
1276 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
1277 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
1281 COMCTL32_Free (oldItems);
1284 infoPtr->items[iItem].mask = pti->mask;
1285 if (pti->mask & TCIF_TEXT) {
1286 len = lstrlenA (pti->pszText);
1287 infoPtr->items[iItem].pszText = COMCTL32_Alloc (len+1);
1288 lstrcpyA (infoPtr->items[iItem].pszText, pti->pszText);
1289 infoPtr->items[iItem].cchTextMax = pti->cchTextMax;
1292 if (pti->mask & TCIF_IMAGE)
1293 infoPtr->items[iItem].iImage = pti->iImage;
1295 if (pti->mask & TCIF_PARAM)
1296 infoPtr->items[iItem].lParam = pti->lParam;
1298 TAB_InvalidateTabArea(hwnd, infoPtr);
1300 TRACE(tab, "[%04x]: added item %d '%s'\n",
1301 hwnd, iItem, infoPtr->items[iItem].pszText);
1303 TAB_SetItemBounds(hwnd);
1304 return iItem;
1307 static LRESULT
1308 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
1310 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1311 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1312 LONG lResult = 0;
1314 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
1316 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
1317 infoPtr->tabWidth = (INT)LOWORD(lParam);
1318 infoPtr->tabHeight = (INT)HIWORD(lParam);
1321 return lResult;
1324 static LRESULT
1325 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1327 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1328 TCITEMA *tabItem;
1329 TAB_ITEM *wineItem;
1330 INT iItem,len;
1332 iItem=(INT) wParam;
1333 tabItem=(LPTCITEMA ) lParam;
1334 TRACE (tab,"%d %p\n",iItem, tabItem);
1335 if ((iItem<0) || (iItem>infoPtr->uNumItem)) return FALSE;
1337 wineItem=& infoPtr->items[iItem];
1339 if (tabItem->mask & TCIF_IMAGE)
1340 wineItem->iImage=tabItem->iImage;
1342 if (tabItem->mask & TCIF_PARAM)
1343 wineItem->lParam=tabItem->lParam;
1345 if (tabItem->mask & TCIF_RTLREADING)
1346 FIXME (tab,"TCIF_RTLREADING\n");
1348 if (tabItem->mask & TCIF_STATE)
1349 wineItem->dwState=tabItem->dwState;
1351 if (tabItem->mask & TCIF_TEXT) {
1352 len=lstrlenA (tabItem->pszText);
1353 if (len>wineItem->cchTextMax)
1354 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
1355 lstrcpynA (wineItem->pszText, tabItem->pszText, len);
1358 return TRUE;
1361 static LRESULT
1362 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1364 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1366 return infoPtr->uNumItem;
1370 static LRESULT
1371 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1373 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1374 TCITEMA *tabItem;
1375 TAB_ITEM *wineItem;
1376 INT iItem;
1378 iItem=(INT) wParam;
1379 tabItem=(LPTCITEMA) lParam;
1380 TRACE (tab,"\n");
1381 if ((iItem<0) || (iItem>infoPtr->uNumItem)) return FALSE;
1383 wineItem=& infoPtr->items[iItem];
1385 if (tabItem->mask & TCIF_IMAGE)
1386 tabItem->iImage=wineItem->iImage;
1388 if (tabItem->mask & TCIF_PARAM)
1389 tabItem->lParam=wineItem->lParam;
1391 if (tabItem->mask & TCIF_RTLREADING)
1392 FIXME (tab, "TCIF_RTLREADING\n");
1394 if (tabItem->mask & TCIF_STATE)
1395 tabItem->dwState=wineItem->dwState;
1397 if (tabItem->mask & TCIF_TEXT)
1398 lstrcpynA (tabItem->pszText, wineItem->pszText, tabItem->cchTextMax);
1400 return TRUE;
1403 static LRESULT
1404 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1406 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1407 INT iItem = (INT) wParam;
1408 BOOL bResult = FALSE;
1410 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
1412 TAB_ITEM *oldItems = infoPtr->items;
1414 infoPtr->uNumItem--;
1415 infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
1417 if (iItem > 0)
1418 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
1420 if (iItem < infoPtr->uNumItem)
1421 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
1422 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
1424 COMCTL32_Free (oldItems);
1427 * Readjust the selected index.
1429 if ((iItem == infoPtr->iSelected) && (iItem > 0))
1430 infoPtr->iSelected--;
1432 if (iItem < infoPtr->iSelected)
1433 infoPtr->iSelected--;
1436 * Reposition and repaint tabs.
1438 TAB_SetItemBounds(hwnd);
1439 TAB_InvalidateTabArea(hwnd,infoPtr);
1441 bResult = TRUE;
1444 return bResult;
1447 static LRESULT
1448 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
1450 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1452 COMCTL32_Free (infoPtr->items);
1453 infoPtr->uNumItem=0;
1455 return TRUE;
1459 static LRESULT
1460 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1462 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1464 TRACE (tab,"\n");
1465 return (LRESULT)infoPtr->hFont;
1468 static LRESULT
1469 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1472 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1474 TRACE (tab,"%x %lx\n",wParam, lParam);
1476 infoPtr->hFont = (HFONT)wParam;
1478 TAB_SetItemBounds(hwnd);
1480 TAB_InvalidateTabArea(hwnd, infoPtr);
1482 return 0;
1486 static LRESULT
1487 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1489 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1491 TRACE (tab,"\n");
1492 return (LRESULT)infoPtr->himl;
1495 static LRESULT
1496 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1498 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1499 HIMAGELIST himlPrev;
1501 TRACE (tab,"\n");
1502 himlPrev = infoPtr->himl;
1503 infoPtr->himl= (HIMAGELIST)lParam;
1504 return (LRESULT)himlPrev;
1508 static LRESULT
1509 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1512 /* I'm not really sure what the following code was meant to do.
1513 This is what it is doing:
1514 When WM_SIZE is sent with SIZE_RESTORED, the control
1515 gets positioned in the top left corner.
1517 RECT parent_rect;
1518 HWND parent;
1519 UINT uPosFlags,cx,cy;
1521 uPosFlags=0;
1522 if (!wParam) {
1523 parent = GetParent (hwnd);
1524 GetClientRect(parent, &parent_rect);
1525 cx=LOWORD (lParam);
1526 cy=HIWORD (lParam);
1527 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
1528 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
1530 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
1531 cx, cy, uPosFlags | SWP_NOZORDER);
1532 } else {
1533 FIXME (tab,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
1534 } */
1537 * Recompute the size/position of the tabs.
1539 TAB_SetItemBounds (hwnd);
1542 * Force a repaint of the control.
1544 InvalidateRect(hwnd, NULL, TRUE);
1546 return 0;
1550 static LRESULT
1551 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
1553 TAB_INFO *infoPtr;
1554 TEXTMETRICA fontMetrics;
1555 HDC hdc;
1556 HFONT hOldFont;
1558 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
1560 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
1562 infoPtr->uNumItem = 0;
1563 infoPtr->hFont = 0;
1564 infoPtr->items = 0;
1565 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
1566 infoPtr->iSelected = 0;
1567 infoPtr->uFocus = 0;
1568 infoPtr->hwndToolTip = 0;
1569 infoPtr->DoRedraw = TRUE;
1570 infoPtr->needsScrolling = FALSE;
1571 infoPtr->hwndUpDown = 0;
1572 infoPtr->leftmostVisible = 0;
1574 TRACE(tab, "Created tab control, hwnd [%04x]\n", hwnd);
1575 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_TOOLTIPS) {
1576 /* Create tooltip control */
1577 infoPtr->hwndToolTip =
1578 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
1579 CW_USEDEFAULT, CW_USEDEFAULT,
1580 CW_USEDEFAULT, CW_USEDEFAULT,
1581 hwnd, 0, 0, 0);
1583 /* Send NM_TOOLTIPSCREATED notification */
1584 if (infoPtr->hwndToolTip) {
1585 NMTOOLTIPSCREATED nmttc;
1587 nmttc.hdr.hwndFrom = hwnd;
1588 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1589 nmttc.hdr.code = NM_TOOLTIPSCREATED;
1590 nmttc.hwndToolTips = infoPtr->hwndToolTip;
1592 SendMessageA (GetParent (hwnd), WM_NOTIFY,
1593 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
1598 * We need to get text information so we need a DC and we need to select
1599 * a font.
1601 hdc = GetDC(hwnd);
1602 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
1605 * Use the system font to determine the initial height of a tab.
1607 GetTextMetricsA(hdc, &fontMetrics);
1610 * Make sure there is enough space for the letters + growing the
1611 * selected item + extra space for the selected item.
1613 infoPtr->tabHeight = fontMetrics.tmHeight + 2*VERTICAL_ITEM_PADDING +
1614 SELECTED_TAB_OFFSET;
1617 * Initialize the width of a tab.
1619 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
1621 SelectObject (hdc, hOldFont);
1622 ReleaseDC(hwnd, hdc);
1624 return 0;
1627 static LRESULT
1628 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
1630 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1631 INT iItem;
1633 if (infoPtr->items) {
1634 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
1635 if (infoPtr->items[iItem].pszText)
1636 COMCTL32_Free (infoPtr->items[iItem].pszText);
1638 COMCTL32_Free (infoPtr->items);
1641 if (infoPtr->hwndToolTip)
1642 DestroyWindow (infoPtr->hwndToolTip);
1644 if (infoPtr->hwndUpDown)
1645 DestroyWindow(infoPtr->hwndUpDown);
1647 COMCTL32_Free (infoPtr);
1648 return 0;
1651 LRESULT WINAPI
1652 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1654 switch (uMsg)
1656 case TCM_GETIMAGELIST:
1657 return TAB_GetImageList (hwnd, wParam, lParam);
1659 case TCM_SETIMAGELIST:
1660 return TAB_SetImageList (hwnd, wParam, lParam);
1662 case TCM_GETITEMCOUNT:
1663 return TAB_GetItemCount (hwnd, wParam, lParam);
1665 case TCM_GETITEMA:
1666 return TAB_GetItemA (hwnd, wParam, lParam);
1668 case TCM_GETITEMW:
1669 FIXME (tab, "Unimplemented msg TCM_GETITEMW\n");
1670 return 0;
1672 case TCM_SETITEMA:
1673 return TAB_SetItemA (hwnd, wParam, lParam);
1675 case TCM_SETITEMW:
1676 FIXME (tab, "Unimplemented msg TCM_SETITEMW\n");
1677 return 0;
1679 case TCM_DELETEITEM:
1680 return TAB_DeleteItem (hwnd, wParam, lParam);
1682 case TCM_DELETEALLITEMS:
1683 return TAB_DeleteAllItems (hwnd, wParam, lParam);
1685 case TCM_GETITEMRECT:
1686 return TAB_GetItemRect (hwnd, wParam, lParam);
1688 case TCM_GETCURSEL:
1689 return TAB_GetCurSel (hwnd);
1691 case TCM_HITTEST:
1692 return TAB_HitTest (hwnd, wParam, lParam);
1694 case TCM_SETCURSEL:
1695 return TAB_SetCurSel (hwnd, wParam);
1697 case TCM_INSERTITEMA:
1698 return TAB_InsertItem (hwnd, wParam, lParam);
1700 case TCM_INSERTITEMW:
1701 FIXME (tab, "Unimplemented msg TCM_INSERTITEM32W\n");
1702 return 0;
1704 case TCM_SETITEMEXTRA:
1705 FIXME (tab, "Unimplemented msg TCM_SETITEMEXTRA\n");
1706 return 0;
1708 case TCM_ADJUSTRECT:
1709 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
1711 case TCM_SETITEMSIZE:
1712 return TAB_SetItemSize (hwnd, wParam, lParam);
1714 case TCM_REMOVEIMAGE:
1715 FIXME (tab, "Unimplemented msg TCM_REMOVEIMAGE\n");
1716 return 0;
1718 case TCM_SETPADDING:
1719 FIXME (tab, "Unimplemented msg TCM_SETPADDING\n");
1720 return 0;
1722 case TCM_GETROWCOUNT:
1723 FIXME (tab, "Unimplemented msg TCM_GETROWCOUNT\n");
1724 return 0;
1726 case TCM_GETUNICODEFORMAT:
1727 FIXME (tab, "Unimplemented msg TCM_GETUNICODEFORMAT\n");
1728 return 0;
1730 case TCM_SETUNICODEFORMAT:
1731 FIXME (tab, "Unimplemented msg TCM_SETUNICODEFORMAT\n");
1732 return 0;
1734 case TCM_HIGHLIGHTITEM:
1735 FIXME (tab, "Unimplemented msg TCM_HIGHLIGHTITEM\n");
1736 return 0;
1738 case TCM_GETTOOLTIPS:
1739 return TAB_GetToolTips (hwnd, wParam, lParam);
1741 case TCM_SETTOOLTIPS:
1742 return TAB_SetToolTips (hwnd, wParam, lParam);
1744 case TCM_GETCURFOCUS:
1745 return TAB_GetCurFocus (hwnd);
1747 case TCM_SETCURFOCUS:
1748 return TAB_SetCurFocus (hwnd, wParam);
1750 case TCM_SETMINTTABWIDTH:
1751 FIXME (tab, "Unimplemented msg TCM_SETMINTTABWIDTH\n");
1752 return 0;
1754 case TCM_DESELECTALL:
1755 FIXME (tab, "Unimplemented msg TCM_DESELECTALL\n");
1756 return 0;
1758 case TCM_GETEXTENDEDSTYLE:
1759 FIXME (tab, "Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
1760 return 0;
1762 case TCM_SETEXTENDEDSTYLE:
1763 FIXME (tab, "Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
1764 return 0;
1766 case WM_GETFONT:
1767 return TAB_GetFont (hwnd, wParam, lParam);
1769 case WM_SETFONT:
1770 return TAB_SetFont (hwnd, wParam, lParam);
1772 case WM_CREATE:
1773 return TAB_Create (hwnd, wParam, lParam);
1775 case WM_NCDESTROY:
1776 return TAB_Destroy (hwnd, wParam, lParam);
1778 case WM_GETDLGCODE:
1779 return DLGC_WANTARROWS | DLGC_WANTCHARS;
1781 case WM_LBUTTONDOWN:
1782 return TAB_LButtonDown (hwnd, wParam, lParam);
1784 case WM_LBUTTONUP:
1785 return TAB_LButtonUp (hwnd, wParam, lParam);
1787 case WM_RBUTTONDOWN:
1788 return TAB_RButtonDown (hwnd, wParam, lParam);
1790 case WM_MOUSEMOVE:
1791 return TAB_MouseMove (hwnd, wParam, lParam);
1793 case WM_ERASEBKGND:
1794 return TAB_EraseBackground (hwnd, (HDC)wParam);
1796 case WM_PAINT:
1797 return TAB_Paint (hwnd, wParam);
1799 case WM_SIZE:
1800 return TAB_Size (hwnd, wParam, lParam);
1802 case WM_SETREDRAW:
1803 return TAB_SetRedraw (hwnd, wParam);
1805 case WM_HSCROLL:
1806 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
1808 case WM_KILLFOCUS:
1809 case WM_SETFOCUS:
1810 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
1812 case WM_KEYUP:
1813 return TAB_KeyUp(hwnd, wParam);
1815 default:
1816 if (uMsg >= WM_USER)
1817 ERR (tab, "unknown msg %04x wp=%08x lp=%08lx\n",
1818 uMsg, wParam, lParam);
1819 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1822 return 0;
1826 VOID
1827 TAB_Register (VOID)
1829 WNDCLASSA wndClass;
1831 if (GlobalFindAtomA (WC_TABCONTROLA)) return;
1833 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1834 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS;
1835 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
1836 wndClass.cbClsExtra = 0;
1837 wndClass.cbWndExtra = sizeof(TAB_INFO *);
1838 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
1839 wndClass.hbrBackground = (HBRUSH)NULL;
1840 wndClass.lpszClassName = WC_TABCONTROLA;
1842 RegisterClassA (&wndClass);
1846 VOID
1847 TAB_Unregister (VOID)
1849 if (GlobalFindAtomA (WC_TABCONTROLA))
1850 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);