4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
18 #include "debugtools.h"
21 DEFAULT_DEBUG_CHANNEL(tab);
31 RECT rect; /* bounding rectangle of the item relative to the
32 * leftmost item (the leftmost item, 0, would have a
33 * "left" member of 0 in this rectangle)
35 * additionally the top member hold the row number
36 * and bottom is unused and should be 0 */
41 UINT uNumItem; /* number of tab items */
42 UINT uNumRows; /* number of tab rows */
43 INT tabHeight; /* height of the tab row */
44 INT tabWidth; /* width of tabs */
45 HFONT hFont; /* handle to the current font */
46 HCURSOR hcurArrow; /* handle to the current cursor */
47 HIMAGELIST himl; /* handle to a image list (may be 0) */
48 HWND hwndToolTip; /* handle to tab's tooltip */
50 INT leftmostVisible; /* Used for scrolling, this member contains
51 * the index of the first visible item */
52 INT iSelected; /* the currently selected item */
53 INT iHotTracked; /* the highlighted item under the mouse */
54 INT uFocus; /* item which has the focus */
55 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
56 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
57 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
58 * the size of the control */
59 BOOL fSizeSet; /* was the size of the tabs explicitly set? */
60 HWND hwndUpDown; /* Updown control used for scrolling */
63 /******************************************************************************
64 * Positioning constants
66 #define SELECTED_TAB_OFFSET 2
67 #define HORIZONTAL_ITEM_PADDING 5
68 #define VERTICAL_ITEM_PADDING 3
69 #define ROUND_CORNER_SIZE 2
70 #define FOCUS_RECT_HOFFSET 2
71 #define FOCUS_RECT_VOFFSET 1
72 #define DISPLAY_AREA_PADDINGX 2
73 #define DISPLAY_AREA_PADDINGY 2
74 #define CONTROL_BORDER_SIZEX 2
75 #define CONTROL_BORDER_SIZEY 2
76 #define BUTTON_SPACINGX 10
77 #define DEFAULT_TAB_WIDTH 96
79 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
81 /******************************************************************************
82 * Hot-tracking timer constants
84 #define TAB_HOTTRACK_TIMER 1
85 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
87 /******************************************************************************
90 static void TAB_Refresh (HWND hwnd, HDC hdc);
91 static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
92 static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
93 static void TAB_DrawItem(HWND hwnd, HDC hdc, INT iItem);
94 static void TAB_DrawItemInterior(HWND hwnd, HDC hdc, INT iItem, RECT* drawRect);
97 TAB_SendSimpleNotify (HWND hwnd, UINT code)
101 nmhdr.hwndFrom = hwnd;
102 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
105 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
106 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
111 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
112 WPARAM wParam, LPARAM lParam)
120 msg.time = GetMessageTime ();
121 msg.pt.x = LOWORD(GetMessagePos ());
122 msg.pt.y = HIWORD(GetMessagePos ());
124 SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
130 TAB_GetCurSel (HWND hwnd)
132 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
134 return infoPtr->iSelected;
138 TAB_GetCurFocus (HWND hwnd)
140 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
142 return infoPtr->uFocus;
146 TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
148 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
150 if (infoPtr == NULL) return 0;
151 return infoPtr->hwndToolTip;
156 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
158 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
159 INT iItem=(INT) wParam;
163 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
164 prevItem=infoPtr->iSelected;
165 infoPtr->iSelected=iItem;
166 TAB_EnsureSelectionVisible(hwnd, infoPtr);
167 TAB_InvalidateTabArea(hwnd, infoPtr);
173 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
175 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
176 INT iItem=(INT) wParam;
178 if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
180 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
181 FIXME("Should set input focus\n");
183 if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) {
184 infoPtr->uFocus=iItem;
185 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
186 infoPtr->iSelected = iItem;
187 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
189 TAB_EnsureSelectionVisible(hwnd, infoPtr);
190 TAB_InvalidateTabArea(hwnd, infoPtr);
198 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
200 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
202 if (infoPtr == NULL) return 0;
203 infoPtr->hwndToolTip = (HWND)wParam;
207 /******************************************************************************
208 * TAB_InternalGetItemRect
210 * This method will calculate the rectangle representing a given tab item in
211 * client coordinates. This method takes scrolling into account.
213 * This method returns TRUE if the item is visible in the window and FALSE
214 * if it is completely outside the client area.
216 static BOOL TAB_InternalGetItemRect(
223 RECT tmpItemRect,clientRect;
224 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
227 * Perform a sanity check and a trivial visibility check.
229 if ( (infoPtr->uNumItem <=0) ||
230 (itemIndex >= infoPtr->uNumItem) ||
231 (!(lStyle &TCS_MULTILINE) && (itemIndex < infoPtr->leftmostVisible)) )
235 * Avoid special cases in this procedure by assigning the "out"
236 * parameters if the caller didn't supply them
239 itemRect = &tmpItemRect;
242 * Retrieve the unmodified item rect.
244 *itemRect = infoPtr->items[itemIndex].rect;
247 * calculate the times bottom and top based on the row
249 GetClientRect(hwnd, &clientRect);
251 if (lStyle & TCS_BOTTOM)
253 itemRect->bottom = clientRect.bottom -
254 SELECTED_TAB_OFFSET -
255 itemRect->top * (infoPtr->tabHeight - 2);
257 itemRect->top = clientRect.bottom -
259 itemRect->top * ( infoPtr->tabHeight - 2);
263 itemRect->bottom = clientRect.top +
265 itemRect->top * (infoPtr->tabHeight - 2);
266 itemRect->top = clientRect.top +
268 itemRect->top * (infoPtr->tabHeight - 2);
272 * "scroll" it to make sure the item at the very left of the
273 * tab control is the leftmost visible tab.
276 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
280 * Move the rectangle so the first item is slightly offset from
281 * the left of the tab control.
289 * Now, calculate the position of the item as if it were selected.
291 if (selectedRect!=NULL)
293 CopyRect(selectedRect, itemRect);
296 * The rectangle of a selected item is a bit wider.
298 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
301 * If it also a bit higher.
303 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
305 selectedRect->top -=2; /* the border is thicker on the bottom */
306 selectedRect->bottom +=SELECTED_TAB_OFFSET;
310 selectedRect->top -=SELECTED_TAB_OFFSET;
311 selectedRect->bottom+=1;
318 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
320 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
321 (LPRECT)lParam, (LPRECT)NULL);
324 /******************************************************************************
327 * This method is called to handle keyboard input
329 static LRESULT TAB_KeyUp(
333 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
339 newItem = infoPtr->uFocus-1;
342 newItem = infoPtr->uFocus+1;
347 * If we changed to a valid item, change the selection
349 if ( (newItem >= 0) &&
350 (newItem < infoPtr->uNumItem) &&
351 (infoPtr->uFocus != newItem) )
353 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
355 infoPtr->iSelected = newItem;
356 infoPtr->uFocus = newItem;
357 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
359 TAB_EnsureSelectionVisible(hwnd, infoPtr);
360 TAB_InvalidateTabArea(hwnd, infoPtr);
367 /******************************************************************************
370 * This method is called whenever the focus goes in or out of this control
371 * it is used to update the visual state of the control.
373 static LRESULT TAB_FocusChanging(
379 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
384 * Get the rectangle for the item.
386 isVisible = TAB_InternalGetItemRect(hwnd,
393 * If the rectangle is not completely invisible, invalidate that
394 * portion of the window.
398 InvalidateRect(hwnd, &selectedRect, TRUE);
402 * Don't otherwise disturb normal behavior.
404 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
407 static HWND TAB_InternalHitTest (
417 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
419 TAB_InternalGetItemRect(hwnd,
425 if (PtInRect (&rect, pt))
427 *flags = TCHT_ONITEM;
437 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
439 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
440 LPTCHITTESTINFO lptest=(LPTCHITTESTINFO) lParam;
442 return TAB_InternalHitTest (hwnd, infoPtr,lptest->pt,&lptest->flags);
447 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
449 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
453 if (infoPtr->hwndToolTip)
454 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
455 WM_LBUTTONDOWN, wParam, lParam);
457 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
461 if (infoPtr->hwndToolTip)
462 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
463 WM_LBUTTONDOWN, wParam, lParam);
465 pt.x = (INT)LOWORD(lParam);
466 pt.y = (INT)HIWORD(lParam);
468 newItem=TAB_InternalHitTest (hwnd, infoPtr,pt,&dummy);
470 TRACE("On Tab, item %d\n", newItem);
472 if ( (newItem!=-1) &&
473 (infoPtr->iSelected != newItem) )
475 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE)
477 infoPtr->iSelected = newItem;
478 infoPtr->uFocus = newItem;
479 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
481 TAB_EnsureSelectionVisible(hwnd, infoPtr);
483 TAB_InvalidateTabArea(hwnd, infoPtr);
490 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
492 TAB_SendSimpleNotify(hwnd, NM_CLICK);
498 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
500 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
504 /******************************************************************************
505 * TAB_DrawLoneItemInterior
507 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
508 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
509 * up the device context and font. This routine does the same setup but
510 * only calls TAB_DrawItemInterior for the single specified item.
513 TAB_DrawLoneItemInterior(HWND hwnd, TAB_INFO* infoPtr, int iItem)
515 HDC hdc = GetDC(hwnd);
516 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
517 TAB_DrawItemInterior(hwnd, hdc, iItem, NULL);
518 SelectObject(hdc, hOldFont);
519 ReleaseDC(hwnd, hdc);
522 /******************************************************************************
523 * TAB_HotTrackTimerProc
525 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
526 * timer is setup so we can check if the mouse is moved out of our window.
527 * (We don't get an event when the mouse leaves, the mouse-move events just
528 * stop being delivered to our window and just start being delivered to
529 * another window.) This function is called when the timer triggers so
530 * we can check if the mouse has left our window. If so, we un-highlight
531 * the hot-tracked tab.
534 TAB_HotTrackTimerProc
536 HWND hwnd, /* handle of window for timer messages */
537 UINT uMsg, /* WM_TIMER message */
538 UINT idEvent, /* timer identifier */
539 DWORD dwTime /* current system time */
542 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
544 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
549 ** If we can't get the cursor position, or if the cursor is outside our
550 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
551 ** "outside" even if it is within our bounding rect if another window
552 ** overlaps. Note also that the case where the cursor stayed within our
553 ** window but has moved off the hot-tracked tab will be handled by the
554 ** WM_MOUSEMOVE event.
556 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
558 /* Redraw iHotTracked to look normal */
559 INT iRedraw = infoPtr->iHotTracked;
560 infoPtr->iHotTracked = -1;
561 TAB_DrawLoneItemInterior(hwnd, infoPtr, iRedraw);
563 /* Kill this timer */
564 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
569 /******************************************************************************
572 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
573 * should be highlighted. This function determines which tab in a tab control,
574 * if any, is under the mouse and records that information. The caller may
575 * supply output parameters to receive the item number of the tab item which
576 * was highlighted but isn't any longer and of the tab item which is now
577 * highlighted but wasn't previously. The caller can use this information to
578 * selectively redraw those tab items.
580 * If the caller has a mouse position, it can supply it through the pos
581 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
582 * supplies NULL and this function determines the current mouse position
590 int* out_redrawLeave,
594 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
599 if (out_redrawLeave != NULL)
600 *out_redrawLeave = -1;
601 if (out_redrawEnter != NULL)
602 *out_redrawEnter = -1;
604 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_HOTTRACK)
612 ScreenToClient(hwnd, &pt);
620 item = TAB_InternalHitTest(hwnd, infoPtr, pt, &flags);
623 if (item != infoPtr->iHotTracked)
625 if (infoPtr->iHotTracked >= 0)
627 /* Mark currently hot-tracked to be redrawn to look normal */
628 if (out_redrawLeave != NULL)
629 *out_redrawLeave = infoPtr->iHotTracked;
633 /* Kill timer which forces recheck of mouse pos */
634 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
639 /* Start timer so we recheck mouse pos */
640 UINT timerID = SetTimer
644 TAB_HOTTRACK_TIMER_INTERVAL,
645 TAB_HotTrackTimerProc
649 return; /* Hot tracking not available */
652 infoPtr->iHotTracked = item;
656 /* Mark new hot-tracked to be redrawn to look highlighted */
657 if (out_redrawEnter != NULL)
658 *out_redrawEnter = item;
663 /******************************************************************************
666 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
669 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
674 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
676 if (infoPtr->hwndToolTip)
677 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
678 WM_LBUTTONDOWN, wParam, lParam);
680 /* Determine which tab to highlight. Redraw tabs which change highlight
682 TAB_RecalcHotTrack(hwnd, &lParam, &redrawLeave, &redrawEnter);
684 if (redrawLeave != -1)
685 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawLeave);
686 if (redrawEnter != -1)
687 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawEnter);
692 /******************************************************************************
695 * Calculates the tab control's display area given the windows rectangle or
696 * the window rectangle given the requested display rectangle.
698 static LRESULT TAB_AdjustRect(
703 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
708 * Go from display rectangle
712 * Add the height of the tabs.
714 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
715 prc->bottom += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
717 prc->top -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
720 * Inflate the rectangle for the padding
722 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
725 * Inflate for the border
727 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
732 * Go from window rectangle.
736 * Deflate the rectangle for the border
738 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
741 * Deflate the rectangle for the padding
743 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
746 * Remove the height of the tabs.
748 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
749 prc->bottom -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
751 prc->top += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
758 /******************************************************************************
761 * This method will handle the notification from the scroll control and
762 * perform the scrolling operation on the tab control.
764 static LRESULT TAB_OnHScroll(
770 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
772 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
774 if(nPos < infoPtr->leftmostVisible)
775 infoPtr->leftmostVisible--;
777 infoPtr->leftmostVisible++;
779 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
780 TAB_InvalidateTabArea(hwnd, infoPtr);
781 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
782 MAKELONG(infoPtr->leftmostVisible, 0));
788 /******************************************************************************
791 * This method will check the current scrolling state and make sure the
792 * scrolling control is displayed (or not).
794 static void TAB_SetupScrolling(
797 const RECT* clientRect)
800 if (infoPtr->needsScrolling)
806 * Calculate the position of the scroll control.
808 controlPos.right = clientRect->right;
809 controlPos.left = controlPos.right - 2*GetSystemMetrics(SM_CXHSCROLL);
811 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
813 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
814 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
818 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
819 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
823 * If we don't have a scroll control yet, we want to create one.
824 * If we have one, we want to make sure it's positioned right.
826 if (infoPtr->hwndUpDown==0)
829 * I use a scrollbar since it seems to be more stable than the Updown
832 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
834 WS_VISIBLE | WS_CHILD | UDS_HORZ,
835 controlPos.left, controlPos.top,
836 controlPos.right - controlPos.left,
837 controlPos.bottom - controlPos.top,
845 SetWindowPos(infoPtr->hwndUpDown,
847 controlPos.left, controlPos.top,
848 controlPos.right - controlPos.left,
849 controlPos.bottom - controlPos.top,
850 SWP_SHOWWINDOW | SWP_NOZORDER);
853 /* Now calculate upper limit of the updown control range.
854 * We do this by calculating how many tabs will be offscreen when the
855 * last tab is visible.
857 if(infoPtr->uNumItem)
859 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
860 maxRange = infoPtr->uNumItem;
861 tabwidth = infoPtr->items[maxRange-1].rect.right;
863 for(; maxRange > 0; maxRange--)
865 if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
869 if(maxRange == infoPtr->uNumItem)
876 * If we once had a scroll control... hide it.
878 if (infoPtr->hwndUpDown!=0)
880 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
883 if (infoPtr->hwndUpDown)
884 SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
887 /******************************************************************************
890 * This method will calculate the position rectangles of all the items in the
891 * control. The rectangle calculated starts at 0 for the first item in the
892 * list and ignores scrolling and selection.
893 * It also uses the current font to determine the height of the tab row and
894 * it checks if all the tabs fit in the client area of the window. If they
895 * dont, a scrolling control is added.
897 static void TAB_SetItemBounds (HWND hwnd)
899 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
900 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
901 TEXTMETRICA fontMetrics;
905 HFONT hFont, hOldFont;
911 * We need to get text information so we need a DC and we need to select
916 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
917 hOldFont = SelectObject (hdc, hFont);
920 * We will base the rectangle calculations on the client rectangle
923 GetClientRect(hwnd, &clientRect);
926 * The leftmost item will be "0" aligned
931 if ( !(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet) )
937 * Use the current font to determine the height of a tab.
939 GetTextMetricsA(hdc, &fontMetrics);
942 * Get the icon height
945 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
948 * Take the highest between font or icon
950 if (fontMetrics.tmHeight > icon_height)
951 item_height = fontMetrics.tmHeight;
953 item_height = icon_height;
956 * Make sure there is enough space for the letters + icon + growing the
957 * selected item + extra space for the selected item.
959 infoPtr->tabHeight = item_height + 2*VERTICAL_ITEM_PADDING +
963 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
966 * Set the leftmost position of the tab.
968 infoPtr->items[curItem].rect.left = curItemLeftPos;
970 if ( (lStyle & TCS_FIXEDWIDTH) || ((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
972 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
974 2*HORIZONTAL_ITEM_PADDING;
982 * Calculate how wide the tab is depending on the text it contains
984 GetTextExtentPoint32A(hdc, infoPtr->items[curItem].pszText,
985 lstrlenA(infoPtr->items[curItem].pszText), &size);
992 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
996 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
997 size.cx + icon_width +
998 num*HORIZONTAL_ITEM_PADDING;
1002 * Check if this is a multiline tab control and if so
1003 * check to see if we should wrap the tabs
1005 * Because we are going to arange all these tabs evenly
1006 * really we are basically just counting rows at this point
1010 if ((lStyle & TCS_MULTILINE)&&
1011 (infoPtr->items[curItem].rect.right > clientRect.right))
1013 infoPtr->items[curItem].rect.right -=
1014 infoPtr->items[curItem].rect.left;
1015 infoPtr->items[curItem].rect.left = 0;
1019 infoPtr->items[curItem].rect.bottom = 0;
1020 infoPtr->items[curItem].rect.top = curItemRowCount;
1022 TRACE("TextSize: %i\n ", size.cx);
1023 TRACE("Rect: T %i, L %i, B %i, R %i\n",
1024 infoPtr->items[curItem].rect.top,
1025 infoPtr->items[curItem].rect.left,
1026 infoPtr->items[curItem].rect.bottom,
1027 infoPtr->items[curItem].rect.right);
1030 * The leftmost position of the next item is the rightmost position
1033 if (lStyle & TCS_BUTTONS)
1034 curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX;
1036 curItemLeftPos = infoPtr->items[curItem].rect.right;
1039 if (!(lStyle & TCS_MULTILINE))
1042 * Check if we need a scrolling control.
1044 infoPtr->needsScrolling = (curItemLeftPos + (2*SELECTED_TAB_OFFSET) >
1047 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1048 if(!infoPtr->needsScrolling)
1049 infoPtr->leftmostVisible = 0;
1051 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
1055 * Set the number of rows
1058 infoPtr->uNumRows = curItemRowCount;
1060 if ((lStyle & TCS_MULTILINE)&&(infoPtr->uNumItem > 0))
1062 INT widthDiff,remainder;
1063 INT tabPerRow,remTab;
1065 INT iIndexStart=0,iIndexEnd=0, iCount=0;
1068 * Ok Microsoft trys to even out the rows. place the same
1069 * number of tabs in each row. So lets give that a shot
1073 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows + 1);
1074 remTab = infoPtr->uNumItem % (infoPtr->uNumRows + 1);
1076 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1077 iItm<infoPtr->uNumItem;
1080 if (iCount >= ((iRow<remTab)?tabPerRow+1:tabPerRow))
1087 * normalize the current rect
1089 infoPtr->items[iItm].rect.right -=
1090 infoPtr->items[iItm].rect.left;
1091 infoPtr->items[iItm].rect.left = 0;
1093 infoPtr->items[iItm].rect.top = iRow;
1094 infoPtr->items[iItm].rect.left +=curItemLeftPos;
1095 infoPtr->items[iItm].rect.right +=curItemLeftPos;
1096 if (lStyle & TCS_BUTTONS)
1097 curItemLeftPos = infoPtr->items[iItm].rect.right +
1100 curItemLeftPos = infoPtr->items[iItm].rect.right;
1108 while(iIndexStart < infoPtr->uNumItem)
1111 * find the indexs of the row
1113 for (iIndexEnd=iIndexStart;
1114 (iIndexEnd < infoPtr->uNumItem) &&
1115 (infoPtr->items[iIndexEnd].rect.top ==
1116 infoPtr->items[iIndexStart].rect.top) ;
1118 /* intentionaly blank */;
1121 * we need to justify these tabs so they fill the whole given
1125 widthDiff = clientRect.right - (2*SELECTED_TAB_OFFSET) -
1126 infoPtr->items[iIndexEnd-1].rect.right;
1128 iCount = iIndexEnd-iIndexStart;
1133 remainder = widthDiff % iCount;
1134 widthDiff = widthDiff / iCount;
1135 for (iIndex=iIndexStart,iCount=0; iIndex < iIndexEnd;
1138 infoPtr->items[iIndex].rect.left +=iCount*widthDiff;
1139 infoPtr->items[iIndex].rect.right +=(iCount+1)*widthDiff;
1141 infoPtr->items[iIndex-1].rect.right += remainder;
1144 iIndexStart=iIndexEnd;
1149 TAB_EnsureSelectionVisible(hwnd,infoPtr);
1150 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1154 SelectObject (hdc, hOldFont);
1155 ReleaseDC (hwnd, hdc);
1158 /******************************************************************************
1159 * TAB_DrawItemInterior
1161 * This method is used to draw the interior (text and icon) of a single tab
1162 * into the tab control.
1165 TAB_DrawItemInterior
1173 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1174 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1178 HPEN htextPen = GetSysColorPen (COLOR_BTNTEXT);
1182 if (drawRect == NULL)
1189 * Get the rectangle for the item.
1191 isVisible = TAB_InternalGetItemRect
1203 * Make sure drawRect points to something valid; simplifies code.
1205 drawRect = &localRect;
1208 * This logic copied from the part of TAB_DrawItem which draws
1209 * the tab background. It's important to keep it in sync. I
1210 * would have liked to avoid code duplication, but couldn't figure
1211 * out how without making spaghetti of TAB_DrawItem.
1213 if (lStyle & TCS_BUTTONS)
1215 *drawRect = itemRect;
1216 if (iItem == infoPtr->iSelected)
1224 if (iItem == infoPtr->iSelected)
1225 *drawRect = selectedRect;
1227 *drawRect = itemRect;
1236 holdPen = SelectObject(hdc, htextPen);
1238 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1244 (iItem == infoPtr->iHotTracked) ? COLOR_HIGHLIGHT : COLOR_BTNTEXT
1249 * Deflate the rectangle to acount for the padding
1251 InflateRect(drawRect, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
1254 * if owner draw, tell the owner to draw
1256 if ( (lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd) )
1262 * get the control id
1264 id = GetWindowLongA( hwnd, GWL_ID );
1267 * put together the DRAWITEMSTRUCT
1269 dis.CtlType = ODT_TAB;
1272 dis.itemAction = ODA_DRAWENTIRE;
1273 if ( iItem == infoPtr->iSelected )
1274 dis.itemState = ODS_SELECTED;
1277 dis.hwndItem = hwnd; /* */
1279 dis.rcItem = *drawRect; /* */
1280 dis.itemData = infoPtr->items[iItem].lParam;
1283 * send the draw message
1285 SendMessageA( GetParent(hwnd), WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1292 * If not owner draw, then do the drawing ourselves.
1296 if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE))
1304 infoPtr->items[iItem].iImage,
1310 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1311 drawRect->left += (cx + HORIZONTAL_ITEM_PADDING);
1317 if (lStyle & TCS_RIGHTJUSTIFY)
1318 uHorizAlign = DT_CENTER;
1320 uHorizAlign = DT_LEFT;
1325 infoPtr->items[iItem].pszText,
1326 lstrlenA(infoPtr->items[iItem].pszText),
1328 uHorizAlign | DT_SINGLELINE | DT_VCENTER
1335 SetBkMode(hdc, oldBkMode);
1336 SelectObject(hdc, holdPen);
1339 /******************************************************************************
1342 * This method is used to draw a single tab into the tab control.
1344 static void TAB_DrawItem(
1349 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1350 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1357 * Get the rectangle for the item.
1359 isVisible = TAB_InternalGetItemRect(hwnd,
1367 HBRUSH hbr = CreateSolidBrush (GetSysColor(COLOR_BTNFACE));
1368 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
1369 HPEN hbPen = GetSysColorPen (COLOR_BTNSHADOW);
1370 HPEN hfocusPen = CreatePen(PS_DOT, 1, GetSysColor(COLOR_BTNTEXT));
1373 BOOL deleteBrush = TRUE;
1375 if (lStyle & TCS_BUTTONS)
1378 * Get item rectangle.
1382 holdPen = SelectObject (hdc, hwPen);
1384 if (iItem == infoPtr->iSelected)
1389 if (!((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
1391 COLORREF bk = GetSysColor(COLOR_3DHILIGHT);
1393 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1394 SetTextColor(hdc, GetSysColor(COLOR_3DFACE));
1395 SetBkColor(hdc, bk);
1397 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1398 * we better use 0x55aa bitmap brush to make scrollbar's background
1399 * look different from the window background.
1401 if (bk == GetSysColor(COLOR_WINDOW))
1402 hbr = CACHE_GetPattern55AABrush();
1404 deleteBrush = FALSE;
1408 * Erase the background.
1410 FillRect(hdc, &r, hbr);
1414 * The rectangles calculated exclude the right and bottom
1415 * borders of the rectangle. To simply the following code, those
1416 * borders are shaved-off beforehand.
1422 MoveToEx (hdc, r.left, r.bottom, NULL);
1423 LineTo (hdc, r.right, r.bottom);
1424 LineTo (hdc, r.right, r.top);
1427 SelectObject(hdc, hbPen);
1428 LineTo (hdc, r.left, r.top);
1429 LineTo (hdc, r.left, r.bottom);
1434 * Erase the background.
1436 FillRect(hdc, &r, hbr);
1439 MoveToEx (hdc, r.left, r.bottom, NULL);
1440 LineTo (hdc, r.left, r.top);
1441 LineTo (hdc, r.right, r.top);
1444 SelectObject(hdc, hbPen);
1445 LineTo (hdc, r.right, r.bottom);
1446 LineTo (hdc, r.left, r.bottom);
1455 hbr = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1458 * We draw a rectangle of different sizes depending on the selection
1461 if (iItem == infoPtr->iSelected)
1467 * Erase the background.
1468 * This is necessary when drawing the selected item since it is larger
1469 * than the others, it might overlap with stuff already drawn by the
1472 FillRect(hdc, &r, hbr);
1476 * The rectangles calculated exclude the right and bottom
1477 * borders of the rectangle. To simply the following code, those
1478 * borders are shaved-off beforehand.
1483 holdPen = SelectObject (hdc, hwPen);
1485 if (lStyle & TCS_BOTTOM)
1488 MoveToEx (hdc, r.left, r.top, NULL);
1489 LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE);
1490 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom);
1493 SelectObject(hdc, hbPen);
1494 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom);
1495 LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE);
1496 LineTo (hdc, r.right, r.top);
1501 MoveToEx (hdc, r.left, r.bottom, NULL);
1502 LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE);
1503 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top);
1504 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top);
1507 SelectObject(hdc, hbPen);
1508 LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE);
1509 LineTo (hdc, r.right, r.bottom);
1513 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1515 /* This modifies r to be the text rectangle. */
1516 TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
1519 * Draw the focus rectangle
1521 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
1522 (GetFocus() == hwnd) &&
1523 (iItem == infoPtr->uFocus) )
1525 InflateRect(&r, FOCUS_RECT_HOFFSET, FOCUS_RECT_VOFFSET);
1527 SelectObject(hdc, hfocusPen);
1529 MoveToEx (hdc, r.left, r.top, NULL);
1530 LineTo (hdc, r.right-1, r.top);
1531 LineTo (hdc, r.right-1, r.bottom -1);
1532 LineTo (hdc, r.left, r.bottom -1);
1533 LineTo (hdc, r.left, r.top);
1539 SetBkMode(hdc, oldBkMode);
1540 SelectObject(hdc, holdPen);
1541 DeleteObject(hfocusPen);
1542 if (deleteBrush) DeleteObject(hbr);
1546 /******************************************************************************
1549 * This method is used to draw the raised border around the tab control
1552 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
1554 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1556 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
1557 HPEN hbPen = GetSysColorPen (COLOR_3DDKSHADOW);
1558 HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW);
1561 GetClientRect (hwnd, &rect);
1564 * Adjust for the style
1566 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1568 rect.bottom -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
1572 rect.top += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
1576 * Shave-off the right and bottom margins (exluded in the
1583 htmPen = SelectObject (hdc, hwPen);
1585 MoveToEx (hdc, rect.left, rect.bottom, NULL);
1586 LineTo (hdc, rect.left, rect.top);
1587 LineTo (hdc, rect.right, rect.top);
1590 SelectObject (hdc, hbPen);
1591 LineTo (hdc, rect.right, rect.bottom );
1592 LineTo (hdc, rect.left, rect.bottom);
1595 SelectObject (hdc, hShade );
1596 MoveToEx (hdc, rect.right-1, rect.top, NULL);
1597 LineTo (hdc, rect.right-1, rect.bottom-1);
1598 LineTo (hdc, rect.left, rect.bottom-1);
1600 SelectObject(hdc, htmPen);
1603 /******************************************************************************
1606 * This method repaints the tab control..
1608 static void TAB_Refresh (HWND hwnd, HDC hdc)
1610 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1614 if (!infoPtr->DoRedraw)
1617 hOldFont = SelectObject (hdc, infoPtr->hFont);
1619 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
1621 for (i = 0; i < infoPtr->uNumItem; i++)
1623 TAB_DrawItem (hwnd, hdc, i);
1629 * Draw all the non selected item first.
1631 for (i = 0; i < infoPtr->uNumItem; i++)
1633 if (i != infoPtr->iSelected)
1634 TAB_DrawItem (hwnd, hdc, i);
1638 * Now, draw the border, draw it before the selected item
1639 * since the selected item overwrites part of the border.
1641 TAB_DrawBorder (hwnd, hdc);
1644 * Then, draw the selected item
1646 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
1649 * If we haven't set the current focus yet, set it now.
1650 * Only happens when we first paint the tab controls.
1652 if (infoPtr->uFocus == -1)
1653 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
1656 SelectObject (hdc, hOldFont);
1660 TAB_GetRowCount (HWND hwnd )
1662 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1664 return infoPtr->uNumRows;
1668 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
1670 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1672 infoPtr->DoRedraw=(BOOL) wParam;
1676 static LRESULT TAB_EraseBackground(
1683 HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1685 hdc = givenDC ? givenDC : GetDC(hwnd);
1687 GetClientRect(hwnd, &clientRect);
1689 FillRect(hdc, &clientRect, brush);
1692 ReleaseDC(hwnd, hdc);
1694 DeleteObject(brush);
1699 /******************************************************************************
1700 * TAB_EnsureSelectionVisible
1702 * This method will make sure that the current selection is completely
1703 * visible by scrolling until it is.
1705 static void TAB_EnsureSelectionVisible(
1709 INT iSelected = infoPtr->iSelected;
1711 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
1714 * set the items row to the bottommost row or topmost row depending on
1718 if (infoPtr->uNumRows > 0)
1720 INT newselected=infoPtr->items[iSelected].rect.top;
1722 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1724 if (lStyle & TCS_BOTTOM)
1727 iTargetRow = infoPtr->uNumRows;
1729 if (newselected != iTargetRow)
1732 for (i=0; i < infoPtr->uNumItem; i++)
1733 if (infoPtr->items[i].rect.top == newselected )
1734 infoPtr->items[i].rect.top = iTargetRow;
1735 else if (lStyle&TCS_BOTTOM)
1737 if (infoPtr->items[i].rect.top < newselected)
1738 infoPtr->items[i].rect.top+=1;
1742 if (infoPtr->items[i].rect.top > newselected)
1743 infoPtr->items[i].rect.top-=1;
1746 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1751 * Do the trivial cases first.
1753 if ( (!infoPtr->needsScrolling) ||
1754 (infoPtr->hwndUpDown==0) )
1757 if (infoPtr->leftmostVisible >= iSelected)
1759 infoPtr->leftmostVisible = iSelected;
1766 * Calculate the part of the client area that is visible.
1768 GetClientRect(hwnd, &r);
1771 GetClientRect(infoPtr->hwndUpDown, &r);
1774 if ((infoPtr->items[iSelected].rect.right -
1775 infoPtr->items[iSelected].rect.left) >= width )
1777 /* Special case: width of selected item is greater than visible
1780 infoPtr->leftmostVisible = iSelected;
1784 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
1786 if ((infoPtr->items[iSelected].rect.right -
1787 infoPtr->items[i].rect.left) < width)
1790 infoPtr->leftmostVisible = i;
1794 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
1795 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1797 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
1798 MAKELONG(infoPtr->leftmostVisible, 0));
1801 /******************************************************************************
1802 * TAB_InvalidateTabArea
1804 * This method will invalidate the portion of the control that contains the
1805 * tabs. It is called when the state of the control changes and needs
1808 static void TAB_InvalidateTabArea(
1814 GetClientRect(hwnd, &clientRect);
1816 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1818 clientRect.top = clientRect.bottom - (infoPtr->tabHeight *
1819 (infoPtr->uNumRows + 1) + 3);
1823 clientRect.bottom = clientRect.top + (infoPtr->tabHeight *
1824 (infoPtr->uNumRows + 1) + 1);
1827 InvalidateRect(hwnd, &clientRect, TRUE);
1831 TAB_Paint (HWND hwnd, WPARAM wParam)
1836 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
1837 TAB_Refresh (hwnd, hdc);
1840 EndPaint (hwnd, &ps);
1846 TAB_InsertItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1848 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1853 GetClientRect (hwnd, &rect);
1854 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
1855 rect.top, rect.left, rect.bottom, rect.right);
1857 pti = (TCITEMA *)lParam;
1858 iItem = (INT)wParam;
1860 if (iItem < 0) return -1;
1861 if (iItem > infoPtr->uNumItem)
1862 iItem = infoPtr->uNumItem;
1864 if (infoPtr->uNumItem == 0) {
1865 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
1866 infoPtr->uNumItem++;
1867 infoPtr->iSelected = 0;
1870 TAB_ITEM *oldItems = infoPtr->items;
1872 infoPtr->uNumItem++;
1873 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
1875 /* pre insert copy */
1877 memcpy (&infoPtr->items[0], &oldItems[0],
1878 iItem * sizeof(TAB_ITEM));
1881 /* post insert copy */
1882 if (iItem < infoPtr->uNumItem - 1) {
1883 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
1884 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
1888 if (iItem <= infoPtr->iSelected)
1889 infoPtr->iSelected++;
1891 COMCTL32_Free (oldItems);
1894 infoPtr->items[iItem].mask = pti->mask;
1895 if (pti->mask & TCIF_TEXT) {
1896 len = lstrlenA (pti->pszText);
1897 infoPtr->items[iItem].pszText = COMCTL32_Alloc (len+1);
1898 strcpy (infoPtr->items[iItem].pszText, pti->pszText);
1899 infoPtr->items[iItem].cchTextMax = pti->cchTextMax;
1902 if (pti->mask & TCIF_IMAGE)
1903 infoPtr->items[iItem].iImage = pti->iImage;
1905 if (pti->mask & TCIF_PARAM)
1906 infoPtr->items[iItem].lParam = pti->lParam;
1908 TAB_SetItemBounds(hwnd);
1909 TAB_InvalidateTabArea(hwnd, infoPtr);
1911 TRACE("[%04x]: added item %d '%s'\n",
1912 hwnd, iItem, infoPtr->items[iItem].pszText);
1918 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
1920 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1921 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1924 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
1926 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
1927 infoPtr->tabWidth = (INT)LOWORD(lParam);
1928 infoPtr->tabHeight = (INT)HIWORD(lParam);
1930 infoPtr->fSizeSet = TRUE;
1936 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1938 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1944 tabItem=(LPTCITEMA ) lParam;
1945 TRACE("%d %p\n",iItem, tabItem);
1946 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
1948 wineItem=& infoPtr->items[iItem];
1950 if (tabItem->mask & TCIF_IMAGE)
1951 wineItem->iImage=tabItem->iImage;
1953 if (tabItem->mask & TCIF_PARAM)
1954 wineItem->lParam=tabItem->lParam;
1956 if (tabItem->mask & TCIF_RTLREADING)
1957 FIXME("TCIF_RTLREADING\n");
1959 if (tabItem->mask & TCIF_STATE)
1960 wineItem->dwState=tabItem->dwState;
1962 if (tabItem->mask & TCIF_TEXT) {
1963 len=lstrlenA (tabItem->pszText);
1964 if (len>wineItem->cchTextMax)
1965 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
1966 strcpy (wineItem->pszText, tabItem->pszText);
1970 * Update and repaint tabs.
1972 TAB_SetItemBounds(hwnd);
1973 TAB_InvalidateTabArea(hwnd,infoPtr);
1979 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1981 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1983 return infoPtr->uNumItem;
1988 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1990 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1996 tabItem=(LPTCITEMA) lParam;
1998 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2000 wineItem=& infoPtr->items[iItem];
2002 if (tabItem->mask & TCIF_IMAGE)
2003 tabItem->iImage=wineItem->iImage;
2005 if (tabItem->mask & TCIF_PARAM)
2006 tabItem->lParam=wineItem->lParam;
2008 if (tabItem->mask & TCIF_RTLREADING)
2009 FIXME("TCIF_RTLREADING\n");
2011 if (tabItem->mask & TCIF_STATE)
2012 tabItem->dwState=wineItem->dwState;
2014 if (tabItem->mask & TCIF_TEXT)
2015 lstrcpynA (tabItem->pszText, wineItem->pszText, tabItem->cchTextMax);
2021 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2023 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2024 INT iItem = (INT) wParam;
2025 BOOL bResult = FALSE;
2027 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2029 TAB_ITEM *oldItems = infoPtr->items;
2031 infoPtr->uNumItem--;
2032 infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
2035 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
2037 if (iItem < infoPtr->uNumItem)
2038 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
2039 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
2041 COMCTL32_Free (oldItems);
2044 * Readjust the selected index.
2046 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2047 infoPtr->iSelected--;
2049 if (iItem < infoPtr->iSelected)
2050 infoPtr->iSelected--;
2052 if (infoPtr->uNumItem == 0)
2053 infoPtr->iSelected = -1;
2056 * Reposition and repaint tabs.
2058 TAB_SetItemBounds(hwnd);
2059 TAB_InvalidateTabArea(hwnd,infoPtr);
2068 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2070 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2072 COMCTL32_Free (infoPtr->items);
2073 infoPtr->uNumItem = 0;
2074 infoPtr->iSelected = -1;
2075 if (infoPtr->iHotTracked >= 0)
2076 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2077 infoPtr->iHotTracked = -1;
2079 TAB_SetItemBounds(hwnd);
2080 TAB_InvalidateTabArea(hwnd,infoPtr);
2086 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2088 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2091 return (LRESULT)infoPtr->hFont;
2095 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2098 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2100 TRACE("%x %lx\n",wParam, lParam);
2102 infoPtr->hFont = (HFONT)wParam;
2104 TAB_SetItemBounds(hwnd);
2106 TAB_InvalidateTabArea(hwnd, infoPtr);
2113 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2115 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2118 return (LRESULT)infoPtr->himl;
2122 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2124 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2125 HIMAGELIST himlPrev;
2128 himlPrev = infoPtr->himl;
2129 infoPtr->himl= (HIMAGELIST)lParam;
2130 return (LRESULT)himlPrev;
2135 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2138 /* I'm not really sure what the following code was meant to do.
2139 This is what it is doing:
2140 When WM_SIZE is sent with SIZE_RESTORED, the control
2141 gets positioned in the top left corner.
2145 UINT uPosFlags,cx,cy;
2149 parent = GetParent (hwnd);
2150 GetClientRect(parent, &parent_rect);
2153 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2154 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2156 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2157 cx, cy, uPosFlags | SWP_NOZORDER);
2159 FIXME (tab,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2163 * Recompute the size/position of the tabs.
2165 TAB_SetItemBounds (hwnd);
2168 * Force a repaint of the control.
2170 InvalidateRect(hwnd, NULL, TRUE);
2177 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2180 TEXTMETRICA fontMetrics;
2185 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
2187 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
2189 infoPtr->uNumItem = 0;
2190 infoPtr->uNumRows = 0;
2193 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
2194 infoPtr->iSelected = -1;
2195 infoPtr->iHotTracked = -1;
2196 infoPtr->uFocus = -1;
2197 infoPtr->hwndToolTip = 0;
2198 infoPtr->DoRedraw = TRUE;
2199 infoPtr->needsScrolling = FALSE;
2200 infoPtr->hwndUpDown = 0;
2201 infoPtr->leftmostVisible = 0;
2202 infoPtr->fSizeSet = FALSE;
2204 TRACE("Created tab control, hwnd [%04x]\n", hwnd);
2206 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2207 if you don't specify in CreateWindow. This is necesary in
2208 order for paint to work correctly. This follows windows behaviour. */
2209 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
2210 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2212 if (dwStyle & TCS_TOOLTIPS) {
2213 /* Create tooltip control */
2214 infoPtr->hwndToolTip =
2215 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2216 CW_USEDEFAULT, CW_USEDEFAULT,
2217 CW_USEDEFAULT, CW_USEDEFAULT,
2220 /* Send NM_TOOLTIPSCREATED notification */
2221 if (infoPtr->hwndToolTip) {
2222 NMTOOLTIPSCREATED nmttc;
2224 nmttc.hdr.hwndFrom = hwnd;
2225 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
2226 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2227 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2229 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2230 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
2235 * We need to get text information so we need a DC and we need to select
2239 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
2242 * Use the system font to determine the initial height of a tab.
2244 GetTextMetricsA(hdc, &fontMetrics);
2247 * Make sure there is enough space for the letters + growing the
2248 * selected item + extra space for the selected item.
2250 infoPtr->tabHeight = fontMetrics.tmHeight + 2*VERTICAL_ITEM_PADDING +
2251 SELECTED_TAB_OFFSET;
2254 * Initialize the width of a tab.
2256 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
2258 SelectObject (hdc, hOldFont);
2259 ReleaseDC(hwnd, hdc);
2265 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
2267 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2273 if (infoPtr->items) {
2274 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
2275 if (infoPtr->items[iItem].pszText)
2276 COMCTL32_Free (infoPtr->items[iItem].pszText);
2278 COMCTL32_Free (infoPtr->items);
2281 if (infoPtr->hwndToolTip)
2282 DestroyWindow (infoPtr->hwndToolTip);
2284 if (infoPtr->hwndUpDown)
2285 DestroyWindow(infoPtr->hwndUpDown);
2287 if (infoPtr->iHotTracked >= 0)
2288 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2290 COMCTL32_Free (infoPtr);
2291 SetWindowLongA(hwnd, 0, 0);
2295 static LRESULT WINAPI
2296 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2299 TRACE("hwnd=%x msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
2300 if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
2301 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
2305 case TCM_GETIMAGELIST:
2306 return TAB_GetImageList (hwnd, wParam, lParam);
2308 case TCM_SETIMAGELIST:
2309 return TAB_SetImageList (hwnd, wParam, lParam);
2311 case TCM_GETITEMCOUNT:
2312 return TAB_GetItemCount (hwnd, wParam, lParam);
2315 return TAB_GetItemA (hwnd, wParam, lParam);
2318 FIXME("Unimplemented msg TCM_GETITEMW\n");
2322 return TAB_SetItemA (hwnd, wParam, lParam);
2325 FIXME("Unimplemented msg TCM_SETITEMW\n");
2328 case TCM_DELETEITEM:
2329 return TAB_DeleteItem (hwnd, wParam, lParam);
2331 case TCM_DELETEALLITEMS:
2332 return TAB_DeleteAllItems (hwnd, wParam, lParam);
2334 case TCM_GETITEMRECT:
2335 return TAB_GetItemRect (hwnd, wParam, lParam);
2338 return TAB_GetCurSel (hwnd);
2341 return TAB_HitTest (hwnd, wParam, lParam);
2344 return TAB_SetCurSel (hwnd, wParam);
2346 case TCM_INSERTITEMA:
2347 return TAB_InsertItem (hwnd, wParam, lParam);
2349 case TCM_INSERTITEMW:
2350 FIXME("Unimplemented msg TCM_INSERTITEMW\n");
2353 case TCM_SETITEMEXTRA:
2354 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
2357 case TCM_ADJUSTRECT:
2358 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
2360 case TCM_SETITEMSIZE:
2361 return TAB_SetItemSize (hwnd, wParam, lParam);
2363 case TCM_REMOVEIMAGE:
2364 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
2367 case TCM_SETPADDING:
2368 FIXME("Unimplemented msg TCM_SETPADDING\n");
2371 case TCM_GETROWCOUNT:
2372 return TAB_GetRowCount(hwnd);
2374 case TCM_GETUNICODEFORMAT:
2375 FIXME("Unimplemented msg TCM_GETUNICODEFORMAT\n");
2378 case TCM_SETUNICODEFORMAT:
2379 FIXME("Unimplemented msg TCM_SETUNICODEFORMAT\n");
2382 case TCM_HIGHLIGHTITEM:
2383 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
2386 case TCM_GETTOOLTIPS:
2387 return TAB_GetToolTips (hwnd, wParam, lParam);
2389 case TCM_SETTOOLTIPS:
2390 return TAB_SetToolTips (hwnd, wParam, lParam);
2392 case TCM_GETCURFOCUS:
2393 return TAB_GetCurFocus (hwnd);
2395 case TCM_SETCURFOCUS:
2396 return TAB_SetCurFocus (hwnd, wParam);
2398 case TCM_SETMINTABWIDTH:
2399 FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
2402 case TCM_DESELECTALL:
2403 FIXME("Unimplemented msg TCM_DESELECTALL\n");
2406 case TCM_GETEXTENDEDSTYLE:
2407 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
2410 case TCM_SETEXTENDEDSTYLE:
2411 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
2415 return TAB_GetFont (hwnd, wParam, lParam);
2418 return TAB_SetFont (hwnd, wParam, lParam);
2421 return TAB_Create (hwnd, wParam, lParam);
2424 return TAB_Destroy (hwnd, wParam, lParam);
2427 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2429 case WM_LBUTTONDOWN:
2430 return TAB_LButtonDown (hwnd, wParam, lParam);
2433 return TAB_LButtonUp (hwnd, wParam, lParam);
2435 case WM_RBUTTONDOWN:
2436 return TAB_RButtonDown (hwnd, wParam, lParam);
2439 return TAB_MouseMove (hwnd, wParam, lParam);
2442 return TAB_EraseBackground (hwnd, (HDC)wParam);
2445 return TAB_Paint (hwnd, wParam);
2448 return TAB_Size (hwnd, wParam, lParam);
2451 return TAB_SetRedraw (hwnd, wParam);
2454 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
2456 case WM_STYLECHANGED:
2457 TAB_SetItemBounds (hwnd);
2458 InvalidateRect(hwnd, NULL, TRUE);
2463 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
2466 return TAB_KeyUp(hwnd, wParam);
2469 if (uMsg >= WM_USER)
2470 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
2471 uMsg, wParam, lParam);
2472 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
2484 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
2485 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
2486 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
2487 wndClass.cbClsExtra = 0;
2488 wndClass.cbWndExtra = sizeof(TAB_INFO *);
2489 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
2490 wndClass.hbrBackground = (HBRUSH)NULL;
2491 wndClass.lpszClassName = WC_TABCONTROLA;
2493 RegisterClassA (&wndClass);
2498 TAB_Unregister (void)
2500 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);