Fixed hash function.
[wine/dcerpc.git] / dlls / comctl32 / tab.c
blob530a608e52af3891dbd8a7ba768a7f9aedb5585a
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"
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;
118 TAB_EnsureSelectionVisible(hwnd, infoPtr);
119 TAB_InvalidateTabArea(hwnd, infoPtr);
121 return prevItem;
124 static LRESULT
125 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
127 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
128 INT iItem=(INT) wParam;
130 if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
132 infoPtr->uFocus=iItem;
133 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
134 FIXME("Should set input focus\n");
135 } else {
136 if (infoPtr->iSelected != iItem) {
137 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
138 infoPtr->iSelected = iItem;
139 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
141 TAB_EnsureSelectionVisible(hwnd, infoPtr);
142 TAB_InvalidateTabArea(hwnd, infoPtr);
146 return 0;
149 static LRESULT
150 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
152 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
154 if (infoPtr == NULL) return 0;
155 infoPtr->hwndToolTip = (HWND)wParam;
156 return 0;
159 /******************************************************************************
160 * TAB_InternalGetItemRect
162 * This method will calculate the rectangle representing a given tab item in
163 * client coordinates. This method takes scrolling into account.
165 * This method returns TRUE if the item is visible in the window and FALSE
166 * if it is completely outside the client area.
168 static BOOL TAB_InternalGetItemRect(
169 HWND hwnd,
170 TAB_INFO* infoPtr,
171 INT itemIndex,
172 RECT* itemRect,
173 RECT* selectedRect)
175 RECT tmpItemRect;
178 * Perform a sanity check and a trivial visibility check.
180 if ( (infoPtr->uNumItem <=0) ||
181 (itemIndex >= infoPtr->uNumItem) ||
182 (itemIndex < infoPtr->leftmostVisible) )
183 return FALSE;
186 * Avoid special cases in this procedure by assigning the "out"
187 * parameters if the caller didn't supply them
189 if (itemRect==NULL)
190 itemRect = &tmpItemRect;
193 * Retrieve the unmodified item rect.
195 *itemRect = infoPtr->items[itemIndex].rect;
198 * "scroll" it to make sure the item at the very left of the
199 * tab control is the leftmost visible tab.
201 OffsetRect(itemRect,
202 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
206 * Move the rectangle so the first item is slightly offset from
207 * the left of the tab control.
209 OffsetRect(itemRect,
210 SELECTED_TAB_OFFSET,
215 * Now, calculate the position of the item as if it were selected.
217 if (selectedRect!=NULL)
219 CopyRect(selectedRect, itemRect);
222 * The rectangle of a selected item is a bit wider.
224 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
227 * If it also a bit higher.
229 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
231 selectedRect->top -=2; /* the border is thicker on the bottom */
232 selectedRect->bottom +=SELECTED_TAB_OFFSET;
234 else
236 selectedRect->top -=SELECTED_TAB_OFFSET;
237 selectedRect->bottom+=1;
241 return TRUE;
244 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
246 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
247 (LPRECT)lParam, (LPRECT)NULL);
250 /******************************************************************************
251 * TAB_KeyUp
253 * This method is called to handle keyboard input
255 static LRESULT TAB_KeyUp(
256 HWND hwnd,
257 WPARAM keyCode)
259 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
260 int newItem = -1;
262 switch (keyCode)
264 case VK_LEFT:
265 newItem = infoPtr->uFocus-1;
266 break;
267 case VK_RIGHT:
268 newItem = infoPtr->uFocus+1;
269 break;
273 * If we changed to a valid item, change the selection
275 if ( (newItem >= 0) &&
276 (newItem < infoPtr->uNumItem) &&
277 (infoPtr->uFocus != newItem) )
279 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
281 infoPtr->iSelected = newItem;
282 infoPtr->uFocus = newItem;
283 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
285 TAB_EnsureSelectionVisible(hwnd, infoPtr);
286 TAB_InvalidateTabArea(hwnd, infoPtr);
290 return 0;
293 /******************************************************************************
294 * TAB_FocusChanging
296 * This method is called whenever the focus goes in or out of this control
297 * it is used to update the visual state of the control.
299 static LRESULT TAB_FocusChanging(
300 HWND hwnd,
301 UINT uMsg,
302 WPARAM wParam,
303 LPARAM lParam)
305 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
306 RECT selectedRect;
307 BOOL isVisible;
310 * Get the rectangle for the item.
312 isVisible = TAB_InternalGetItemRect(hwnd,
313 infoPtr,
314 infoPtr->uFocus,
315 NULL,
316 &selectedRect);
319 * If the rectangle is not completely invisible, invalidate that
320 * portion of the window.
322 if (isVisible)
324 InvalidateRect(hwnd, &selectedRect, TRUE);
328 * Don't otherwise disturb normal behavior.
330 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
333 static HWND TAB_InternalHitTest (
334 HWND hwnd,
335 TAB_INFO* infoPtr,
336 POINT pt,
337 UINT* flags)
340 RECT rect;
341 int iCount;
343 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
345 TAB_InternalGetItemRect(hwnd,
346 infoPtr,
347 iCount,
348 &rect,
349 NULL);
351 if (PtInRect (&rect, pt))
353 *flags = TCHT_ONITEM;
354 return iCount;
358 *flags=TCHT_NOWHERE;
359 return -1;
362 static LRESULT
363 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
365 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
366 LPTCHITTESTINFO lptest=(LPTCHITTESTINFO) lParam;
368 return TAB_InternalHitTest (hwnd, infoPtr,lptest->pt,&lptest->flags);
372 static LRESULT
373 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
375 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
376 POINT pt;
377 INT newItem,dummy;
379 if (infoPtr->hwndToolTip)
380 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
381 WM_LBUTTONDOWN, wParam, lParam);
383 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
384 SetFocus (hwnd);
387 if (infoPtr->hwndToolTip)
388 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
389 WM_LBUTTONDOWN, wParam, lParam);
391 pt.x = (INT)LOWORD(lParam);
392 pt.y = (INT)HIWORD(lParam);
394 newItem=TAB_InternalHitTest (hwnd, infoPtr,pt,&dummy);
396 TRACE("On Tab, item %d\n", newItem);
398 if ( (newItem!=-1) &&
399 (infoPtr->iSelected != newItem) )
401 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE)
403 infoPtr->iSelected = newItem;
404 infoPtr->uFocus = newItem;
405 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
407 TAB_EnsureSelectionVisible(hwnd, infoPtr);
409 TAB_InvalidateTabArea(hwnd, infoPtr);
412 return 0;
415 static LRESULT
416 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
418 TAB_SendSimpleNotify(hwnd, NM_CLICK);
420 return 0;
423 static LRESULT
424 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
426 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
427 return 0;
430 static LRESULT
431 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
433 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
435 if (infoPtr->hwndToolTip)
436 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
437 WM_LBUTTONDOWN, wParam, lParam);
438 return 0;
441 /******************************************************************************
442 * TAB_AdjustRect
444 * Calculates the tab control's display area given the windows rectangle or
445 * the window rectangle given the requested display rectangle.
447 static LRESULT TAB_AdjustRect(
448 HWND hwnd,
449 WPARAM fLarger,
450 LPRECT prc)
452 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
454 if (fLarger)
457 * Go from display rectangle
461 * Add the height of the tabs.
463 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
464 prc->bottom += infoPtr->tabHeight;
465 else
466 prc->top -= infoPtr->tabHeight;
469 * Inflate the rectangle for the padding
471 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
474 * Inflate for the border
476 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
478 else
481 * Go from window rectangle.
485 * Deflate the rectangle for the border
487 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
490 * Deflate the rectangle for the padding
492 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
495 * Remove the height of the tabs.
497 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
498 prc->bottom -= infoPtr->tabHeight;
499 else
500 prc->top += infoPtr->tabHeight;
504 return 0;
507 /******************************************************************************
508 * TAB_OnHScroll
510 * This method will handle the notification from the scroll control and
511 * perform the scrolling operation on the tab control.
513 static LRESULT TAB_OnHScroll(
514 HWND hwnd,
515 int nScrollCode,
516 int nPos,
517 HWND hwndScroll)
519 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
521 if (nScrollCode == SB_LINELEFT)
523 if (infoPtr->leftmostVisible>0)
525 infoPtr->leftmostVisible--;
527 TAB_InvalidateTabArea(hwnd, infoPtr);
530 else if (nScrollCode == SB_LINERIGHT)
532 if (infoPtr->leftmostVisible< (infoPtr->uNumItem-1))
534 infoPtr->leftmostVisible++;
536 TAB_InvalidateTabArea(hwnd, infoPtr);
540 return 0;
543 /******************************************************************************
544 * TAB_SetupScroling
546 * This method will check the current scrolling state and make sure the
547 * scrolling control is displayed (or not).
549 static void TAB_SetupScrolling(
550 HWND hwnd,
551 TAB_INFO* infoPtr,
552 const RECT* clientRect)
554 if (infoPtr->needsScrolling)
556 RECT controlPos;
559 * Calculate the position of the scroll control.
561 controlPos.right = clientRect->right;
562 controlPos.left = controlPos.right - 2*GetSystemMetrics(SM_CXHSCROLL);
564 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
566 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
567 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
569 else
571 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
572 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
576 * If we don't have a scroll control yet, we want to create one.
577 * If we have one, we want to make sure it's positioned right.
579 if (infoPtr->hwndUpDown==0)
582 * I use a scrollbar since it seems to be more stable than the Updown
583 * control.
585 infoPtr->hwndUpDown = CreateWindowA("ScrollBar",
587 WS_VISIBLE | WS_CHILD | WS_OVERLAPPED | SBS_HORZ,
588 controlPos.left, controlPos.top,
589 controlPos.right - controlPos.left,
590 controlPos.bottom - controlPos.top,
591 hwnd,
592 (HMENU)NULL,
593 (HINSTANCE)NULL,
594 NULL);
596 else
598 SetWindowPos(infoPtr->hwndUpDown,
599 (HWND)NULL,
600 controlPos.left, controlPos.top,
601 controlPos.right - controlPos.left,
602 controlPos.bottom - controlPos.top,
603 SWP_SHOWWINDOW | SWP_NOZORDER);
606 else
609 * If we once had a scroll control... hide it.
611 if (infoPtr->hwndUpDown!=0)
613 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
618 /******************************************************************************
619 * TAB_SetItemBounds
621 * This method will calculate the position rectangles of all the items in the
622 * control. The rectangle calculated starts at 0 for the first item in the
623 * list and ignores scrolling and selection.
624 * It also uses the current font to determine the height of the tab row and
625 * it checks if all the tabs fit in the client area of the window. If they
626 * dont, a scrolling control is added.
628 static void TAB_SetItemBounds (HWND hwnd)
630 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
631 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
632 TEXTMETRICA fontMetrics;
633 INT curItem;
634 INT curItemLeftPos;
635 HFONT hFont, hOldFont;
636 HDC hdc;
637 RECT clientRect;
638 SIZE size;
641 * We need to get text information so we need a DC and we need to select
642 * a font.
644 hdc = GetDC(hwnd);
646 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
647 hOldFont = SelectObject (hdc, hFont);
650 * We will base the rectangle calculations on the client rectangle
651 * of the control.
653 GetClientRect(hwnd, &clientRect);
656 * The leftmost item will be "0" aligned
658 curItemLeftPos = 0;
660 if (!((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED)))
662 int item_height;
663 int icon_height = 0;
666 * Use the current font to determine the height of a tab.
668 GetTextMetricsA(hdc, &fontMetrics);
671 * Get the icon height
673 if (infoPtr->himl)
674 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
677 * Take the highest between font or icon
679 if (fontMetrics.tmHeight > icon_height)
680 item_height = fontMetrics.tmHeight;
681 else
682 item_height = icon_height;
685 * Make sure there is enough space for the letters + icon + growing the
686 * selected item + extra space for the selected item.
688 infoPtr->tabHeight = item_height + 2*VERTICAL_ITEM_PADDING +
689 SELECTED_TAB_OFFSET;
692 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
695 * Calculate the vertical position of the tab
697 if (lStyle & TCS_BOTTOM)
699 infoPtr->items[curItem].rect.bottom = clientRect.bottom -
700 SELECTED_TAB_OFFSET;
701 infoPtr->items[curItem].rect.top = clientRect.bottom -
702 infoPtr->tabHeight;
704 else
706 infoPtr->items[curItem].rect.top = clientRect.top +
707 SELECTED_TAB_OFFSET;
708 infoPtr->items[curItem].rect.bottom = clientRect.top +
709 infoPtr->tabHeight;
713 * Set the leftmost position of the tab.
715 infoPtr->items[curItem].rect.left = curItemLeftPos;
717 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
719 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
720 infoPtr->tabWidth +
721 2*HORIZONTAL_ITEM_PADDING;
723 else
725 int icon_width = 0;
726 int num = 2;
729 * Calculate how wide the tab is depending on the text it contains
731 GetTextExtentPoint32A(hdc, infoPtr->items[curItem].pszText,
732 lstrlenA(infoPtr->items[curItem].pszText), &size);
735 * Add the icon width
737 if (infoPtr->himl)
739 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
740 num++;
743 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
744 size.cx + icon_width +
745 num*HORIZONTAL_ITEM_PADDING;
748 TRACE("TextSize: %i\n ", size.cx);
749 TRACE("Rect: T %i, L %i, B %i, R %i\n",
750 infoPtr->items[curItem].rect.top,
751 infoPtr->items[curItem].rect.left,
752 infoPtr->items[curItem].rect.bottom,
753 infoPtr->items[curItem].rect.right);
756 * The leftmost position of the next item is the rightmost position
757 * of this one.
759 if (lStyle & TCS_BUTTONS)
760 curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX;
761 else
762 curItemLeftPos = infoPtr->items[curItem].rect.right;
766 * Check if we need a scrolling control.
768 infoPtr->needsScrolling = (curItemLeftPos + (2*SELECTED_TAB_OFFSET) >
769 clientRect.right);
771 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
774 * Cleanup
776 SelectObject (hdc, hOldFont);
777 ReleaseDC (hwnd, hdc);
780 /******************************************************************************
781 * TAB_DrawItem
783 * This method is used to draw a single tab into the tab control.
785 static void TAB_DrawItem(
786 HWND hwnd,
787 HDC hdc,
788 INT iItem)
790 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
791 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
792 RECT itemRect;
793 RECT selectedRect;
794 BOOL isVisible;
795 RECT r;
798 * Get the rectangle for the item.
800 isVisible = TAB_InternalGetItemRect(hwnd,
801 infoPtr,
802 iItem,
803 &itemRect,
804 &selectedRect);
806 if (isVisible)
808 HBRUSH hbr = CreateSolidBrush (GetSysColor(COLOR_BTNFACE));
809 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
810 HPEN hbPen = GetSysColorPen (COLOR_BTNSHADOW);
811 HPEN hsdPen = GetSysColorPen (COLOR_BTNTEXT);
812 HPEN hfocusPen = CreatePen(PS_DOT, 1, GetSysColor(COLOR_BTNTEXT));
813 HPEN holdPen;
814 INT oldBkMode;
815 INT cx,cy;
816 BOOL deleteBrush;
818 if (lStyle & TCS_BUTTONS)
821 * Get item rectangle.
823 r = itemRect;
825 holdPen = SelectObject (hdc, hwPen);
827 if (iItem == infoPtr->iSelected)
830 * Background color.
832 if (!(lStyle & TCS_OWNERDRAWFIXED))
834 COLORREF bk = GetSysColor(COLOR_3DHILIGHT);
835 DeleteObject(hbr);
836 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
837 SetTextColor(hdc, GetSysColor(COLOR_3DFACE));
838 SetBkColor(hdc, bk);
840 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
841 * we better use 0x55aa bitmap brush to make scrollbar's background
842 * look different from the window background.
844 if (bk == GetSysColor(COLOR_WINDOW))
845 hbr = CACHE_GetPattern55AABrush();
847 deleteBrush = FALSE;
851 * Erase the background.
853 FillRect(hdc, &r, hbr);
856 * Draw the tab now.
857 * The rectangles calculated exclude the right and bottom
858 * borders of the rectangle. To simply the following code, those
859 * borders are shaved-off beforehand.
861 r.right--;
862 r.bottom--;
864 /* highlight */
865 MoveToEx (hdc, r.left, r.bottom, NULL);
866 LineTo (hdc, r.right, r.bottom);
867 LineTo (hdc, r.right, r.top);
869 /* shadow */
870 SelectObject(hdc, hbPen);
871 LineTo (hdc, r.left, r.top);
872 LineTo (hdc, r.left, r.bottom);
874 else
877 * Erase the background.
879 FillRect(hdc, &r, hbr);
881 /* highlight */
882 MoveToEx (hdc, r.left, r.bottom, NULL);
883 LineTo (hdc, r.left, r.top);
884 LineTo (hdc, r.right, r.top);
886 /* shadow */
887 SelectObject(hdc, hbPen);
888 LineTo (hdc, r.right, r.bottom);
889 LineTo (hdc, r.left, r.bottom);
892 else
895 * Background color.
897 DeleteObject(hbr);
898 hbr = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
901 * We draw a rectangle of different sizes depending on the selection
902 * state.
904 if (iItem == infoPtr->iSelected)
905 r = selectedRect;
906 else
907 r = itemRect;
910 * Erase the background.
911 * This is necessary when drawing the selected item since it is larger
912 * than the others, it might overlap with stuff already drawn by the
913 * other tabs
915 FillRect(hdc, &r, hbr);
918 * Draw the tab now.
919 * The rectangles calculated exclude the right and bottom
920 * borders of the rectangle. To simply the following code, those
921 * borders are shaved-off beforehand.
923 r.right--;
924 r.bottom--;
926 holdPen = SelectObject (hdc, hwPen);
928 if (lStyle & TCS_BOTTOM)
930 /* highlight */
931 MoveToEx (hdc, r.left, r.top, NULL);
932 LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE);
933 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom);
935 /* shadow */
936 SelectObject(hdc, hbPen);
937 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom);
938 LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE);
939 LineTo (hdc, r.right, r.top);
941 else
943 /* highlight */
944 MoveToEx (hdc, r.left, r.bottom, NULL);
945 LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE);
946 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top);
947 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top);
949 /* shadow */
950 SelectObject(hdc, hbPen);
951 LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE);
952 LineTo (hdc, r.right, r.bottom);
957 * Text pen
959 SelectObject(hdc, hsdPen);
961 oldBkMode = SetBkMode(hdc, TRANSPARENT);
962 SetTextColor (hdc, COLOR_BTNTEXT);
965 * Deflate the rectangle to acount for the padding
967 InflateRect(&r, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
970 * Draw the icon.
972 if (infoPtr->himl)
974 ImageList_Draw (infoPtr->himl, iItem, hdc,
975 r.left, r.top+1, ILD_NORMAL);
976 ImageList_GetIconSize (infoPtr->himl, &cx, &cy);
977 r.left+=(cx + HORIZONTAL_ITEM_PADDING);
981 * Draw the text;
983 DrawTextA(hdc,
984 infoPtr->items[iItem].pszText,
985 lstrlenA(infoPtr->items[iItem].pszText),
986 &r,
987 DT_LEFT|DT_SINGLELINE|DT_VCENTER);
990 * Draw the focus rectangle
992 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
993 (GetFocus() == hwnd) &&
994 (iItem == infoPtr->uFocus) )
996 InflateRect(&r, FOCUS_RECT_HOFFSET, FOCUS_RECT_VOFFSET);
998 SelectObject(hdc, hfocusPen);
1000 MoveToEx (hdc, r.left, r.top, NULL);
1001 LineTo (hdc, r.right-1, r.top);
1002 LineTo (hdc, r.right-1, r.bottom -1);
1003 LineTo (hdc, r.left, r.bottom -1);
1004 LineTo (hdc, r.left, r.top);
1008 * Cleanup
1010 SetBkMode(hdc, oldBkMode);
1011 SelectObject(hdc, holdPen);
1012 DeleteObject(hfocusPen);
1013 if (deleteBrush) DeleteObject(hbr);
1017 /******************************************************************************
1018 * TAB_DrawBorder
1020 * This method is used to draw the raised border around the tab control
1021 * "content" area.
1023 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
1025 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1026 HPEN htmPen;
1027 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
1028 HPEN hbPen = GetSysColorPen (COLOR_3DDKSHADOW);
1029 HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW);
1030 RECT rect;
1032 GetClientRect (hwnd, &rect);
1035 * Adjust for the style
1037 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1039 rect.bottom -= infoPtr->tabHeight;
1041 else
1043 rect.top += infoPtr->tabHeight;
1047 * Shave-off the right and bottom margins (exluded in the
1048 * rect)
1050 rect.right--;
1051 rect.bottom--;
1053 /* highlight */
1054 htmPen = SelectObject (hdc, hwPen);
1056 MoveToEx (hdc, rect.left, rect.bottom, NULL);
1057 LineTo (hdc, rect.left, rect.top);
1058 LineTo (hdc, rect.right, rect.top);
1060 /* Dark Shadow */
1061 SelectObject (hdc, hbPen);
1062 LineTo (hdc, rect.right, rect.bottom );
1063 LineTo (hdc, rect.left, rect.bottom);
1065 /* shade */
1066 SelectObject (hdc, hShade );
1067 MoveToEx (hdc, rect.right-1, rect.top, NULL);
1068 LineTo (hdc, rect.right-1, rect.bottom-1);
1069 LineTo (hdc, rect.left, rect.bottom-1);
1071 SelectObject(hdc, htmPen);
1074 /******************************************************************************
1075 * TAB_Refresh
1077 * This method repaints the tab control..
1079 static void TAB_Refresh (HWND hwnd, HDC hdc)
1081 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1082 HFONT hOldFont;
1083 INT i;
1085 if (!infoPtr->DoRedraw)
1086 return;
1088 hOldFont = SelectObject (hdc, infoPtr->hFont);
1090 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
1092 for (i = 0; i < infoPtr->uNumItem; i++)
1094 TAB_DrawItem (hwnd, hdc, i);
1097 else
1100 * Draw all the non selected item first.
1102 for (i = 0; i < infoPtr->uNumItem; i++)
1104 if (i != infoPtr->iSelected)
1105 TAB_DrawItem (hwnd, hdc, i);
1109 * Now, draw the border, draw it before the selected item
1110 * since the selected item overwrites part of the border.
1112 TAB_DrawBorder (hwnd, hdc);
1115 * Then, draw the selected item
1117 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
1120 SelectObject (hdc, hOldFont);
1123 static LRESULT
1124 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
1126 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1128 infoPtr->DoRedraw=(BOOL) wParam;
1129 return 0;
1132 static LRESULT TAB_EraseBackground(
1133 HWND hwnd,
1134 HDC givenDC)
1136 HDC hdc;
1137 RECT clientRect;
1139 HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1141 hdc = givenDC ? givenDC : GetDC(hwnd);
1143 GetClientRect(hwnd, &clientRect);
1145 FillRect(hdc, &clientRect, brush);
1147 if (givenDC==0)
1148 ReleaseDC(hwnd, hdc);
1150 DeleteObject(brush);
1152 return 0;
1155 /******************************************************************************
1156 * TAB_EnsureSelectionVisible
1158 * This method will make sure that the current selection is completely
1159 * visible by scrolling until it is.
1161 static void TAB_EnsureSelectionVisible(
1162 HWND hwnd,
1163 TAB_INFO* infoPtr)
1165 RECT selectedRect;
1166 RECT visibleRect;
1167 RECT scrollerRect;
1168 BOOL isVisible;
1171 * Do the trivial cases first.
1173 if ( (!infoPtr->needsScrolling) ||
1174 (infoPtr->hwndUpDown==0) )
1175 return;
1177 if (infoPtr->leftmostVisible > infoPtr->iSelected)
1179 infoPtr->leftmostVisible = infoPtr->iSelected;
1180 return;
1184 * Calculate the part of the client area that is visible.
1186 GetClientRect(hwnd, &visibleRect);
1187 GetClientRect(infoPtr->hwndUpDown, &scrollerRect);
1188 visibleRect.right -= scrollerRect.right;
1191 * Get the rectangle for the item
1193 isVisible = TAB_InternalGetItemRect(hwnd,
1194 infoPtr,
1195 infoPtr->iSelected,
1196 NULL,
1197 &selectedRect);
1200 * If this function can't say it's completely invisible, maybe it
1201 * is partially visible. Let's check.
1203 if (isVisible)
1205 POINT pt1;
1206 POINT pt2;
1208 pt1.x = selectedRect.left;
1209 pt1.y = selectedRect.top;
1210 pt2.x = selectedRect.right - 1;
1211 pt2.y = selectedRect.bottom - 1;
1213 isVisible = PtInRect(&visibleRect, pt1) && PtInRect(&visibleRect, pt2);
1216 while ( (infoPtr->leftmostVisible < infoPtr->iSelected) &&
1217 !isVisible)
1219 infoPtr->leftmostVisible++;
1222 * Get the rectangle for the item
1224 isVisible = TAB_InternalGetItemRect(hwnd,
1225 infoPtr,
1226 infoPtr->iSelected,
1227 NULL,
1228 &selectedRect);
1231 * If this function can't say it's completely invisible, maybe it
1232 * is partially visible. Let's check.
1234 if (isVisible)
1236 POINT pt1;
1237 POINT pt2;
1239 pt1.x = selectedRect.left;
1240 pt1.y = selectedRect.top;
1241 pt2.x = selectedRect.right - 1;
1242 pt2.y = selectedRect.bottom - 1;
1244 isVisible = PtInRect(&visibleRect, pt1) && PtInRect(&visibleRect, pt2);
1249 /******************************************************************************
1250 * TAB_InvalidateTabArea
1252 * This method will invalidate the portion of the control that contains the
1253 * tabs. It is called when the state of the control changes and needs
1254 * to be redisplayed
1256 static void TAB_InvalidateTabArea(
1257 HWND hwnd,
1258 TAB_INFO* infoPtr)
1260 RECT clientRect;
1262 GetClientRect(hwnd, &clientRect);
1264 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1266 clientRect.top = clientRect.bottom - (infoPtr->tabHeight + 1);
1268 else
1270 clientRect.bottom = clientRect.top + (infoPtr->tabHeight + 1);
1273 InvalidateRect(hwnd, &clientRect, TRUE);
1276 static LRESULT
1277 TAB_Paint (HWND hwnd, WPARAM wParam)
1279 HDC hdc;
1280 PAINTSTRUCT ps;
1282 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
1283 TAB_Refresh (hwnd, hdc);
1285 if(!wParam)
1286 EndPaint (hwnd, &ps);
1288 return 0;
1291 static LRESULT
1292 TAB_InsertItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1294 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1295 TCITEMA *pti;
1296 INT iItem, len;
1297 RECT rect;
1299 GetClientRect (hwnd, &rect);
1300 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
1301 rect.top, rect.left, rect.bottom, rect.right);
1303 pti = (TCITEMA *)lParam;
1304 iItem = (INT)wParam;
1306 if (iItem < 0) return -1;
1307 if (iItem > infoPtr->uNumItem)
1308 iItem = infoPtr->uNumItem;
1310 if (infoPtr->uNumItem == 0) {
1311 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
1312 infoPtr->uNumItem++;
1313 infoPtr->iSelected = 0;
1315 else {
1316 TAB_ITEM *oldItems = infoPtr->items;
1318 infoPtr->uNumItem++;
1319 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
1321 /* pre insert copy */
1322 if (iItem > 0) {
1323 memcpy (&infoPtr->items[0], &oldItems[0],
1324 iItem * sizeof(TAB_ITEM));
1327 /* post insert copy */
1328 if (iItem < infoPtr->uNumItem - 1) {
1329 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
1330 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
1334 if (iItem <= infoPtr->iSelected)
1335 infoPtr->iSelected++;
1337 COMCTL32_Free (oldItems);
1340 infoPtr->items[iItem].mask = pti->mask;
1341 if (pti->mask & TCIF_TEXT) {
1342 len = lstrlenA (pti->pszText);
1343 infoPtr->items[iItem].pszText = COMCTL32_Alloc (len+1);
1344 lstrcpyA (infoPtr->items[iItem].pszText, pti->pszText);
1345 infoPtr->items[iItem].cchTextMax = pti->cchTextMax;
1348 if (pti->mask & TCIF_IMAGE)
1349 infoPtr->items[iItem].iImage = pti->iImage;
1351 if (pti->mask & TCIF_PARAM)
1352 infoPtr->items[iItem].lParam = pti->lParam;
1354 TAB_InvalidateTabArea(hwnd, infoPtr);
1356 TRACE("[%04x]: added item %d '%s'\n",
1357 hwnd, iItem, infoPtr->items[iItem].pszText);
1359 TAB_SetItemBounds(hwnd);
1360 return iItem;
1363 static LRESULT
1364 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
1366 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1367 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1368 LONG lResult = 0;
1370 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
1372 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
1373 infoPtr->tabWidth = (INT)LOWORD(lParam);
1374 infoPtr->tabHeight = (INT)HIWORD(lParam);
1377 return lResult;
1380 static LRESULT
1381 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1383 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1384 TCITEMA *tabItem;
1385 TAB_ITEM *wineItem;
1386 INT iItem,len;
1388 iItem=(INT) wParam;
1389 tabItem=(LPTCITEMA ) lParam;
1390 TRACE("%d %p\n",iItem, tabItem);
1391 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
1393 wineItem=& infoPtr->items[iItem];
1395 if (tabItem->mask & TCIF_IMAGE)
1396 wineItem->iImage=tabItem->iImage;
1398 if (tabItem->mask & TCIF_PARAM)
1399 wineItem->lParam=tabItem->lParam;
1401 if (tabItem->mask & TCIF_RTLREADING)
1402 FIXME("TCIF_RTLREADING\n");
1404 if (tabItem->mask & TCIF_STATE)
1405 wineItem->dwState=tabItem->dwState;
1407 if (tabItem->mask & TCIF_TEXT) {
1408 len=lstrlenA (tabItem->pszText);
1409 if (len>wineItem->cchTextMax)
1410 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
1411 lstrcpyA (wineItem->pszText, tabItem->pszText);
1415 * Update and repaint tabs.
1417 TAB_SetItemBounds(hwnd);
1418 TAB_InvalidateTabArea(hwnd,infoPtr);
1420 return TRUE;
1423 static LRESULT
1424 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1426 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1428 return infoPtr->uNumItem;
1432 static LRESULT
1433 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1435 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1436 TCITEMA *tabItem;
1437 TAB_ITEM *wineItem;
1438 INT iItem;
1440 iItem=(INT) wParam;
1441 tabItem=(LPTCITEMA) lParam;
1442 TRACE("\n");
1443 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
1445 wineItem=& infoPtr->items[iItem];
1447 if (tabItem->mask & TCIF_IMAGE)
1448 tabItem->iImage=wineItem->iImage;
1450 if (tabItem->mask & TCIF_PARAM)
1451 tabItem->lParam=wineItem->lParam;
1453 if (tabItem->mask & TCIF_RTLREADING)
1454 FIXME("TCIF_RTLREADING\n");
1456 if (tabItem->mask & TCIF_STATE)
1457 tabItem->dwState=wineItem->dwState;
1459 if (tabItem->mask & TCIF_TEXT)
1460 lstrcpynA (tabItem->pszText, wineItem->pszText, tabItem->cchTextMax);
1462 return TRUE;
1465 static LRESULT
1466 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1468 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1469 INT iItem = (INT) wParam;
1470 BOOL bResult = FALSE;
1472 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
1474 TAB_ITEM *oldItems = infoPtr->items;
1476 infoPtr->uNumItem--;
1477 infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
1479 if (iItem > 0)
1480 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
1482 if (iItem < infoPtr->uNumItem)
1483 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
1484 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
1486 COMCTL32_Free (oldItems);
1489 * Readjust the selected index.
1491 if ((iItem == infoPtr->iSelected) && (iItem > 0))
1492 infoPtr->iSelected--;
1494 if (iItem < infoPtr->iSelected)
1495 infoPtr->iSelected--;
1497 if (infoPtr->uNumItem == 0)
1498 infoPtr->iSelected = -1;
1501 * Reposition and repaint tabs.
1503 TAB_SetItemBounds(hwnd);
1504 TAB_InvalidateTabArea(hwnd,infoPtr);
1506 bResult = TRUE;
1509 return bResult;
1512 static LRESULT
1513 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
1515 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1517 COMCTL32_Free (infoPtr->items);
1518 infoPtr->uNumItem = 0;
1519 infoPtr->iSelected = -1;
1521 return TRUE;
1525 static LRESULT
1526 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1528 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1530 TRACE("\n");
1531 return (LRESULT)infoPtr->hFont;
1534 static LRESULT
1535 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1538 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1540 TRACE("%x %lx\n",wParam, lParam);
1542 infoPtr->hFont = (HFONT)wParam;
1544 TAB_SetItemBounds(hwnd);
1546 TAB_InvalidateTabArea(hwnd, infoPtr);
1548 return 0;
1552 static LRESULT
1553 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1555 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1557 TRACE("\n");
1558 return (LRESULT)infoPtr->himl;
1561 static LRESULT
1562 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1564 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1565 HIMAGELIST himlPrev;
1567 TRACE("\n");
1568 himlPrev = infoPtr->himl;
1569 infoPtr->himl= (HIMAGELIST)lParam;
1570 return (LRESULT)himlPrev;
1574 static LRESULT
1575 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1578 /* I'm not really sure what the following code was meant to do.
1579 This is what it is doing:
1580 When WM_SIZE is sent with SIZE_RESTORED, the control
1581 gets positioned in the top left corner.
1583 RECT parent_rect;
1584 HWND parent;
1585 UINT uPosFlags,cx,cy;
1587 uPosFlags=0;
1588 if (!wParam) {
1589 parent = GetParent (hwnd);
1590 GetClientRect(parent, &parent_rect);
1591 cx=LOWORD (lParam);
1592 cy=HIWORD (lParam);
1593 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
1594 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
1596 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
1597 cx, cy, uPosFlags | SWP_NOZORDER);
1598 } else {
1599 FIXME (tab,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
1600 } */
1603 * Recompute the size/position of the tabs.
1605 TAB_SetItemBounds (hwnd);
1608 * Force a repaint of the control.
1610 InvalidateRect(hwnd, NULL, TRUE);
1612 return 0;
1616 static LRESULT
1617 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
1619 TAB_INFO *infoPtr;
1620 TEXTMETRICA fontMetrics;
1621 HDC hdc;
1622 HFONT hOldFont;
1624 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
1626 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
1628 infoPtr->uNumItem = 0;
1629 infoPtr->hFont = 0;
1630 infoPtr->items = 0;
1631 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
1632 infoPtr->iSelected = -1;
1633 infoPtr->uFocus = 0;
1634 infoPtr->hwndToolTip = 0;
1635 infoPtr->DoRedraw = TRUE;
1636 infoPtr->needsScrolling = FALSE;
1637 infoPtr->hwndUpDown = 0;
1638 infoPtr->leftmostVisible = 0;
1640 TRACE("Created tab control, hwnd [%04x]\n", hwnd);
1641 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_TOOLTIPS) {
1642 /* Create tooltip control */
1643 infoPtr->hwndToolTip =
1644 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
1645 CW_USEDEFAULT, CW_USEDEFAULT,
1646 CW_USEDEFAULT, CW_USEDEFAULT,
1647 hwnd, 0, 0, 0);
1649 /* Send NM_TOOLTIPSCREATED notification */
1650 if (infoPtr->hwndToolTip) {
1651 NMTOOLTIPSCREATED nmttc;
1653 nmttc.hdr.hwndFrom = hwnd;
1654 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1655 nmttc.hdr.code = NM_TOOLTIPSCREATED;
1656 nmttc.hwndToolTips = infoPtr->hwndToolTip;
1658 SendMessageA (GetParent (hwnd), WM_NOTIFY,
1659 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
1664 * We need to get text information so we need a DC and we need to select
1665 * a font.
1667 hdc = GetDC(hwnd);
1668 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
1671 * Use the system font to determine the initial height of a tab.
1673 GetTextMetricsA(hdc, &fontMetrics);
1676 * Make sure there is enough space for the letters + growing the
1677 * selected item + extra space for the selected item.
1679 infoPtr->tabHeight = fontMetrics.tmHeight + 2*VERTICAL_ITEM_PADDING +
1680 SELECTED_TAB_OFFSET;
1683 * Initialize the width of a tab.
1685 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
1687 SelectObject (hdc, hOldFont);
1688 ReleaseDC(hwnd, hdc);
1690 return 0;
1693 static LRESULT
1694 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
1696 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1697 INT iItem;
1699 if (infoPtr->items) {
1700 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
1701 if (infoPtr->items[iItem].pszText)
1702 COMCTL32_Free (infoPtr->items[iItem].pszText);
1704 COMCTL32_Free (infoPtr->items);
1707 if (infoPtr->hwndToolTip)
1708 DestroyWindow (infoPtr->hwndToolTip);
1710 if (infoPtr->hwndUpDown)
1711 DestroyWindow(infoPtr->hwndUpDown);
1713 COMCTL32_Free (infoPtr);
1714 return 0;
1717 static LRESULT WINAPI
1718 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1720 switch (uMsg)
1722 case TCM_GETIMAGELIST:
1723 return TAB_GetImageList (hwnd, wParam, lParam);
1725 case TCM_SETIMAGELIST:
1726 return TAB_SetImageList (hwnd, wParam, lParam);
1728 case TCM_GETITEMCOUNT:
1729 return TAB_GetItemCount (hwnd, wParam, lParam);
1731 case TCM_GETITEMA:
1732 return TAB_GetItemA (hwnd, wParam, lParam);
1734 case TCM_GETITEMW:
1735 FIXME("Unimplemented msg TCM_GETITEMW\n");
1736 return 0;
1738 case TCM_SETITEMA:
1739 return TAB_SetItemA (hwnd, wParam, lParam);
1741 case TCM_SETITEMW:
1742 FIXME("Unimplemented msg TCM_SETITEMW\n");
1743 return 0;
1745 case TCM_DELETEITEM:
1746 return TAB_DeleteItem (hwnd, wParam, lParam);
1748 case TCM_DELETEALLITEMS:
1749 return TAB_DeleteAllItems (hwnd, wParam, lParam);
1751 case TCM_GETITEMRECT:
1752 return TAB_GetItemRect (hwnd, wParam, lParam);
1754 case TCM_GETCURSEL:
1755 return TAB_GetCurSel (hwnd);
1757 case TCM_HITTEST:
1758 return TAB_HitTest (hwnd, wParam, lParam);
1760 case TCM_SETCURSEL:
1761 return TAB_SetCurSel (hwnd, wParam);
1763 case TCM_INSERTITEMA:
1764 return TAB_InsertItem (hwnd, wParam, lParam);
1766 case TCM_INSERTITEMW:
1767 FIXME("Unimplemented msg TCM_INSERTITEM32W\n");
1768 return 0;
1770 case TCM_SETITEMEXTRA:
1771 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
1772 return 0;
1774 case TCM_ADJUSTRECT:
1775 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
1777 case TCM_SETITEMSIZE:
1778 return TAB_SetItemSize (hwnd, wParam, lParam);
1780 case TCM_REMOVEIMAGE:
1781 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
1782 return 0;
1784 case TCM_SETPADDING:
1785 FIXME("Unimplemented msg TCM_SETPADDING\n");
1786 return 0;
1788 case TCM_GETROWCOUNT:
1789 FIXME("Unimplemented msg TCM_GETROWCOUNT\n");
1790 return 0;
1792 case TCM_GETUNICODEFORMAT:
1793 FIXME("Unimplemented msg TCM_GETUNICODEFORMAT\n");
1794 return 0;
1796 case TCM_SETUNICODEFORMAT:
1797 FIXME("Unimplemented msg TCM_SETUNICODEFORMAT\n");
1798 return 0;
1800 case TCM_HIGHLIGHTITEM:
1801 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
1802 return 0;
1804 case TCM_GETTOOLTIPS:
1805 return TAB_GetToolTips (hwnd, wParam, lParam);
1807 case TCM_SETTOOLTIPS:
1808 return TAB_SetToolTips (hwnd, wParam, lParam);
1810 case TCM_GETCURFOCUS:
1811 return TAB_GetCurFocus (hwnd);
1813 case TCM_SETCURFOCUS:
1814 return TAB_SetCurFocus (hwnd, wParam);
1816 case TCM_SETMINTTABWIDTH:
1817 FIXME("Unimplemented msg TCM_SETMINTTABWIDTH\n");
1818 return 0;
1820 case TCM_DESELECTALL:
1821 FIXME("Unimplemented msg TCM_DESELECTALL\n");
1822 return 0;
1824 case TCM_GETEXTENDEDSTYLE:
1825 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
1826 return 0;
1828 case TCM_SETEXTENDEDSTYLE:
1829 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
1830 return 0;
1832 case WM_GETFONT:
1833 return TAB_GetFont (hwnd, wParam, lParam);
1835 case WM_SETFONT:
1836 return TAB_SetFont (hwnd, wParam, lParam);
1838 case WM_CREATE:
1839 return TAB_Create (hwnd, wParam, lParam);
1841 case WM_NCDESTROY:
1842 return TAB_Destroy (hwnd, wParam, lParam);
1844 case WM_GETDLGCODE:
1845 return DLGC_WANTARROWS | DLGC_WANTCHARS;
1847 case WM_LBUTTONDOWN:
1848 return TAB_LButtonDown (hwnd, wParam, lParam);
1850 case WM_LBUTTONUP:
1851 return TAB_LButtonUp (hwnd, wParam, lParam);
1853 case WM_RBUTTONDOWN:
1854 return TAB_RButtonDown (hwnd, wParam, lParam);
1856 case WM_MOUSEMOVE:
1857 return TAB_MouseMove (hwnd, wParam, lParam);
1859 case WM_ERASEBKGND:
1860 return TAB_EraseBackground (hwnd, (HDC)wParam);
1862 case WM_PAINT:
1863 return TAB_Paint (hwnd, wParam);
1865 case WM_SIZE:
1866 return TAB_Size (hwnd, wParam, lParam);
1868 case WM_SETREDRAW:
1869 return TAB_SetRedraw (hwnd, wParam);
1871 case WM_HSCROLL:
1872 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
1874 case WM_KILLFOCUS:
1875 case WM_SETFOCUS:
1876 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
1878 case WM_KEYUP:
1879 return TAB_KeyUp(hwnd, wParam);
1881 default:
1882 if (uMsg >= WM_USER)
1883 ERR("unknown msg %04x wp=%08x lp=%08lx\n",
1884 uMsg, wParam, lParam);
1885 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1888 return 0;
1892 VOID
1893 TAB_Register (void)
1895 WNDCLASSA wndClass;
1897 if (GlobalFindAtomA (WC_TABCONTROLA)) return;
1899 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1900 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS;
1901 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
1902 wndClass.cbClsExtra = 0;
1903 wndClass.cbWndExtra = sizeof(TAB_INFO *);
1904 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
1905 wndClass.hbrBackground = (HBRUSH)NULL;
1906 wndClass.lpszClassName = WC_TABCONTROLA;
1908 RegisterClassA (&wndClass);
1912 VOID
1913 TAB_Unregister (void)
1915 if (GlobalFindAtomA (WC_TABCONTROLA))
1916 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);