4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Unicode support (under construction)
27 * UpDown control not displayed until after a tab is clicked on
35 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(tab);
47 RECT rect; /* bounding rectangle of the item relative to the
48 * leftmost item (the leftmost item, 0, would have a
49 * "left" member of 0 in this rectangle)
51 * additionally the top member hold the row number
52 * and bottom is unused and should be 0 */
57 UINT uNumItem; /* number of tab items */
58 UINT uNumRows; /* number of tab rows */
59 INT tabHeight; /* height of the tab row */
60 INT tabWidth; /* width of tabs */
61 HFONT hFont; /* handle to the current font */
62 HCURSOR hcurArrow; /* handle to the current cursor */
63 HIMAGELIST himl; /* handle to a image list (may be 0) */
64 HWND hwndToolTip; /* handle to tab's tooltip */
65 INT leftmostVisible; /* Used for scrolling, this member contains
66 * the index of the first visible item */
67 INT iSelected; /* the currently selected item */
68 INT iHotTracked; /* the highlighted item under the mouse */
69 INT uFocus; /* item which has the focus */
70 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
71 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
72 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
73 * the size of the control */
74 BOOL fSizeSet; /* was the size of the tabs explicitly set? */
75 BOOL bUnicode; /* Unicode control? */
76 HWND hwndUpDown; /* Updown control used for scrolling */
79 /******************************************************************************
80 * Positioning constants
82 #define SELECTED_TAB_OFFSET 2
83 #define HORIZONTAL_ITEM_PADDING 5
84 #define VERTICAL_ITEM_PADDING 3
85 #define ROUND_CORNER_SIZE 2
86 #define DISPLAY_AREA_PADDINGX 2
87 #define DISPLAY_AREA_PADDINGY 2
88 #define CONTROL_BORDER_SIZEX 2
89 #define CONTROL_BORDER_SIZEY 2
90 #define BUTTON_SPACINGX 4
91 #define BUTTON_SPACINGY 4
92 #define FLAT_BTN_SPACINGX 8
93 #define DEFAULT_TAB_WIDTH 96
95 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
97 /******************************************************************************
98 * Hot-tracking timer constants
100 #define TAB_HOTTRACK_TIMER 1
101 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
103 /******************************************************************************
106 static void TAB_Refresh (HWND hwnd, HDC hdc);
107 static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
108 static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
109 static void TAB_DrawItem(HWND hwnd, HDC hdc, INT iItem);
110 static void TAB_DrawItemInterior(HWND hwnd, HDC hdc, INT iItem, RECT* drawRect);
113 TAB_SendSimpleNotify (HWND hwnd, UINT code)
117 nmhdr.hwndFrom = hwnd;
118 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
121 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
122 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
126 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
127 WPARAM wParam, LPARAM lParam)
135 msg.time = GetMessageTime ();
136 msg.pt.x = LOWORD(GetMessagePos ());
137 msg.pt.y = HIWORD(GetMessagePos ());
139 SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
143 TAB_GetCurSel (HWND hwnd)
145 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
147 return infoPtr->iSelected;
151 TAB_GetCurFocus (HWND hwnd)
153 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
155 return infoPtr->uFocus;
159 TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
161 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
163 if (infoPtr == NULL) return 0;
164 return infoPtr->hwndToolTip;
168 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
170 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
171 INT iItem = (INT)wParam;
175 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
176 prevItem=infoPtr->iSelected;
177 infoPtr->iSelected=iItem;
178 TAB_EnsureSelectionVisible(hwnd, infoPtr);
179 TAB_InvalidateTabArea(hwnd, infoPtr);
185 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
187 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
188 INT iItem=(INT) wParam;
190 if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
192 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
193 FIXME("Should set input focus\n");
195 int oldFocus = infoPtr->uFocus;
196 if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) {
197 infoPtr->uFocus = iItem;
198 if (oldFocus != -1) {
199 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
200 infoPtr->iSelected = iItem;
201 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
204 infoPtr->iSelected = iItem;
205 TAB_EnsureSelectionVisible(hwnd, infoPtr);
206 TAB_InvalidateTabArea(hwnd, infoPtr);
214 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
216 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
218 if (infoPtr == NULL) return 0;
219 infoPtr->hwndToolTip = (HWND)wParam;
223 /******************************************************************************
224 * TAB_InternalGetItemRect
226 * This method will calculate the rectangle representing a given tab item in
227 * client coordinates. This method takes scrolling into account.
229 * This method returns TRUE if the item is visible in the window and FALSE
230 * if it is completely outside the client area.
232 static BOOL TAB_InternalGetItemRect(
239 RECT tmpItemRect,clientRect;
240 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
242 /* Perform a sanity check and a trivial visibility check. */
243 if ( (infoPtr->uNumItem <= 0) ||
244 (itemIndex >= infoPtr->uNumItem) ||
245 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
249 * Avoid special cases in this procedure by assigning the "out"
250 * parameters if the caller didn't supply them
252 if (itemRect == NULL)
253 itemRect = &tmpItemRect;
255 /* Retrieve the unmodified item rect. */
256 *itemRect = infoPtr->items[itemIndex].rect;
258 /* calculate the times bottom and top based on the row */
259 GetClientRect(hwnd, &clientRect);
261 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
263 itemRect->bottom = clientRect.bottom -
264 SELECTED_TAB_OFFSET -
265 itemRect->top * (infoPtr->tabHeight - 2) -
266 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
268 itemRect->top = clientRect.bottom -
270 itemRect->top * (infoPtr->tabHeight - 2) -
271 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
273 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
275 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * (infoPtr->tabHeight - 2) -
276 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
277 itemRect->left = clientRect.right - infoPtr->tabHeight - itemRect->left * (infoPtr->tabHeight - 2) -
278 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
280 else if((lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM))
282 itemRect->right = clientRect.left + infoPtr->tabHeight + itemRect->left * (infoPtr->tabHeight - 2) +
283 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
284 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * (infoPtr->tabHeight - 2) +
285 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
287 else if(!(lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM)) /* not TCS_BOTTOM and not TCS_VERTICAL */
289 itemRect->bottom = clientRect.top +
291 itemRect->top * (infoPtr->tabHeight - 2) +
292 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
293 itemRect->top = clientRect.top +
294 SELECTED_TAB_OFFSET +
295 itemRect->top * (infoPtr->tabHeight - 2) +
296 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
300 * "scroll" it to make sure the item at the very left of the
301 * tab control is the leftmost visible tab.
303 if(lStyle & TCS_VERTICAL)
307 -(clientRect.bottom - infoPtr->items[infoPtr->leftmostVisible].rect.bottom));
310 * Move the rectangle so the first item is slightly offset from
311 * the bottom of the tab control.
315 -SELECTED_TAB_OFFSET);
320 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
324 * Move the rectangle so the first item is slightly offset from
325 * the left of the tab control.
332 /* Now, calculate the position of the item as if it were selected. */
333 if (selectedRect!=NULL)
335 CopyRect(selectedRect, itemRect);
337 /* The rectangle of a selected item is a bit wider. */
338 if(lStyle & TCS_VERTICAL)
339 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
341 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
343 /* If it also a bit higher. */
344 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
346 selectedRect->top -= 2; /* the border is thicker on the bottom */
347 selectedRect->bottom += SELECTED_TAB_OFFSET;
349 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
351 selectedRect->left -= 2; /* the border is thicker on the right */
352 selectedRect->right += SELECTED_TAB_OFFSET;
354 else if(lStyle & TCS_VERTICAL)
356 selectedRect->left -= SELECTED_TAB_OFFSET;
357 selectedRect->right += 1;
361 selectedRect->top -= SELECTED_TAB_OFFSET;
362 selectedRect->bottom += 1;
369 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
371 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
372 (LPRECT)lParam, (LPRECT)NULL);
375 /******************************************************************************
378 * This method is called to handle keyboard input
380 static LRESULT TAB_KeyUp(
384 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
390 newItem = infoPtr->uFocus - 1;
393 newItem = infoPtr->uFocus + 1;
398 * If we changed to a valid item, change the selection
400 if ((newItem >= 0) &&
401 (newItem < infoPtr->uNumItem) &&
402 (infoPtr->uFocus != newItem))
404 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
406 infoPtr->iSelected = newItem;
407 infoPtr->uFocus = newItem;
408 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
410 TAB_EnsureSelectionVisible(hwnd, infoPtr);
411 TAB_InvalidateTabArea(hwnd, infoPtr);
418 /******************************************************************************
421 * This method is called whenever the focus goes in or out of this control
422 * it is used to update the visual state of the control.
424 static LRESULT TAB_FocusChanging(
430 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
435 * Get the rectangle for the item.
437 isVisible = TAB_InternalGetItemRect(hwnd,
444 * If the rectangle is not completely invisible, invalidate that
445 * portion of the window.
449 InvalidateRect(hwnd, &selectedRect, TRUE);
453 * Don't otherwise disturb normal behavior.
455 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
458 static HWND TAB_InternalHitTest (
468 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
470 TAB_InternalGetItemRect(hwnd, infoPtr, iCount, &rect, NULL);
472 if (PtInRect(&rect, pt))
474 *flags = TCHT_ONITEM;
479 *flags = TCHT_NOWHERE;
484 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
486 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
487 LPTCHITTESTINFO lptest = (LPTCHITTESTINFO) lParam;
489 return TAB_InternalHitTest (hwnd, infoPtr, lptest->pt, &lptest->flags);
492 /******************************************************************************
495 * Napster v2b5 has a tab control for its main navigation which has a client
496 * area that covers the whole area of the dialog pages.
497 * That's why it receives all msgs for that area and the underlying dialog ctrls
499 * So I decided that we should handle WM_NCHITTEST here and return
500 * HTTRANSPARENT if we don't hit the tab control buttons.
501 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
502 * doesn't do it that way. Maybe depends on tab control styles ?
505 TAB_NCHitTest (HWND hwnd, LPARAM lParam)
507 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
511 pt.x = LOWORD(lParam);
512 pt.y = HIWORD(lParam);
513 ScreenToClient(hwnd, &pt);
515 if (TAB_InternalHitTest(hwnd, infoPtr, pt, &dummyflag) == -1)
516 return HTTRANSPARENT;
522 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
524 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
528 if (infoPtr->hwndToolTip)
529 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
530 WM_LBUTTONDOWN, wParam, lParam);
532 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
536 if (infoPtr->hwndToolTip)
537 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
538 WM_LBUTTONDOWN, wParam, lParam);
540 pt.x = (INT)LOWORD(lParam);
541 pt.y = (INT)HIWORD(lParam);
543 newItem = TAB_InternalHitTest (hwnd, infoPtr, pt, &dummy);
545 TRACE("On Tab, item %d\n", newItem);
547 if ((newItem != -1) && (infoPtr->iSelected != newItem))
549 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING) != TRUE)
551 infoPtr->iSelected = newItem;
552 infoPtr->uFocus = newItem;
553 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
555 TAB_EnsureSelectionVisible(hwnd, infoPtr);
557 TAB_InvalidateTabArea(hwnd, infoPtr);
564 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
566 TAB_SendSimpleNotify(hwnd, NM_CLICK);
572 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
574 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
578 /******************************************************************************
579 * TAB_DrawLoneItemInterior
581 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
582 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
583 * up the device context and font. This routine does the same setup but
584 * only calls TAB_DrawItemInterior for the single specified item.
587 TAB_DrawLoneItemInterior(HWND hwnd, TAB_INFO* infoPtr, int iItem)
589 HDC hdc = GetDC(hwnd);
590 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
591 TAB_DrawItemInterior(hwnd, hdc, iItem, NULL);
592 SelectObject(hdc, hOldFont);
593 ReleaseDC(hwnd, hdc);
596 /******************************************************************************
597 * TAB_HotTrackTimerProc
599 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
600 * timer is setup so we can check if the mouse is moved out of our window.
601 * (We don't get an event when the mouse leaves, the mouse-move events just
602 * stop being delivered to our window and just start being delivered to
603 * another window.) This function is called when the timer triggers so
604 * we can check if the mouse has left our window. If so, we un-highlight
605 * the hot-tracked tab.
608 TAB_HotTrackTimerProc
610 HWND hwnd, /* handle of window for timer messages */
611 UINT uMsg, /* WM_TIMER message */
612 UINT idEvent, /* timer identifier */
613 DWORD dwTime /* current system time */
616 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
618 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
623 ** If we can't get the cursor position, or if the cursor is outside our
624 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
625 ** "outside" even if it is within our bounding rect if another window
626 ** overlaps. Note also that the case where the cursor stayed within our
627 ** window but has moved off the hot-tracked tab will be handled by the
628 ** WM_MOUSEMOVE event.
630 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
632 /* Redraw iHotTracked to look normal */
633 INT iRedraw = infoPtr->iHotTracked;
634 infoPtr->iHotTracked = -1;
635 TAB_DrawLoneItemInterior(hwnd, infoPtr, iRedraw);
637 /* Kill this timer */
638 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
643 /******************************************************************************
646 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
647 * should be highlighted. This function determines which tab in a tab control,
648 * if any, is under the mouse and records that information. The caller may
649 * supply output parameters to receive the item number of the tab item which
650 * was highlighted but isn't any longer and of the tab item which is now
651 * highlighted but wasn't previously. The caller can use this information to
652 * selectively redraw those tab items.
654 * If the caller has a mouse position, it can supply it through the pos
655 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
656 * supplies NULL and this function determines the current mouse position
664 int* out_redrawLeave,
668 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
673 if (out_redrawLeave != NULL)
674 *out_redrawLeave = -1;
675 if (out_redrawEnter != NULL)
676 *out_redrawEnter = -1;
678 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_HOTTRACK)
686 ScreenToClient(hwnd, &pt);
694 item = TAB_InternalHitTest(hwnd, infoPtr, pt, &flags);
697 if (item != infoPtr->iHotTracked)
699 if (infoPtr->iHotTracked >= 0)
701 /* Mark currently hot-tracked to be redrawn to look normal */
702 if (out_redrawLeave != NULL)
703 *out_redrawLeave = infoPtr->iHotTracked;
707 /* Kill timer which forces recheck of mouse pos */
708 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
713 /* Start timer so we recheck mouse pos */
714 UINT timerID = SetTimer
718 TAB_HOTTRACK_TIMER_INTERVAL,
719 TAB_HotTrackTimerProc
723 return; /* Hot tracking not available */
726 infoPtr->iHotTracked = item;
730 /* Mark new hot-tracked to be redrawn to look highlighted */
731 if (out_redrawEnter != NULL)
732 *out_redrawEnter = item;
737 /******************************************************************************
740 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
743 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
748 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
750 if (infoPtr->hwndToolTip)
751 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
752 WM_LBUTTONDOWN, wParam, lParam);
754 /* Determine which tab to highlight. Redraw tabs which change highlight
756 TAB_RecalcHotTrack(hwnd, &lParam, &redrawLeave, &redrawEnter);
758 if (redrawLeave != -1)
759 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawLeave);
760 if (redrawEnter != -1)
761 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawEnter);
766 /******************************************************************************
769 * Calculates the tab control's display area given the window rectangle or
770 * the window rectangle given the requested display rectangle.
772 static LRESULT TAB_AdjustRect(
777 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
778 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
780 if(lStyle & TCS_VERTICAL)
782 if (fLarger) /* Go from display rectangle */
784 /* Add the height of the tabs. */
785 if (lStyle & TCS_BOTTOM)
786 prc->right += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
788 prc->left -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
790 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
791 /* Inflate the rectangle for the padding */
792 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
794 /* Inflate for the border */
795 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
797 else /* Go from window rectangle. */
799 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
800 /* Deflate the rectangle for the border */
801 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
803 /* Deflate the rectangle for the padding */
804 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
806 /* Remove the height of the tabs. */
807 if (lStyle & TCS_BOTTOM)
808 prc->right -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
810 prc->left += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
814 if (fLarger) /* Go from display rectangle */
816 /* Add the height of the tabs. */
817 if (lStyle & TCS_BOTTOM)
818 prc->bottom += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
820 prc->top -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
822 /* Inflate the rectangle for the padding */
823 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
825 /* Inflate for the border */
826 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
828 else /* Go from window rectangle. */
830 /* Deflate the rectangle for the border */
831 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
833 /* Deflate the rectangle for the padding */
834 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
836 /* Remove the height of the tabs. */
837 if (lStyle & TCS_BOTTOM)
838 prc->bottom -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
840 prc->top += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
847 /******************************************************************************
850 * This method will handle the notification from the scroll control and
851 * perform the scrolling operation on the tab control.
853 static LRESULT TAB_OnHScroll(
859 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
861 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
863 if(nPos < infoPtr->leftmostVisible)
864 infoPtr->leftmostVisible--;
866 infoPtr->leftmostVisible++;
868 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
869 TAB_InvalidateTabArea(hwnd, infoPtr);
870 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
871 MAKELONG(infoPtr->leftmostVisible, 0));
877 /******************************************************************************
880 * This method will check the current scrolling state and make sure the
881 * scrolling control is displayed (or not).
883 static void TAB_SetupScrolling(
886 const RECT* clientRect)
889 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
891 if (infoPtr->needsScrolling)
897 * Calculate the position of the scroll control.
899 if(lStyle & TCS_VERTICAL)
901 controlPos.right = clientRect->right;
902 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
904 if (lStyle & TCS_BOTTOM)
906 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
907 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
911 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
912 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
917 controlPos.right = clientRect->right;
918 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
920 if (lStyle & TCS_BOTTOM)
922 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
923 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
927 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
928 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
933 * If we don't have a scroll control yet, we want to create one.
934 * If we have one, we want to make sure it's positioned properly.
936 if (infoPtr->hwndUpDown==0)
938 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
940 WS_VISIBLE | WS_CHILD | UDS_HORZ,
941 controlPos.left, controlPos.top,
942 controlPos.right - controlPos.left,
943 controlPos.bottom - controlPos.top,
951 SetWindowPos(infoPtr->hwndUpDown,
953 controlPos.left, controlPos.top,
954 controlPos.right - controlPos.left,
955 controlPos.bottom - controlPos.top,
956 SWP_SHOWWINDOW | SWP_NOZORDER);
959 /* Now calculate upper limit of the updown control range.
960 * We do this by calculating how many tabs will be offscreen when the
961 * last tab is visible.
963 if(infoPtr->uNumItem)
965 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
966 maxRange = infoPtr->uNumItem;
967 tabwidth = infoPtr->items[maxRange - 1].rect.right;
969 for(; maxRange > 0; maxRange--)
971 if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
975 if(maxRange == infoPtr->uNumItem)
981 /* If we once had a scroll control... hide it */
982 if (infoPtr->hwndUpDown!=0)
983 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
985 if (infoPtr->hwndUpDown)
986 SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
989 /******************************************************************************
992 * This method will calculate the position rectangles of all the items in the
993 * control. The rectangle calculated starts at 0 for the first item in the
994 * list and ignores scrolling and selection.
995 * It also uses the current font to determine the height of the tab row and
996 * it checks if all the tabs fit in the client area of the window. If they
997 * dont, a scrolling control is added.
999 static void TAB_SetItemBounds (HWND hwnd)
1001 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1002 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1003 TEXTMETRICA fontMetrics;
1006 INT curItemRowCount;
1007 HFONT hFont, hOldFont;
1016 * We need to get text information so we need a DC and we need to select
1021 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1022 hOldFont = SelectObject (hdc, hFont);
1025 * We will base the rectangle calculations on the client rectangle
1028 GetClientRect(hwnd, &clientRect);
1030 /* if TCS_VERTICAL then swap the height and width so this code places the
1031 tabs along the top of the rectangle and we can just rotate them after
1032 rather than duplicate all of the below code */
1033 if(lStyle & TCS_VERTICAL)
1035 iTemp = clientRect.bottom;
1036 clientRect.bottom = clientRect.right;
1037 clientRect.right = iTemp;
1040 /* The leftmost item will be "0" aligned */
1042 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1044 if (!(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet) )
1047 int icon_height = 0;
1049 /* Use the current font to determine the height of a tab. */
1050 GetTextMetricsA(hdc, &fontMetrics);
1052 /* Get the icon height */
1054 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1056 /* Take the highest between font or icon */
1057 if (fontMetrics.tmHeight > icon_height)
1058 item_height = fontMetrics.tmHeight;
1060 item_height = icon_height;
1063 * Make sure there is enough space for the letters + icon + growing the
1064 * selected item + extra space for the selected item.
1066 infoPtr->tabHeight = item_height + 2 * VERTICAL_ITEM_PADDING +
1067 SELECTED_TAB_OFFSET;
1071 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1073 /* Set the leftmost position of the tab. */
1074 infoPtr->items[curItem].rect.left = curItemLeftPos;
1076 if ( (lStyle & TCS_FIXEDWIDTH) || ((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
1078 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1080 2 * HORIZONTAL_ITEM_PADDING;
1087 /* Calculate how wide the tab is depending on the text it contains */
1088 GetTextExtentPoint32W(hdc, infoPtr->items[curItem].pszText,
1089 lstrlenW(infoPtr->items[curItem].pszText), &size);
1091 /* under Windows, there seems to be a minimum width of 2x the height
1092 * for button style tabs */
1093 if (lStyle & TCS_BUTTONS)
1094 size.cx = max(size.cx, 2 * (infoPtr->tabHeight - 2));
1096 /* Add the icon width */
1099 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1103 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1104 size.cx + icon_width +
1105 num * HORIZONTAL_ITEM_PADDING;
1109 * Check if this is a multiline tab control and if so
1110 * check to see if we should wrap the tabs
1112 * Because we are going to arange all these tabs evenly
1113 * really we are basically just counting rows at this point
1117 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1118 (infoPtr->items[curItem].rect.right > clientRect.right))
1120 infoPtr->items[curItem].rect.right -=
1121 infoPtr->items[curItem].rect.left;
1123 infoPtr->items[curItem].rect.left = 0;
1127 infoPtr->items[curItem].rect.bottom = 0;
1128 infoPtr->items[curItem].rect.top = curItemRowCount - 1;
1130 TRACE("TextSize: %li\n", size.cx);
1131 TRACE("Rect: T %i, L %i, B %i, R %i\n",
1132 infoPtr->items[curItem].rect.top,
1133 infoPtr->items[curItem].rect.left,
1134 infoPtr->items[curItem].rect.bottom,
1135 infoPtr->items[curItem].rect.right);
1138 * The leftmost position of the next item is the rightmost position
1141 if (lStyle & TCS_BUTTONS)
1143 curItemLeftPos = infoPtr->items[curItem].rect.right + 1;
1144 if (lStyle & TCS_FLATBUTTONS)
1145 curItemLeftPos += FLAT_BTN_SPACINGX;
1148 curItemLeftPos = infoPtr->items[curItem].rect.right;
1151 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1154 * Check if we need a scrolling control.
1156 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1159 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1160 if(!infoPtr->needsScrolling)
1161 infoPtr->leftmostVisible = 0;
1163 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
1166 /* Set the number of rows */
1167 infoPtr->uNumRows = curItemRowCount;
1169 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->uNumItem > 0))
1171 INT widthDiff, remainder;
1172 INT tabPerRow,remTab;
1174 INT iIndexStart=0,iIndexEnd=0, iCount=0;
1177 * Ok windows tries to even out the rows. place the same
1178 * number of tabs in each row. So lets give that a shot
1181 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1182 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1184 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1185 iItm<infoPtr->uNumItem;
1188 /* if we have reached the maximum number of tabs on this row */
1189 /* move to the next row, reset our current item left position and */
1190 /* the count of items on this row */
1191 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow))
1198 /* normalize the current rect */
1200 /* shift the item to the left side of the clientRect */
1201 infoPtr->items[iItm].rect.right -=
1202 infoPtr->items[iItm].rect.left;
1203 infoPtr->items[iItm].rect.left = 0;
1205 /* shift the item to the right to place it as the next item in this row */
1206 infoPtr->items[iItm].rect.left += curItemLeftPos;
1207 infoPtr->items[iItm].rect.right += curItemLeftPos;
1208 infoPtr->items[iItm].rect.top = iRow;
1209 if (lStyle & TCS_BUTTONS)
1211 curItemLeftPos = infoPtr->items[iItm].rect.right + 1;
1212 if (lStyle & TCS_FLATBUTTONS)
1213 curItemLeftPos += FLAT_BTN_SPACINGX;
1216 curItemLeftPos = infoPtr->items[iItm].rect.right;
1223 while(iIndexStart < infoPtr->uNumItem)
1226 * find the indexs of the row
1228 /* find the first item on the next row */
1229 for (iIndexEnd=iIndexStart;
1230 (iIndexEnd < infoPtr->uNumItem) &&
1231 (infoPtr->items[iIndexEnd].rect.top ==
1232 infoPtr->items[iIndexStart].rect.top) ;
1234 /* intentionally blank */;
1237 * we need to justify these tabs so they fill the whole given
1241 /* find the amount of space remaining on this row */
1242 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1243 infoPtr->items[iIndexEnd - 1].rect.right;
1245 /* iCount is the number of tab items on this row */
1246 iCount = iIndexEnd - iIndexStart;
1251 remainder = widthDiff % iCount;
1252 widthDiff = widthDiff / iCount;
1253 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1254 for (iIndex=iIndexStart,iCount=0; iIndex < iIndexEnd;
1257 infoPtr->items[iIndex].rect.left += iCount * widthDiff;
1258 infoPtr->items[iIndex].rect.right += (iCount + 1) * widthDiff;
1260 infoPtr->items[iIndex - 1].rect.right += remainder;
1262 else /* we have only one item on this row, make it take up the entire row */
1264 infoPtr->items[iIndexStart].rect.left = clientRect.left;
1265 infoPtr->items[iIndexStart].rect.right = clientRect.right - 4;
1269 iIndexStart = iIndexEnd;
1274 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1275 if(lStyle & TCS_VERTICAL)
1278 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1280 rcItem = &(infoPtr->items[iIndex].rect);
1282 rcOriginal = *rcItem;
1284 /* this is rotating the items by 90 degrees around the center of the control */
1285 rcItem->top = (clientRect.right - (rcOriginal.left - clientRect.left)) - (rcOriginal.right - rcOriginal.left);
1286 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1287 rcItem->left = rcOriginal.top;
1288 rcItem->right = rcOriginal.bottom;
1292 TAB_EnsureSelectionVisible(hwnd,infoPtr);
1293 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1296 SelectObject (hdc, hOldFont);
1297 ReleaseDC (hwnd, hdc);
1300 /******************************************************************************
1301 * TAB_DrawItemInterior
1303 * This method is used to draw the interior (text and icon) of a single tab
1304 * into the tab control.
1307 TAB_DrawItemInterior
1315 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1316 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1324 if (drawRect == NULL)
1331 * Get the rectangle for the item.
1333 isVisible = TAB_InternalGetItemRect(hwnd, infoPtr, iItem, &itemRect, &selectedRect);
1338 * Make sure drawRect points to something valid; simplifies code.
1340 drawRect = &localRect;
1343 * This logic copied from the part of TAB_DrawItem which draws
1344 * the tab background. It's important to keep it in sync. I
1345 * would have liked to avoid code duplication, but couldn't figure
1346 * out how without making spaghetti of TAB_DrawItem.
1348 if (lStyle & TCS_BUTTONS)
1350 *drawRect = itemRect;
1351 if (iItem == infoPtr->iSelected)
1359 if (iItem == infoPtr->iSelected)
1360 *drawRect = selectedRect;
1362 *drawRect = itemRect;
1371 htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1372 holdPen = SelectObject(hdc, htextPen);
1374 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1375 SetTextColor(hdc, GetSysColor((iItem == infoPtr->iHotTracked) ? COLOR_HIGHLIGHT : COLOR_BTNTEXT));
1378 * Deflate the rectangle to acount for the padding
1380 if(lStyle & TCS_VERTICAL)
1381 InflateRect(drawRect, -VERTICAL_ITEM_PADDING, -HORIZONTAL_ITEM_PADDING);
1383 InflateRect(drawRect, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
1387 * if owner draw, tell the owner to draw
1389 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd))
1395 * get the control id
1397 id = GetWindowLongA( hwnd, GWL_ID );
1400 * put together the DRAWITEMSTRUCT
1402 dis.CtlType = ODT_TAB;
1405 dis.itemAction = ODA_DRAWENTIRE;
1406 if ( iItem == infoPtr->iSelected )
1407 dis.itemState = ODS_SELECTED;
1410 dis.hwndItem = hwnd; /* */
1412 dis.rcItem = *drawRect; /* */
1413 dis.itemData = infoPtr->items[iItem].lParam;
1416 * send the draw message
1418 SendMessageA( GetParent(hwnd), WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1429 HFONT hOldFont = 0; /* stop uninitialized warning */
1431 INT nEscapement = 0; /* stop uninitialized warning */
1432 INT nOrientation = 0; /* stop uninitialized warning */
1435 /* used to center the icon and text in the tab */
1439 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1440 rcImage = *drawRect;
1445 * Setup for text output
1447 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1448 SetTextColor(hdc, GetSysColor((iItem == infoPtr->iHotTracked) ? COLOR_HIGHLIGHT : COLOR_BTNTEXT));
1450 /* get the rectangle that the text fits in */
1451 DrawTextW(hdc, infoPtr->items[iItem].pszText, -1,
1452 &rcText, DT_CALCRECT);
1455 * If not owner draw, then do the drawing ourselves.
1459 if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE))
1461 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1463 if(lStyle & TCS_VERTICAL)
1464 center_offset = ((drawRect->bottom - drawRect->top) - (cy + VERTICAL_ITEM_PADDING + (rcText.right - rcText.left))) / 2;
1466 center_offset = ((drawRect->right - drawRect->left) - (cx + HORIZONTAL_ITEM_PADDING + (rcText.right - rcText.left))) / 2;
1468 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1470 /* rcImage.left = drawRect->left; */ /* explicit from above rcImage = *drawRect */
1471 rcImage.top = drawRect->top + center_offset;
1472 rcImage.left = drawRect->right - cx; /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1473 /* right side of the tab, but the image still uses the left as its x position */
1474 /* this keeps the image always drawn off of the same side of the tab */
1475 drawRect->top = rcImage.top + (cx + VERTICAL_ITEM_PADDING);
1477 else if(lStyle & TCS_VERTICAL)
1479 /* rcImage.left = drawRect->left; */ /* explicit from above rcImage = *drawRect */
1480 rcImage.top = drawRect->bottom - cy - center_offset;
1482 drawRect->bottom = rcImage.top - VERTICAL_ITEM_PADDING;
1484 else /* normal style, whether TCS_BOTTOM or not */
1486 rcImage.left = drawRect->left + center_offset;
1487 /* rcImage.top = drawRect->top; */ /* explicit from above rcImage = *drawRect */
1489 drawRect->left = rcImage.left + cx + HORIZONTAL_ITEM_PADDING;
1495 infoPtr->items[iItem].iImage,
1501 } else /* no image, so just shift the drawRect borders around */
1503 if(lStyle & TCS_VERTICAL)
1507 currently the rcText rect is flawed because the rotated font does not
1508 often match the horizontal font. So leave this as 0
1509 ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1511 if(lStyle & TCS_BOTTOM)
1512 drawRect->top+=center_offset;
1514 drawRect->bottom-=center_offset;
1518 center_offset = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1519 drawRect->left+=center_offset;
1524 if (lStyle & TCS_RIGHTJUSTIFY)
1525 uHorizAlign = DT_CENTER;
1527 uHorizAlign = DT_LEFT;
1529 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1531 if(lStyle & TCS_BOTTOM)
1534 nOrientation = -900;
1543 /* to get a font with the escapement and orientation we are looking for, we need to */
1544 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1545 if(lStyle & TCS_VERTICAL)
1547 if (!GetObjectA((infoPtr->hFont) ?
1548 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1549 sizeof(LOGFONTA),&logfont))
1553 lstrcpyA(logfont.lfFaceName, "Arial");
1554 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1556 logfont.lfWeight = FW_NORMAL;
1557 logfont.lfItalic = 0;
1558 logfont.lfUnderline = 0;
1559 logfont.lfStrikeOut = 0;
1562 logfont.lfEscapement = nEscapement;
1563 logfont.lfOrientation = nOrientation;
1564 hFont = CreateFontIndirectA(&logfont);
1565 hOldFont = SelectObject(hdc, hFont);
1568 if (lStyle & TCS_VERTICAL)
1571 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1572 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1575 infoPtr->items[iItem].pszText,
1576 lstrlenW(infoPtr->items[iItem].pszText),
1584 infoPtr->items[iItem].pszText,
1585 lstrlenW(infoPtr->items[iItem].pszText),
1587 uHorizAlign | DT_SINGLELINE
1591 /* clean things up */
1592 *drawRect = rcTemp; /* restore drawRect */
1594 if(lStyle & TCS_VERTICAL)
1596 SelectObject(hdc, hOldFont); /* restore the original font */
1598 DeleteObject(hFont);
1605 SetBkMode(hdc, oldBkMode);
1606 SelectObject(hdc, holdPen);
1607 DeleteObject( htextPen );
1610 /******************************************************************************
1613 * This method is used to draw a single tab into the tab control.
1615 static void TAB_DrawItem(
1620 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1621 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1628 * Get the rectangle for the item.
1630 isVisible = TAB_InternalGetItemRect(hwnd,
1638 HBRUSH hbr = CreateSolidBrush (GetSysColor(COLOR_BTNFACE));
1639 HPEN hwPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT) );
1640 HPEN hbPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_3DDKSHADOW) );
1641 HPEN hShade = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW) );
1644 BOOL deleteBrush = TRUE;
1646 if (lStyle & TCS_BUTTONS)
1648 /* Get item rectangle */
1651 holdPen = SelectObject (hdc, hwPen);
1653 /* Separators between flat buttons */
1654 /* FIXME: test and correct this if necessary for TCS_FLATBUTTONS style */
1655 if (lStyle & TCS_FLATBUTTONS)
1657 int x = r.right + FLAT_BTN_SPACINGX - 2;
1660 MoveToEx (hdc, x, r.bottom - 1, NULL);
1661 LineTo (hdc, x, r.top - 1);
1665 SelectObject(hdc, hbPen);
1666 MoveToEx (hdc, x, r.bottom - 1, NULL);
1667 LineTo (hdc, x, r.top - 1);
1670 SelectObject (hdc, hShade );
1671 MoveToEx (hdc, x - 1, r.bottom - 1, NULL);
1672 LineTo (hdc, x - 1, r.top - 1);
1675 if (iItem == infoPtr->iSelected)
1677 /* Background color */
1678 if (!((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
1680 COLORREF bk = GetSysColor(COLOR_3DHILIGHT);
1682 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1684 SetTextColor(hdc, GetSysColor(COLOR_3DFACE));
1685 SetBkColor(hdc, bk);
1687 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1688 * we better use 0x55aa bitmap brush to make scrollbar's background
1689 * look different from the window background.
1691 if (bk == GetSysColor(COLOR_WINDOW))
1692 hbr = COMCTL32_hPattern55AABrush;
1694 deleteBrush = FALSE;
1697 /* Erase the background */
1698 FillRect(hdc, &r, hbr);
1702 * The rectangles calculated exclude the right and bottom
1703 * borders of the rectangle. To simplify the following code, those
1704 * borders are shaved-off beforehand.
1710 SelectObject(hdc, hwPen);
1711 MoveToEx (hdc, r.left, r.bottom, NULL);
1712 LineTo (hdc, r.right, r.bottom);
1713 LineTo (hdc, r.right, r.top + 1);
1716 SelectObject(hdc, hbPen);
1717 LineTo (hdc, r.left + 1, r.top + 1);
1718 LineTo (hdc, r.left + 1, r.bottom);
1721 SelectObject (hdc, hShade );
1722 MoveToEx (hdc, r.right, r.top, NULL);
1723 LineTo (hdc, r.left, r.top);
1724 LineTo (hdc, r.left, r.bottom);
1728 /* Erase the background */
1729 FillRect(hdc, &r, hbr);
1731 if (!(lStyle & TCS_FLATBUTTONS))
1734 MoveToEx (hdc, r.left, r.bottom, NULL);
1735 LineTo (hdc, r.left, r.top);
1736 LineTo (hdc, r.right, r.top);
1739 SelectObject(hdc, hbPen);
1740 LineTo (hdc, r.right, r.bottom);
1741 LineTo (hdc, r.left, r.bottom);
1744 SelectObject (hdc, hShade );
1745 MoveToEx (hdc, r.right - 1, r.top, NULL);
1746 LineTo (hdc, r.right - 1, r.bottom - 1);
1747 LineTo (hdc, r.left + 1, r.bottom - 1);
1751 else /* !TCS_BUTTONS */
1753 /* Background color */
1755 hbr = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1757 /* We draw a rectangle of different sizes depending on the selection
1759 if (iItem == infoPtr->iSelected)
1765 * Erase the background.
1766 * This is necessary when drawing the selected item since it is larger
1767 * than the others, it might overlap with stuff already drawn by the
1770 FillRect(hdc, &r, hbr);
1774 * The rectangles calculated exclude the right and bottom
1775 * borders of the rectangle. To simplify the following code, those
1776 * borders are shaved-off beforehand.
1781 holdPen = SelectObject (hdc, hwPen);
1782 if(lStyle & TCS_VERTICAL)
1784 if (lStyle & TCS_BOTTOM)
1787 MoveToEx (hdc, r.left, r.top, NULL);
1788 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top);
1789 LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE);
1792 SelectObject(hdc, hbPen);
1793 LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE);
1794 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom);
1795 LineTo (hdc, r.left - 1, r.bottom);
1798 SelectObject (hdc, hShade );
1799 MoveToEx (hdc, r.right - 1, r.top, NULL);
1800 LineTo (hdc, r.right - 1, r.bottom - 1);
1801 LineTo (hdc, r.left - 1, r.bottom - 1);
1806 MoveToEx (hdc, r.right, r.top, NULL);
1807 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top);
1808 LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE);
1809 LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE);
1812 SelectObject(hdc, hbPen);
1813 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom);
1814 LineTo (hdc, r.right + 1, r.bottom);
1817 SelectObject (hdc, hShade );
1818 MoveToEx (hdc, r.left + ROUND_CORNER_SIZE - 1, r.bottom - 1, NULL);
1819 LineTo (hdc, r.right + 1, r.bottom - 1);
1824 if (lStyle & TCS_BOTTOM)
1827 MoveToEx (hdc, r.left, r.top, NULL);
1828 LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE);
1829 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom);
1832 SelectObject(hdc, hbPen);
1833 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom);
1834 LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE);
1835 LineTo (hdc, r.right, r.top - 1);
1838 SelectObject (hdc, hShade );
1839 MoveToEx (hdc, r.left, r.bottom - 1, NULL);
1840 LineTo (hdc, r.right - ROUND_CORNER_SIZE - 1, r.bottom - 1);
1841 LineTo (hdc, r.right - 1, r.bottom - ROUND_CORNER_SIZE - 1);
1842 LineTo (hdc, r.right - 1, r.top - 1);
1847 if(infoPtr->items[iItem].rect.left == 0) /* if leftmost draw the line longer */
1848 MoveToEx (hdc, r.left, r.bottom, NULL);
1850 MoveToEx (hdc, r.left, r.bottom - 1, NULL);
1852 LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE);
1853 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top);
1854 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top);
1857 SelectObject(hdc, hbPen);
1858 LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE);
1859 LineTo (hdc, r.right, r.bottom + 1);
1863 SelectObject (hdc, hShade );
1864 MoveToEx (hdc, r.right - 1, r.top + ROUND_CORNER_SIZE, NULL);
1865 LineTo (hdc, r.right - 1, r.bottom + 1);
1870 /* This modifies r to be the text rectangle. */
1872 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1873 TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
1874 SelectObject(hdc,hOldFont);
1877 /* Draw the focus rectangle */
1878 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
1879 (GetFocus() == hwnd) &&
1880 (iItem == infoPtr->uFocus) )
1883 InflateRect(&r, -1, -1);
1885 DrawFocusRect(hdc, &r);
1889 SelectObject(hdc, holdPen);
1890 DeleteObject( hwPen );
1891 DeleteObject( hbPen );
1892 DeleteObject( hShade );
1893 if (deleteBrush) DeleteObject(hbr);
1897 /******************************************************************************
1900 * This method is used to draw the raised border around the tab control
1903 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
1905 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1907 HPEN hwPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT) );
1908 HPEN hbPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_3DDKSHADOW) );
1909 HPEN hShade = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW) );
1911 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1913 GetClientRect (hwnd, &rect);
1916 * Adjust for the style
1919 if (infoPtr->uNumItem)
1921 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
1923 rect.bottom -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
1925 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
1927 rect.right -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
1929 else if(lStyle & TCS_VERTICAL)
1931 rect.left += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
1933 else /* not TCS_VERTICAL and not TCS_BOTTOM */
1935 rect.top += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 1;
1940 * Shave-off the right and bottom margins (exluded in the
1947 htmPen = SelectObject (hdc, hwPen);
1949 MoveToEx (hdc, rect.left, rect.bottom, NULL);
1950 LineTo (hdc, rect.left, rect.top);
1951 LineTo (hdc, rect.right, rect.top);
1954 SelectObject (hdc, hbPen);
1955 LineTo (hdc, rect.right, rect.bottom );
1956 LineTo (hdc, rect.left, rect.bottom);
1959 SelectObject (hdc, hShade );
1960 MoveToEx (hdc, rect.right - 1, rect.top, NULL);
1961 LineTo (hdc, rect.right - 1, rect.bottom - 1);
1962 LineTo (hdc, rect.left, rect.bottom - 1);
1964 SelectObject(hdc, htmPen);
1965 DeleteObject( hwPen );
1966 DeleteObject( hbPen );
1967 DeleteObject( hShade );
1970 /******************************************************************************
1973 * This method repaints the tab control..
1975 static void TAB_Refresh (HWND hwnd, HDC hdc)
1977 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1981 if (!infoPtr->DoRedraw)
1984 hOldFont = SelectObject (hdc, infoPtr->hFont);
1986 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
1988 for (i = 0; i < infoPtr->uNumItem; i++)
1989 TAB_DrawItem (hwnd, hdc, i);
1993 /* Draw all the non selected item first */
1994 for (i = 0; i < infoPtr->uNumItem; i++)
1996 if (i != infoPtr->iSelected)
1997 TAB_DrawItem (hwnd, hdc, i);
2000 /* Now, draw the border, draw it before the selected item
2001 * since the selected item overwrites part of the border. */
2002 TAB_DrawBorder (hwnd, hdc);
2004 /* Then, draw the selected item */
2005 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
2007 /* If we haven't set the current focus yet, set it now.
2008 * Only happens when we first paint the tab controls */
2009 if (infoPtr->uFocus == -1)
2010 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
2013 SelectObject (hdc, hOldFont);
2017 TAB_GetRowCount (HWND hwnd )
2019 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2021 return infoPtr->uNumRows;
2025 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
2027 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2029 infoPtr->DoRedraw=(BOOL) wParam;
2033 static LRESULT TAB_EraseBackground(
2040 HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
2042 hdc = givenDC ? givenDC : GetDC(hwnd);
2044 GetClientRect(hwnd, &clientRect);
2046 FillRect(hdc, &clientRect, brush);
2049 ReleaseDC(hwnd, hdc);
2051 DeleteObject(brush);
2056 /******************************************************************************
2057 * TAB_EnsureSelectionVisible
2059 * This method will make sure that the current selection is completely
2060 * visible by scrolling until it is.
2062 static void TAB_EnsureSelectionVisible(
2066 INT iSelected = infoPtr->iSelected;
2067 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2068 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2070 /* set the items row to the bottommost row or topmost row depending on
2072 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2077 if(lStyle & TCS_VERTICAL)
2078 newselected = infoPtr->items[iSelected].rect.left;
2080 newselected = infoPtr->items[iSelected].rect.top;
2082 /* the target row is always (number of rows - 1)
2083 as row 0 is furthest from the clientRect */
2084 iTargetRow = infoPtr->uNumRows - 1;
2086 if (newselected != iTargetRow)
2089 if(lStyle & TCS_VERTICAL)
2091 for (i=0; i < infoPtr->uNumItem; i++)
2093 /* move everything in the row of the selected item to the iTargetRow */
2094 if (infoPtr->items[i].rect.left == newselected )
2095 infoPtr->items[i].rect.left = iTargetRow;
2098 if (infoPtr->items[i].rect.left > newselected)
2099 infoPtr->items[i].rect.left-=1;
2105 for (i=0; i < infoPtr->uNumItem; i++)
2107 if (infoPtr->items[i].rect.top == newselected )
2108 infoPtr->items[i].rect.top = iTargetRow;
2111 if (infoPtr->items[i].rect.top > newselected)
2112 infoPtr->items[i].rect.top-=1;
2116 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2121 * Do the trivial cases first.
2123 if ( (!infoPtr->needsScrolling) ||
2124 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2127 if (infoPtr->leftmostVisible >= iSelected)
2129 infoPtr->leftmostVisible = iSelected;
2136 /* Calculate the part of the client area that is visible */
2137 GetClientRect(hwnd, &r);
2140 GetClientRect(infoPtr->hwndUpDown, &r);
2143 if ((infoPtr->items[iSelected].rect.right -
2144 infoPtr->items[iSelected].rect.left) >= width )
2146 /* Special case: width of selected item is greater than visible
2149 infoPtr->leftmostVisible = iSelected;
2153 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2155 if ((infoPtr->items[iSelected].rect.right -
2156 infoPtr->items[i].rect.left) < width)
2159 infoPtr->leftmostVisible = i;
2163 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2164 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2166 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2167 MAKELONG(infoPtr->leftmostVisible, 0));
2170 /******************************************************************************
2171 * TAB_InvalidateTabArea
2173 * This method will invalidate the portion of the control that contains the
2174 * tabs. It is called when the state of the control changes and needs
2177 static void TAB_InvalidateTabArea(
2182 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2183 INT lastRow = infoPtr->uNumRows - 1;
2185 if (lastRow < 0) return;
2187 GetClientRect(hwnd, &clientRect);
2189 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2191 clientRect.top = clientRect.bottom -
2192 infoPtr->tabHeight -
2193 lastRow * (infoPtr->tabHeight - 2) -
2194 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 2;
2196 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2198 clientRect.left = clientRect.right - infoPtr->tabHeight -
2199 lastRow * (infoPtr->tabHeight - 2) -
2200 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 2;
2202 else if(lStyle & TCS_VERTICAL)
2204 clientRect.right = clientRect.left + infoPtr->tabHeight +
2205 lastRow * (infoPtr->tabHeight - 2) -
2206 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 1;
2211 clientRect.bottom = clientRect.top + infoPtr->tabHeight +
2212 lastRow * (infoPtr->tabHeight - 2) +
2213 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 1;
2216 InvalidateRect(hwnd, &clientRect, TRUE);
2220 TAB_Paint (HWND hwnd, WPARAM wParam)
2225 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2226 TAB_Refresh (hwnd, hdc);
2229 EndPaint (hwnd, &ps);
2235 TAB_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2237 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2242 GetClientRect (hwnd, &rect);
2243 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
2244 rect.top, rect.left, rect.bottom, rect.right);
2246 pti = (TCITEMA *)lParam;
2247 iItem = (INT)wParam;
2249 if (iItem < 0) return -1;
2250 if (iItem > infoPtr->uNumItem)
2251 iItem = infoPtr->uNumItem;
2253 if (infoPtr->uNumItem == 0) {
2254 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
2255 infoPtr->uNumItem++;
2256 infoPtr->iSelected = 0;
2259 TAB_ITEM *oldItems = infoPtr->items;
2261 infoPtr->uNumItem++;
2262 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2264 /* pre insert copy */
2266 memcpy (&infoPtr->items[0], &oldItems[0],
2267 iItem * sizeof(TAB_ITEM));
2270 /* post insert copy */
2271 if (iItem < infoPtr->uNumItem - 1) {
2272 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2273 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2277 if (iItem <= infoPtr->iSelected)
2278 infoPtr->iSelected++;
2280 COMCTL32_Free (oldItems);
2283 infoPtr->items[iItem].mask = pti->mask;
2284 if (pti->mask & TCIF_TEXT)
2285 Str_SetPtrAtoW (&infoPtr->items[iItem].pszText, pti->pszText);
2287 if (pti->mask & TCIF_IMAGE)
2288 infoPtr->items[iItem].iImage = pti->iImage;
2290 if (pti->mask & TCIF_PARAM)
2291 infoPtr->items[iItem].lParam = pti->lParam;
2293 TAB_SetItemBounds(hwnd);
2294 TAB_InvalidateTabArea(hwnd, infoPtr);
2296 TRACE("[%04x]: added item %d %s\n",
2297 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2304 TAB_InsertItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2306 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2311 GetClientRect (hwnd, &rect);
2312 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
2313 rect.top, rect.left, rect.bottom, rect.right);
2315 pti = (TCITEMW *)lParam;
2316 iItem = (INT)wParam;
2318 if (iItem < 0) return -1;
2319 if (iItem > infoPtr->uNumItem)
2320 iItem = infoPtr->uNumItem;
2322 if (infoPtr->uNumItem == 0) {
2323 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
2324 infoPtr->uNumItem++;
2325 infoPtr->iSelected = 0;
2328 TAB_ITEM *oldItems = infoPtr->items;
2330 infoPtr->uNumItem++;
2331 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2333 /* pre insert copy */
2335 memcpy (&infoPtr->items[0], &oldItems[0],
2336 iItem * sizeof(TAB_ITEM));
2339 /* post insert copy */
2340 if (iItem < infoPtr->uNumItem - 1) {
2341 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2342 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2346 if (iItem <= infoPtr->iSelected)
2347 infoPtr->iSelected++;
2349 COMCTL32_Free (oldItems);
2352 infoPtr->items[iItem].mask = pti->mask;
2353 if (pti->mask & TCIF_TEXT)
2354 Str_SetPtrW (&infoPtr->items[iItem].pszText, pti->pszText);
2356 if (pti->mask & TCIF_IMAGE)
2357 infoPtr->items[iItem].iImage = pti->iImage;
2359 if (pti->mask & TCIF_PARAM)
2360 infoPtr->items[iItem].lParam = pti->lParam;
2362 TAB_SetItemBounds(hwnd);
2363 TAB_InvalidateTabArea(hwnd, infoPtr);
2365 TRACE("[%04x]: added item %d %s\n",
2366 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2373 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2375 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2376 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2379 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
2381 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2382 infoPtr->tabWidth = (INT)LOWORD(lParam);
2383 infoPtr->tabHeight = (INT)HIWORD(lParam);
2385 infoPtr->fSizeSet = TRUE;
2391 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2393 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2398 iItem = (INT)wParam;
2399 tabItem = (LPTCITEMA)lParam;
2401 TRACE("%d %p\n", iItem, tabItem);
2402 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2404 wineItem = &infoPtr->items[iItem];
2406 if (tabItem->mask & TCIF_IMAGE)
2407 wineItem->iImage = tabItem->iImage;
2409 if (tabItem->mask & TCIF_PARAM)
2410 wineItem->lParam = tabItem->lParam;
2412 if (tabItem->mask & TCIF_RTLREADING)
2413 FIXME("TCIF_RTLREADING\n");
2415 if (tabItem->mask & TCIF_STATE)
2416 wineItem->dwState = tabItem->dwState;
2418 if (tabItem->mask & TCIF_TEXT)
2419 Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2421 /* Update and repaint tabs */
2422 TAB_SetItemBounds(hwnd);
2423 TAB_InvalidateTabArea(hwnd,infoPtr);
2430 TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2432 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2437 iItem = (INT)wParam;
2438 tabItem = (LPTCITEMW)lParam;
2440 TRACE("%d %p\n", iItem, tabItem);
2441 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2443 wineItem = &infoPtr->items[iItem];
2445 if (tabItem->mask & TCIF_IMAGE)
2446 wineItem->iImage = tabItem->iImage;
2448 if (tabItem->mask & TCIF_PARAM)
2449 wineItem->lParam = tabItem->lParam;
2451 if (tabItem->mask & TCIF_RTLREADING)
2452 FIXME("TCIF_RTLREADING\n");
2454 if (tabItem->mask & TCIF_STATE)
2455 wineItem->dwState = tabItem->dwState;
2457 if (tabItem->mask & TCIF_TEXT)
2458 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2460 /* Update and repaint tabs */
2461 TAB_SetItemBounds(hwnd);
2462 TAB_InvalidateTabArea(hwnd,infoPtr);
2469 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2471 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2473 return infoPtr->uNumItem;
2478 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2480 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2485 iItem = (INT)wParam;
2486 tabItem = (LPTCITEMA)lParam;
2488 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2491 wineItem = &infoPtr->items[iItem];
2493 if (tabItem->mask & TCIF_IMAGE)
2494 tabItem->iImage = wineItem->iImage;
2496 if (tabItem->mask & TCIF_PARAM)
2497 tabItem->lParam = wineItem->lParam;
2499 if (tabItem->mask & TCIF_RTLREADING)
2500 FIXME("TCIF_RTLREADING\n");
2502 if (tabItem->mask & TCIF_STATE)
2503 tabItem->dwState = wineItem->dwState;
2505 if (tabItem->mask & TCIF_TEXT)
2506 Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2513 TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2515 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2520 iItem = (INT)wParam;
2521 tabItem = (LPTCITEMW)lParam;
2523 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2526 wineItem=& infoPtr->items[iItem];
2528 if (tabItem->mask & TCIF_IMAGE)
2529 tabItem->iImage = wineItem->iImage;
2531 if (tabItem->mask & TCIF_PARAM)
2532 tabItem->lParam = wineItem->lParam;
2534 if (tabItem->mask & TCIF_RTLREADING)
2535 FIXME("TCIF_RTLREADING\n");
2537 if (tabItem->mask & TCIF_STATE)
2538 tabItem->dwState = wineItem->dwState;
2540 if (tabItem->mask & TCIF_TEXT)
2541 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2548 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2550 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2551 INT iItem = (INT) wParam;
2552 BOOL bResult = FALSE;
2554 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2556 TAB_ITEM *oldItems = infoPtr->items;
2558 infoPtr->uNumItem--;
2559 infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
2562 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
2564 if (iItem < infoPtr->uNumItem)
2565 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
2566 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
2568 COMCTL32_Free(oldItems);
2570 /* Readjust the selected index */
2571 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2572 infoPtr->iSelected--;
2574 if (iItem < infoPtr->iSelected)
2575 infoPtr->iSelected--;
2577 if (infoPtr->uNumItem == 0)
2578 infoPtr->iSelected = -1;
2580 /* Reposition and repaint tabs */
2581 TAB_SetItemBounds(hwnd);
2582 TAB_InvalidateTabArea(hwnd,infoPtr);
2591 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2593 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2595 COMCTL32_Free (infoPtr->items);
2596 infoPtr->uNumItem = 0;
2597 infoPtr->iSelected = -1;
2598 if (infoPtr->iHotTracked >= 0)
2599 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2600 infoPtr->iHotTracked = -1;
2602 TAB_SetItemBounds(hwnd);
2603 TAB_InvalidateTabArea(hwnd,infoPtr);
2609 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2611 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2614 return (LRESULT)infoPtr->hFont;
2618 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2621 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2623 TRACE("%x %lx\n",wParam, lParam);
2625 infoPtr->hFont = (HFONT)wParam;
2627 TAB_SetItemBounds(hwnd);
2629 TAB_InvalidateTabArea(hwnd, infoPtr);
2636 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2638 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2641 return (LRESULT)infoPtr->himl;
2645 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2647 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2648 HIMAGELIST himlPrev;
2651 himlPrev = infoPtr->himl;
2652 infoPtr->himl= (HIMAGELIST)lParam;
2653 return (LRESULT)himlPrev;
2657 TAB_GetUnicodeFormat (HWND hwnd)
2659 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2660 return infoPtr->bUnicode;
2664 TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
2666 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2667 BOOL bTemp = infoPtr->bUnicode;
2669 infoPtr->bUnicode = (BOOL)wParam;
2675 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2678 /* I'm not really sure what the following code was meant to do.
2679 This is what it is doing:
2680 When WM_SIZE is sent with SIZE_RESTORED, the control
2681 gets positioned in the top left corner.
2685 UINT uPosFlags,cx,cy;
2689 parent = GetParent (hwnd);
2690 GetClientRect(parent, &parent_rect);
2693 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2694 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2696 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2697 cx, cy, uPosFlags | SWP_NOZORDER);
2699 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2702 /* Recompute the size/position of the tabs. */
2703 TAB_SetItemBounds (hwnd);
2705 /* Force a repaint of the control. */
2706 InvalidateRect(hwnd, NULL, TRUE);
2713 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2716 TEXTMETRICA fontMetrics;
2721 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
2723 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
2725 infoPtr->uNumItem = 0;
2726 infoPtr->uNumRows = 0;
2729 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
2730 infoPtr->iSelected = -1;
2731 infoPtr->iHotTracked = -1;
2732 infoPtr->uFocus = -1;
2733 infoPtr->hwndToolTip = 0;
2734 infoPtr->DoRedraw = TRUE;
2735 infoPtr->needsScrolling = FALSE;
2736 infoPtr->hwndUpDown = 0;
2737 infoPtr->leftmostVisible = 0;
2738 infoPtr->fSizeSet = FALSE;
2739 infoPtr->bUnicode = IsWindowUnicode (hwnd);
2741 TRACE("Created tab control, hwnd [%04x]\n", hwnd);
2743 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2744 if you don't specify it in CreateWindow. This is necessary in
2745 order for paint to work correctly. This follows windows behaviour. */
2746 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
2747 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2749 if (dwStyle & TCS_TOOLTIPS) {
2750 /* Create tooltip control */
2751 infoPtr->hwndToolTip =
2752 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2753 CW_USEDEFAULT, CW_USEDEFAULT,
2754 CW_USEDEFAULT, CW_USEDEFAULT,
2757 /* Send NM_TOOLTIPSCREATED notification */
2758 if (infoPtr->hwndToolTip) {
2759 NMTOOLTIPSCREATED nmttc;
2761 nmttc.hdr.hwndFrom = hwnd;
2762 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
2763 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2764 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2766 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2767 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
2772 * We need to get text information so we need a DC and we need to select
2776 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
2778 /* Use the system font to determine the initial height of a tab. */
2779 GetTextMetricsA(hdc, &fontMetrics);
2782 * Make sure there is enough space for the letters + growing the
2783 * selected item + extra space for the selected item.
2785 infoPtr->tabHeight = fontMetrics.tmHeight + 2 * VERTICAL_ITEM_PADDING +
2786 SELECTED_TAB_OFFSET;
2788 /* Initialize the width of a tab. */
2789 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
2791 SelectObject (hdc, hOldFont);
2792 ReleaseDC(hwnd, hdc);
2798 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
2800 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2806 if (infoPtr->items) {
2807 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
2808 if (infoPtr->items[iItem].pszText)
2809 COMCTL32_Free (infoPtr->items[iItem].pszText);
2811 COMCTL32_Free (infoPtr->items);
2814 if (infoPtr->hwndToolTip)
2815 DestroyWindow (infoPtr->hwndToolTip);
2817 if (infoPtr->hwndUpDown)
2818 DestroyWindow(infoPtr->hwndUpDown);
2820 if (infoPtr->iHotTracked >= 0)
2821 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2823 COMCTL32_Free (infoPtr);
2824 SetWindowLongA(hwnd, 0, 0);
2828 static LRESULT WINAPI
2829 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2832 TRACE("hwnd=%x msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
2833 if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
2834 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
2838 case TCM_GETIMAGELIST:
2839 return TAB_GetImageList (hwnd, wParam, lParam);
2841 case TCM_SETIMAGELIST:
2842 return TAB_SetImageList (hwnd, wParam, lParam);
2844 case TCM_GETITEMCOUNT:
2845 return TAB_GetItemCount (hwnd, wParam, lParam);
2848 return TAB_GetItemA (hwnd, wParam, lParam);
2851 return TAB_GetItemW (hwnd, wParam, lParam);
2854 return TAB_SetItemA (hwnd, wParam, lParam);
2857 return TAB_SetItemW (hwnd, wParam, lParam);
2859 case TCM_DELETEITEM:
2860 return TAB_DeleteItem (hwnd, wParam, lParam);
2862 case TCM_DELETEALLITEMS:
2863 return TAB_DeleteAllItems (hwnd, wParam, lParam);
2865 case TCM_GETITEMRECT:
2866 return TAB_GetItemRect (hwnd, wParam, lParam);
2869 return TAB_GetCurSel (hwnd);
2872 return TAB_HitTest (hwnd, wParam, lParam);
2875 return TAB_SetCurSel (hwnd, wParam);
2877 case TCM_INSERTITEMA:
2878 return TAB_InsertItemA (hwnd, wParam, lParam);
2880 case TCM_INSERTITEMW:
2881 return TAB_InsertItemW (hwnd, wParam, lParam);
2883 case TCM_SETITEMEXTRA:
2884 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
2887 case TCM_ADJUSTRECT:
2888 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
2890 case TCM_SETITEMSIZE:
2891 return TAB_SetItemSize (hwnd, wParam, lParam);
2893 case TCM_REMOVEIMAGE:
2894 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
2897 case TCM_SETPADDING:
2898 FIXME("Unimplemented msg TCM_SETPADDING\n");
2901 case TCM_GETROWCOUNT:
2902 return TAB_GetRowCount(hwnd);
2904 case TCM_GETUNICODEFORMAT:
2905 return TAB_GetUnicodeFormat (hwnd);
2907 case TCM_SETUNICODEFORMAT:
2908 return TAB_SetUnicodeFormat (hwnd, wParam);
2910 case TCM_HIGHLIGHTITEM:
2911 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
2914 case TCM_GETTOOLTIPS:
2915 return TAB_GetToolTips (hwnd, wParam, lParam);
2917 case TCM_SETTOOLTIPS:
2918 return TAB_SetToolTips (hwnd, wParam, lParam);
2920 case TCM_GETCURFOCUS:
2921 return TAB_GetCurFocus (hwnd);
2923 case TCM_SETCURFOCUS:
2924 return TAB_SetCurFocus (hwnd, wParam);
2926 case TCM_SETMINTABWIDTH:
2927 FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
2930 case TCM_DESELECTALL:
2931 FIXME("Unimplemented msg TCM_DESELECTALL\n");
2934 case TCM_GETEXTENDEDSTYLE:
2935 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
2938 case TCM_SETEXTENDEDSTYLE:
2939 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
2943 return TAB_GetFont (hwnd, wParam, lParam);
2946 return TAB_SetFont (hwnd, wParam, lParam);
2949 return TAB_Create (hwnd, wParam, lParam);
2952 return TAB_Destroy (hwnd, wParam, lParam);
2955 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2957 case WM_LBUTTONDOWN:
2958 return TAB_LButtonDown (hwnd, wParam, lParam);
2961 return TAB_LButtonUp (hwnd, wParam, lParam);
2964 return SendMessageA(GetParent(hwnd), WM_NOTIFY, wParam, lParam);
2966 case WM_RBUTTONDOWN:
2967 return TAB_RButtonDown (hwnd, wParam, lParam);
2970 return TAB_MouseMove (hwnd, wParam, lParam);
2973 return TAB_EraseBackground (hwnd, (HDC)wParam);
2976 return TAB_Paint (hwnd, wParam);
2979 return TAB_Size (hwnd, wParam, lParam);
2982 return TAB_SetRedraw (hwnd, wParam);
2985 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
2987 case WM_STYLECHANGED:
2988 TAB_SetItemBounds (hwnd);
2989 InvalidateRect(hwnd, NULL, TRUE);
2994 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
2997 return TAB_KeyUp(hwnd, wParam);
2999 return TAB_NCHitTest(hwnd, lParam);
3002 if (uMsg >= WM_USER)
3003 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3004 uMsg, wParam, lParam);
3005 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
3017 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3018 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3019 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
3020 wndClass.cbClsExtra = 0;
3021 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3022 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
3023 wndClass.hbrBackground = (HBRUSH)NULL;
3024 wndClass.lpszClassName = WC_TABCONTROLA;
3026 RegisterClassA (&wndClass);
3031 TAB_Unregister (void)
3033 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);