4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
7 * Copyright 2003 Vitaliy Margolen
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Unicode support (under construction)
32 * TCM_GETEXTENDEDSTYLE
33 * TCM_SETEXTENDEDSTYLE
47 #include "wine/debug.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(tab);
58 RECT rect; /* bounding rectangle of the item relative to the
59 * leftmost item (the leftmost item, 0, would have a
60 * "left" member of 0 in this rectangle)
62 * additionally the top member hold the row number
63 * and bottom is unused and should be 0 */
64 BYTE extra[1]; /* Space for caller supplied info, variable size */
67 /* The size of a tab item depends on how much extra data is requested */
68 #define TAB_ITEM_SIZE(infoPtr) (sizeof(TAB_ITEM) - sizeof(BYTE) + infoPtr->cbInfo)
72 HWND hwndNotify; /* notification window (parent) */
73 UINT uNumItem; /* number of tab items */
74 UINT uNumRows; /* number of tab rows */
75 INT tabHeight; /* height of the tab row */
76 INT tabWidth; /* width of tabs */
77 INT tabMinWidth; /* minimum width of items */
78 USHORT uHItemPadding; /* amount of horizontal padding, in pixels */
79 USHORT uVItemPadding; /* amount of vertical padding, in pixels */
80 USHORT uHItemPadding_s; /* Set amount of horizontal padding, in pixels */
81 USHORT uVItemPadding_s; /* Set amount of vertical padding, in pixels */
82 HFONT hFont; /* handle to the current font */
83 HCURSOR hcurArrow; /* handle to the current cursor */
84 HIMAGELIST himl; /* handle to a image list (may be 0) */
85 HWND hwndToolTip; /* handle to tab's tooltip */
86 INT leftmostVisible; /* Used for scrolling, this member contains
87 * the index of the first visible item */
88 INT iSelected; /* the currently selected item */
89 INT iHotTracked; /* the highlighted item under the mouse */
90 INT uFocus; /* item which has the focus */
91 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
92 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
93 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
94 * the size of the control */
95 BOOL fHeightSet; /* was the height of the tabs explicitly set? */
96 BOOL bUnicode; /* Unicode control? */
97 HWND hwndUpDown; /* Updown control used for scrolling */
98 INT cbInfo; /* Number of bytes of caller supplied info per tab */
101 /******************************************************************************
102 * Positioning constants
104 #define SELECTED_TAB_OFFSET 2
105 #define ROUND_CORNER_SIZE 2
106 #define DISPLAY_AREA_PADDINGX 2
107 #define DISPLAY_AREA_PADDINGY 2
108 #define CONTROL_BORDER_SIZEX 2
109 #define CONTROL_BORDER_SIZEY 2
110 #define BUTTON_SPACINGX 3
111 #define BUTTON_SPACINGY 3
112 #define FLAT_BTN_SPACINGX 8
113 #define DEFAULT_TAB_WIDTH 96
115 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongPtrW(hwnd,0))
117 /******************************************************************************
118 * Hot-tracking timer constants
120 #define TAB_HOTTRACK_TIMER 1
121 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
123 /******************************************************************************
126 static void TAB_Refresh (HWND hwnd, HDC hdc);
127 static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
128 static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
129 static void TAB_DrawItem(HWND hwnd, HDC hdc, INT iItem);
130 static void TAB_DrawItemInterior(HWND hwnd, HDC hdc, INT iItem, RECT* drawRect);
133 TAB_SendSimpleNotify (HWND hwnd, UINT code)
135 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
138 nmhdr.hwndFrom = hwnd;
139 nmhdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
142 return (BOOL) SendMessageA (infoPtr->hwndNotify, WM_NOTIFY,
143 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
147 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
148 WPARAM wParam, LPARAM lParam)
156 msg.time = GetMessageTime ();
157 msg.pt.x = LOWORD(GetMessagePos ());
158 msg.pt.y = HIWORD(GetMessagePos ());
160 SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
164 TAB_DumpItemExternalA(TCITEMA *pti, UINT iItem)
167 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
168 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
169 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextA=%s\n",
170 iItem, pti->iImage, pti->lParam, debugstr_a(pti->pszText));
176 TAB_DumpItemExternalW(TCITEMW *pti, UINT iItem)
179 TRACE("external tab %d, mask=0x%08x, dwState=0x%08lx, dwStateMask=0x%08lx, cchTextMax=0x%08x\n",
180 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
181 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
182 iItem, pti->iImage, pti->lParam, debugstr_w(pti->pszText));
187 TAB_DumpItemInternal(TAB_INFO *infoPtr, UINT iItem)
192 ti = &infoPtr->items[iItem];
193 TRACE("tab %d, mask=0x%08x, dwState=0x%08lx, pszText=%s, iImage=%d\n",
194 iItem, ti->mask, ti->dwState, debugstr_w(ti->pszText),
196 TRACE("tab %d, rect.left=%ld, rect.top(row)=%ld\n",
197 iItem, ti->rect.left, ti->rect.top);
202 TAB_GetCurSel (HWND hwnd)
204 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
206 return infoPtr->iSelected;
210 TAB_GetCurFocus (HWND hwnd)
212 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
214 return infoPtr->uFocus;
218 TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
220 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
222 if (infoPtr == NULL) return 0;
223 return (LRESULT)infoPtr->hwndToolTip;
227 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
229 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
230 INT iItem = (INT)wParam;
234 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
235 prevItem=infoPtr->iSelected;
236 infoPtr->iSelected=iItem;
237 TAB_EnsureSelectionVisible(hwnd, infoPtr);
238 TAB_InvalidateTabArea(hwnd, infoPtr);
244 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
246 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
247 INT iItem=(INT) wParam;
249 if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
251 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
252 FIXME("Should set input focus\n");
254 int oldFocus = infoPtr->uFocus;
255 if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) {
256 infoPtr->uFocus = iItem;
257 if (oldFocus != -1) {
258 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
259 infoPtr->iSelected = iItem;
260 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
263 infoPtr->iSelected = iItem;
264 TAB_EnsureSelectionVisible(hwnd, infoPtr);
265 TAB_InvalidateTabArea(hwnd, infoPtr);
273 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
275 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
277 if (infoPtr == NULL) return 0;
278 infoPtr->hwndToolTip = (HWND)wParam;
283 TAB_SetPadding (HWND hwnd, WPARAM wParam, LPARAM lParam)
285 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
287 if (infoPtr == NULL) return 0;
288 infoPtr->uHItemPadding_s=LOWORD(lParam);
289 infoPtr->uVItemPadding_s=HIWORD(lParam);
293 /******************************************************************************
294 * TAB_InternalGetItemRect
296 * This method will calculate the rectangle representing a given tab item in
297 * client coordinates. This method takes scrolling into account.
299 * This method returns TRUE if the item is visible in the window and FALSE
300 * if it is completely outside the client area.
302 static BOOL TAB_InternalGetItemRect(
309 RECT tmpItemRect,clientRect;
310 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
312 /* Perform a sanity check and a trivial visibility check. */
313 if ( (infoPtr->uNumItem <= 0) ||
314 (itemIndex >= infoPtr->uNumItem) ||
315 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
319 * Avoid special cases in this procedure by assigning the "out"
320 * parameters if the caller didn't supply them
322 if (itemRect == NULL)
323 itemRect = &tmpItemRect;
325 /* Retrieve the unmodified item rect. */
326 *itemRect = infoPtr->items[itemIndex].rect;
328 /* calculate the times bottom and top based on the row */
329 GetClientRect(hwnd, &clientRect);
331 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
333 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * infoPtr->tabHeight -
334 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
335 itemRect->left = itemRect->right - infoPtr->tabHeight;
337 else if (lStyle & TCS_VERTICAL)
339 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * infoPtr->tabHeight +
340 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
341 itemRect->right = itemRect->left + infoPtr->tabHeight;
343 else if (lStyle & TCS_BOTTOM)
345 itemRect->bottom = clientRect.bottom - itemRect->top * infoPtr->tabHeight -
346 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
347 itemRect->top = itemRect->bottom - infoPtr->tabHeight;
349 else /* not TCS_BOTTOM and not TCS_VERTICAL */
351 itemRect->top = clientRect.top + itemRect->top * infoPtr->tabHeight +
352 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
353 itemRect->bottom = itemRect->top + infoPtr->tabHeight;
357 * "scroll" it to make sure the item at the very left of the
358 * tab control is the leftmost visible tab.
360 if(lStyle & TCS_VERTICAL)
364 -infoPtr->items[infoPtr->leftmostVisible].rect.top);
367 * Move the rectangle so the first item is slightly offset from
368 * the bottom of the tab control.
372 SELECTED_TAB_OFFSET);
377 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
381 * Move the rectangle so the first item is slightly offset from
382 * the left of the tab control.
388 TRACE("item %d tab h=%d, rect=(%ld,%ld)-(%ld,%ld)\n",
389 itemIndex, infoPtr->tabHeight,
390 itemRect->left, itemRect->top, itemRect->right, itemRect->bottom);
392 /* Now, calculate the position of the item as if it were selected. */
393 if (selectedRect!=NULL)
395 CopyRect(selectedRect, itemRect);
397 /* The rectangle of a selected item is a bit wider. */
398 if(lStyle & TCS_VERTICAL)
399 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
401 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
403 /* If it also a bit higher. */
404 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
406 selectedRect->left -= 2; /* the border is thicker on the right */
407 selectedRect->right += SELECTED_TAB_OFFSET;
409 else if (lStyle & TCS_VERTICAL)
411 selectedRect->left -= SELECTED_TAB_OFFSET;
412 selectedRect->right += 1;
414 else if (lStyle & TCS_BOTTOM)
416 selectedRect->bottom += SELECTED_TAB_OFFSET;
418 else /* not TCS_BOTTOM and not TCS_VERTICAL */
420 selectedRect->top -= SELECTED_TAB_OFFSET;
421 selectedRect->bottom -= 1;
428 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
430 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
431 (LPRECT)lParam, (LPRECT)NULL);
434 /******************************************************************************
437 * This method is called to handle keyboard input
439 static LRESULT TAB_KeyUp(
443 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
449 newItem = infoPtr->uFocus - 1;
452 newItem = infoPtr->uFocus + 1;
457 * If we changed to a valid item, change the selection
459 if ((newItem >= 0) &&
460 (newItem < infoPtr->uNumItem) &&
461 (infoPtr->uFocus != newItem))
463 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
465 infoPtr->iSelected = newItem;
466 infoPtr->uFocus = newItem;
467 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
469 TAB_EnsureSelectionVisible(hwnd, infoPtr);
470 TAB_InvalidateTabArea(hwnd, infoPtr);
477 /******************************************************************************
480 * This method is called whenever the focus goes in or out of this control
481 * it is used to update the visual state of the control.
483 static LRESULT TAB_FocusChanging(
489 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
494 * Get the rectangle for the item.
496 isVisible = TAB_InternalGetItemRect(hwnd,
503 * If the rectangle is not completely invisible, invalidate that
504 * portion of the window.
508 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
509 selectedRect.left,selectedRect.top,
510 selectedRect.right,selectedRect.bottom);
511 InvalidateRect(hwnd, &selectedRect, TRUE);
515 * Don't otherwise disturb normal behavior.
517 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
520 static INT TAB_InternalHitTest (
530 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
532 TAB_InternalGetItemRect(hwnd, infoPtr, iCount, &rect, NULL);
534 if (PtInRect(&rect, pt))
536 *flags = TCHT_ONITEM;
541 *flags = TCHT_NOWHERE;
546 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
548 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
549 LPTCHITTESTINFO lptest = (LPTCHITTESTINFO) lParam;
551 return TAB_InternalHitTest (hwnd, infoPtr, lptest->pt, &lptest->flags);
554 /******************************************************************************
557 * Napster v2b5 has a tab control for its main navigation which has a client
558 * area that covers the whole area of the dialog pages.
559 * That's why it receives all msgs for that area and the underlying dialog ctrls
561 * So I decided that we should handle WM_NCHITTEST here and return
562 * HTTRANSPARENT if we don't hit the tab control buttons.
563 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
564 * doesn't do it that way. Maybe depends on tab control styles ?
567 TAB_NCHitTest (HWND hwnd, LPARAM lParam)
569 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
573 pt.x = LOWORD(lParam);
574 pt.y = HIWORD(lParam);
575 ScreenToClient(hwnd, &pt);
577 if (TAB_InternalHitTest(hwnd, infoPtr, pt, &dummyflag) == -1)
578 return HTTRANSPARENT;
584 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
586 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
590 if (infoPtr->hwndToolTip)
591 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
592 WM_LBUTTONDOWN, wParam, lParam);
594 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
598 if (infoPtr->hwndToolTip)
599 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
600 WM_LBUTTONDOWN, wParam, lParam);
602 pt.x = (INT)LOWORD(lParam);
603 pt.y = (INT)HIWORD(lParam);
605 newItem = TAB_InternalHitTest (hwnd, infoPtr, pt, &dummy);
607 TRACE("On Tab, item %d\n", newItem);
609 if ((newItem != -1) && (infoPtr->iSelected != newItem))
611 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING) != TRUE)
613 infoPtr->iSelected = newItem;
614 infoPtr->uFocus = newItem;
615 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
617 TAB_EnsureSelectionVisible(hwnd, infoPtr);
619 TAB_InvalidateTabArea(hwnd, infoPtr);
626 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
628 TAB_SendSimpleNotify(hwnd, NM_CLICK);
634 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
636 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
640 /******************************************************************************
641 * TAB_DrawLoneItemInterior
643 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
644 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
645 * up the device context and font. This routine does the same setup but
646 * only calls TAB_DrawItemInterior for the single specified item.
649 TAB_DrawLoneItemInterior(HWND hwnd, TAB_INFO* infoPtr, int iItem)
651 HDC hdc = GetDC(hwnd);
654 /* Clip UpDown control to not draw over it */
655 if (infoPtr->needsScrolling)
657 GetWindowRect(hwnd, &rC);
658 GetWindowRect(infoPtr->hwndUpDown, &r);
659 ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top);
661 TAB_DrawItemInterior(hwnd, hdc, iItem, NULL);
662 ReleaseDC(hwnd, hdc);
665 /******************************************************************************
666 * TAB_HotTrackTimerProc
668 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
669 * timer is setup so we can check if the mouse is moved out of our window.
670 * (We don't get an event when the mouse leaves, the mouse-move events just
671 * stop being delivered to our window and just start being delivered to
672 * another window.) This function is called when the timer triggers so
673 * we can check if the mouse has left our window. If so, we un-highlight
674 * the hot-tracked tab.
677 TAB_HotTrackTimerProc
679 HWND hwnd, /* handle of window for timer messages */
680 UINT uMsg, /* WM_TIMER message */
681 UINT idEvent, /* timer identifier */
682 DWORD dwTime /* current system time */
685 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
687 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
692 ** If we can't get the cursor position, or if the cursor is outside our
693 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
694 ** "outside" even if it is within our bounding rect if another window
695 ** overlaps. Note also that the case where the cursor stayed within our
696 ** window but has moved off the hot-tracked tab will be handled by the
697 ** WM_MOUSEMOVE event.
699 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
701 /* Redraw iHotTracked to look normal */
702 INT iRedraw = infoPtr->iHotTracked;
703 infoPtr->iHotTracked = -1;
704 TAB_DrawLoneItemInterior(hwnd, infoPtr, iRedraw);
706 /* Kill this timer */
707 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
712 /******************************************************************************
715 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
716 * should be highlighted. This function determines which tab in a tab control,
717 * if any, is under the mouse and records that information. The caller may
718 * supply output parameters to receive the item number of the tab item which
719 * was highlighted but isn't any longer and of the tab item which is now
720 * highlighted but wasn't previously. The caller can use this information to
721 * selectively redraw those tab items.
723 * If the caller has a mouse position, it can supply it through the pos
724 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
725 * supplies NULL and this function determines the current mouse position
733 int* out_redrawLeave,
737 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
742 if (out_redrawLeave != NULL)
743 *out_redrawLeave = -1;
744 if (out_redrawEnter != NULL)
745 *out_redrawEnter = -1;
747 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_HOTTRACK)
755 ScreenToClient(hwnd, &pt);
763 item = TAB_InternalHitTest(hwnd, infoPtr, pt, &flags);
766 if (item != infoPtr->iHotTracked)
768 if (infoPtr->iHotTracked >= 0)
770 /* Mark currently hot-tracked to be redrawn to look normal */
771 if (out_redrawLeave != NULL)
772 *out_redrawLeave = infoPtr->iHotTracked;
776 /* Kill timer which forces recheck of mouse pos */
777 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
782 /* Start timer so we recheck mouse pos */
783 UINT timerID = SetTimer
787 TAB_HOTTRACK_TIMER_INTERVAL,
788 TAB_HotTrackTimerProc
792 return; /* Hot tracking not available */
795 infoPtr->iHotTracked = item;
799 /* Mark new hot-tracked to be redrawn to look highlighted */
800 if (out_redrawEnter != NULL)
801 *out_redrawEnter = item;
806 /******************************************************************************
809 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
812 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
817 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
819 if (infoPtr->hwndToolTip)
820 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
821 WM_LBUTTONDOWN, wParam, lParam);
823 /* Determine which tab to highlight. Redraw tabs which change highlight
825 TAB_RecalcHotTrack(hwnd, &lParam, &redrawLeave, &redrawEnter);
827 if (redrawLeave != -1)
828 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawLeave);
829 if (redrawEnter != -1)
830 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawEnter);
835 /******************************************************************************
838 * Calculates the tab control's display area given the window rectangle or
839 * the window rectangle given the requested display rectangle.
841 static LRESULT TAB_AdjustRect(
846 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
847 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
848 LONG *iRightBottom, *iLeftTop;
850 TRACE ("hwnd=%p fLarger=%d (%ld,%ld)-(%ld,%ld)\n", hwnd, fLarger, prc->left, prc->top, prc->right, prc->bottom);
852 if(lStyle & TCS_VERTICAL)
854 iRightBottom = &(prc->right);
855 iLeftTop = &(prc->left);
859 iRightBottom = &(prc->bottom);
860 iLeftTop = &(prc->top);
863 if (fLarger) /* Go from display rectangle */
865 /* Add the height of the tabs. */
866 if (lStyle & TCS_BOTTOM)
867 *iRightBottom += infoPtr->tabHeight * infoPtr->uNumRows;
869 *iLeftTop -= infoPtr->tabHeight * infoPtr->uNumRows +
870 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
872 /* Inflate the rectangle for the padding */
873 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
875 /* Inflate for the border */
876 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEY);
878 else /* Go from window rectangle. */
880 /* Deflate the rectangle for the border */
881 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEY);
883 /* Deflate the rectangle for the padding */
884 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
886 /* Remove the height of the tabs. */
887 if (lStyle & TCS_BOTTOM)
888 *iRightBottom -= infoPtr->tabHeight * infoPtr->uNumRows;
890 *iLeftTop += (infoPtr->tabHeight) * infoPtr->uNumRows +
891 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
897 /******************************************************************************
900 * This method will handle the notification from the scroll control and
901 * perform the scrolling operation on the tab control.
903 static LRESULT TAB_OnHScroll(
909 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
911 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
913 if(nPos < infoPtr->leftmostVisible)
914 infoPtr->leftmostVisible--;
916 infoPtr->leftmostVisible++;
918 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
919 TAB_InvalidateTabArea(hwnd, infoPtr);
920 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
921 MAKELONG(infoPtr->leftmostVisible, 0));
927 /******************************************************************************
930 * This method will check the current scrolling state and make sure the
931 * scrolling control is displayed (or not).
933 static void TAB_SetupScrolling(
936 const RECT* clientRect)
939 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
941 if (infoPtr->needsScrolling)
947 * Calculate the position of the scroll control.
949 if(lStyle & TCS_VERTICAL)
951 controlPos.right = clientRect->right;
952 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
954 if (lStyle & TCS_BOTTOM)
956 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
957 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
961 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
962 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
967 controlPos.right = clientRect->right;
968 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
970 if (lStyle & TCS_BOTTOM)
972 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
973 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
977 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
978 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
983 * If we don't have a scroll control yet, we want to create one.
984 * If we have one, we want to make sure it's positioned properly.
986 if (infoPtr->hwndUpDown==0)
988 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
990 WS_VISIBLE | WS_CHILD | UDS_HORZ,
991 controlPos.left, controlPos.top,
992 controlPos.right - controlPos.left,
993 controlPos.bottom - controlPos.top,
1001 SetWindowPos(infoPtr->hwndUpDown,
1003 controlPos.left, controlPos.top,
1004 controlPos.right - controlPos.left,
1005 controlPos.bottom - controlPos.top,
1006 SWP_SHOWWINDOW | SWP_NOZORDER);
1009 /* Now calculate upper limit of the updown control range.
1010 * We do this by calculating how many tabs will be offscreen when the
1011 * last tab is visible.
1013 if(infoPtr->uNumItem)
1015 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
1016 maxRange = infoPtr->uNumItem;
1017 tabwidth = infoPtr->items[maxRange - 1].rect.right;
1019 for(; maxRange > 0; maxRange--)
1021 if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
1025 if(maxRange == infoPtr->uNumItem)
1031 /* If we once had a scroll control... hide it */
1032 if (infoPtr->hwndUpDown!=0)
1033 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1035 if (infoPtr->hwndUpDown)
1036 SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1039 /******************************************************************************
1042 * This method will calculate the position rectangles of all the items in the
1043 * control. The rectangle calculated starts at 0 for the first item in the
1044 * list and ignores scrolling and selection.
1045 * It also uses the current font to determine the height of the tab row and
1046 * it checks if all the tabs fit in the client area of the window. If they
1047 * don't, a scrolling control is added.
1049 static void TAB_SetItemBounds (HWND hwnd)
1051 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1052 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1053 TEXTMETRICA fontMetrics;
1056 INT curItemRowCount;
1057 HFONT hFont, hOldFont;
1067 * We need to get text information so we need a DC and we need to select
1072 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1073 hOldFont = SelectObject (hdc, hFont);
1076 * We will base the rectangle calculations on the client rectangle
1079 GetClientRect(hwnd, &clientRect);
1081 /* if TCS_VERTICAL then swap the height and width so this code places the
1082 tabs along the top of the rectangle and we can just rotate them after
1083 rather than duplicate all of the below code */
1084 if(lStyle & TCS_VERTICAL)
1086 iTemp = clientRect.bottom;
1087 clientRect.bottom = clientRect.right;
1088 clientRect.right = iTemp;
1091 /* Now use hPadding and vPadding */
1092 infoPtr->uHItemPadding = infoPtr->uHItemPadding_s;
1093 infoPtr->uVItemPadding = infoPtr->uVItemPadding_s;
1095 /* The leftmost item will be "0" aligned */
1097 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1099 if (!(infoPtr->fHeightSet))
1102 int icon_height = 0;
1104 /* Use the current font to determine the height of a tab. */
1105 GetTextMetricsA(hdc, &fontMetrics);
1107 /* Get the icon height */
1109 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1111 /* Take the highest between font or icon */
1112 if (fontMetrics.tmHeight > icon_height)
1113 item_height = fontMetrics.tmHeight + 2;
1115 item_height = icon_height;
1118 * Make sure there is enough space for the letters + icon + growing the
1119 * selected item + extra space for the selected item.
1121 infoPtr->tabHeight = item_height +
1122 ((lStyle & TCS_BUTTONS) ? 2 : 1) *
1123 infoPtr->uVItemPadding;
1125 TRACE("tabH=%d, tmH=%ld, iconh=%d\n",
1126 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1129 TRACE("client right=%ld\n", clientRect.right);
1131 /* Get the icon width */
1134 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1136 if (lStyle & TCS_FIXEDWIDTH)
1139 /* Add padding if icon is present */
1140 icon_width += infoPtr->uHItemPadding;
1143 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1145 /* Set the leftmost position of the tab. */
1146 infoPtr->items[curItem].rect.left = curItemLeftPos;
1148 if ((lStyle & TCS_FIXEDWIDTH) || !infoPtr->items[curItem].pszText)
1150 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1151 max(infoPtr->tabWidth, icon_width);
1157 /* Calculate how wide the tab is depending on the text it contains */
1158 GetTextExtentPoint32W(hdc, infoPtr->items[curItem].pszText,
1159 lstrlenW(infoPtr->items[curItem].pszText), &size);
1161 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1162 size.cx + icon_width +
1163 num * infoPtr->uHItemPadding;
1164 TRACE("for <%s>, l,r=%ld,%ld, num=%d\n",
1165 debugstr_w(infoPtr->items[curItem].pszText),
1166 infoPtr->items[curItem].rect.left,
1167 infoPtr->items[curItem].rect.right,
1172 * Check if this is a multiline tab control and if so
1173 * check to see if we should wrap the tabs
1175 * Wrap all these tabs. We will arrange them evenly later.
1179 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1180 (infoPtr->items[curItem].rect.right >
1181 (clientRect.right - CONTROL_BORDER_SIZEX - DISPLAY_AREA_PADDINGX)))
1183 infoPtr->items[curItem].rect.right -=
1184 infoPtr->items[curItem].rect.left;
1186 infoPtr->items[curItem].rect.left = 0;
1188 TRACE("wrapping <%s>, l,r=%ld,%ld\n",
1189 debugstr_w(infoPtr->items[curItem].pszText),
1190 infoPtr->items[curItem].rect.left,
1191 infoPtr->items[curItem].rect.right);
1194 infoPtr->items[curItem].rect.bottom = 0;
1195 infoPtr->items[curItem].rect.top = curItemRowCount - 1;
1197 TRACE("TextSize: %li\n", size.cx);
1198 TRACE("Rect: T %li, L %li, B %li, R %li\n",
1199 infoPtr->items[curItem].rect.top,
1200 infoPtr->items[curItem].rect.left,
1201 infoPtr->items[curItem].rect.bottom,
1202 infoPtr->items[curItem].rect.right);
1205 * The leftmost position of the next item is the rightmost position
1208 if (lStyle & TCS_BUTTONS)
1210 curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX;
1211 if (lStyle & TCS_FLATBUTTONS)
1212 curItemLeftPos += FLAT_BTN_SPACINGX;
1215 curItemLeftPos = infoPtr->items[curItem].rect.right;
1218 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1221 * Check if we need a scrolling control.
1223 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1226 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1227 if(!infoPtr->needsScrolling)
1228 infoPtr->leftmostVisible = 0;
1233 * No scrolling in Multiline or Vertical styles.
1235 infoPtr->needsScrolling = FALSE;
1236 infoPtr->leftmostVisible = 0;
1238 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
1240 /* Set the number of rows */
1241 infoPtr->uNumRows = curItemRowCount;
1243 /* Arrange all tabs evenly if style says so */
1244 if (!(lStyle & TCS_RAGGEDRIGHT) && ((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->uNumItem > 0))
1246 INT tabPerRow,remTab,iRow;
1251 * Ok windows tries to even out the rows. place the same
1252 * number of tabs in each row. So lets give that a shot
1255 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1256 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1258 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1259 iItm<infoPtr->uNumItem;
1262 /* normalize the current rect */
1264 /* shift the item to the left side of the clientRect */
1265 infoPtr->items[iItm].rect.right -=
1266 infoPtr->items[iItm].rect.left;
1267 infoPtr->items[iItm].rect.left = 0;
1269 TRACE("r=%ld, cl=%d, cl.r=%ld, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1270 infoPtr->items[iItm].rect.right,
1271 curItemLeftPos, clientRect.right,
1272 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1274 /* if we have reached the maximum number of tabs on this row */
1275 /* move to the next row, reset our current item left position and */
1276 /* the count of items on this row */
1278 if (lStyle & TCS_VERTICAL) {
1279 /* Vert: Add the remaining tabs in the *last* remainder rows */
1280 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1286 /* Horz: Add the remaining tabs in the *first* remainder rows */
1287 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1294 /* shift the item to the right to place it as the next item in this row */
1295 infoPtr->items[iItm].rect.left += curItemLeftPos;
1296 infoPtr->items[iItm].rect.right += curItemLeftPos;
1297 infoPtr->items[iItm].rect.top = iRow;
1298 if (lStyle & TCS_BUTTONS)
1300 curItemLeftPos = infoPtr->items[iItm].rect.right + 1;
1301 if (lStyle & TCS_FLATBUTTONS)
1302 curItemLeftPos += FLAT_BTN_SPACINGX;
1305 curItemLeftPos = infoPtr->items[iItm].rect.right;
1307 TRACE("arranging <%s>, l,r=%ld,%ld, row=%ld\n",
1308 debugstr_w(infoPtr->items[iItm].pszText),
1309 infoPtr->items[iItm].rect.left,
1310 infoPtr->items[iItm].rect.right,
1311 infoPtr->items[iItm].rect.top);
1318 INT widthDiff, iIndexStart=0, iIndexEnd=0;
1322 while(iIndexStart < infoPtr->uNumItem)
1325 * find the indexs of the row
1327 /* find the first item on the next row */
1328 for (iIndexEnd=iIndexStart;
1329 (iIndexEnd < infoPtr->uNumItem) &&
1330 (infoPtr->items[iIndexEnd].rect.top ==
1331 infoPtr->items[iIndexStart].rect.top) ;
1333 /* intentionally blank */;
1336 * we need to justify these tabs so they fill the whole given
1340 /* find the amount of space remaining on this row */
1341 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1342 infoPtr->items[iIndexEnd - 1].rect.right;
1344 /* iCount is the number of tab items on this row */
1345 iCount = iIndexEnd - iIndexStart;
1349 remainder = widthDiff % iCount;
1350 widthDiff = widthDiff / iCount;
1351 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1352 for (iIndex=iIndexStart, iCount=0; iIndex < iIndexEnd; iIndex++, iCount++)
1354 infoPtr->items[iIndex].rect.left += iCount * widthDiff;
1355 infoPtr->items[iIndex].rect.right += (iCount + 1) * widthDiff;
1357 TRACE("adjusting 1 <%s>, l,r=%ld,%ld\n",
1358 debugstr_w(infoPtr->items[iIndex].pszText),
1359 infoPtr->items[iIndex].rect.left,
1360 infoPtr->items[iIndex].rect.right);
1363 infoPtr->items[iIndex - 1].rect.right += remainder;
1365 else /* we have only one item on this row, make it take up the entire row */
1367 infoPtr->items[iIndexStart].rect.left = clientRect.left;
1368 infoPtr->items[iIndexStart].rect.right = clientRect.right - 4;
1370 TRACE("adjusting 2 <%s>, l,r=%ld,%ld\n",
1371 debugstr_w(infoPtr->items[iIndexStart].pszText),
1372 infoPtr->items[iIndexStart].rect.left,
1373 infoPtr->items[iIndexStart].rect.right);
1378 iIndexStart = iIndexEnd;
1383 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1384 if(lStyle & TCS_VERTICAL)
1387 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1389 rcItem = &(infoPtr->items[iIndex].rect);
1391 rcOriginal = *rcItem;
1393 /* this is rotating the items by 90 degrees clockwise around the center of the control */
1394 rcItem->top = (rcOriginal.left - clientRect.left);
1395 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1396 rcItem->left = rcOriginal.top;
1397 rcItem->right = rcOriginal.bottom;
1401 TAB_EnsureSelectionVisible(hwnd,infoPtr);
1402 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1405 SelectObject (hdc, hOldFont);
1406 ReleaseDC (hwnd, hdc);
1411 TAB_EraseTabInterior
1419 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1420 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1421 HBRUSH hbr = CreateSolidBrush (comctl32_color.clrBtnFace);
1422 BOOL deleteBrush = TRUE;
1423 RECT rTemp = *drawRect;
1425 InflateRect(&rTemp, -2, -2);
1426 if (lStyle & TCS_BUTTONS)
1428 if (iItem == infoPtr->iSelected)
1430 /* Background color */
1431 if (!(lStyle & TCS_OWNERDRAWFIXED))
1434 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1436 SetTextColor(hdc, comctl32_color.clr3dFace);
1437 SetBkColor(hdc, comctl32_color.clr3dHilight);
1439 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1440 * we better use 0x55aa bitmap brush to make scrollbar's background
1441 * look different from the window background.
1443 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1444 hbr = COMCTL32_hPattern55AABrush;
1446 deleteBrush = FALSE;
1448 FillRect(hdc, &rTemp, hbr);
1450 else /* ! selected */
1452 if (lStyle & TCS_FLATBUTTONS)
1454 FillRect(hdc, drawRect, hbr);
1455 if (iItem == infoPtr->iHotTracked)
1456 DrawEdge(hdc, drawRect, EDGE_RAISED, BF_SOFT|BF_RECT);
1459 FillRect(hdc, &rTemp, hbr);
1463 else /* !TCS_BUTTONS */
1465 FillRect(hdc, &rTemp, hbr);
1469 if (deleteBrush) DeleteObject(hbr);
1472 /******************************************************************************
1473 * TAB_DrawItemInterior
1475 * This method is used to draw the interior (text and icon) of a single tab
1476 * into the tab control.
1479 TAB_DrawItemInterior
1487 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1488 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1497 /* if (drawRect == NULL) */
1504 * Get the rectangle for the item.
1506 isVisible = TAB_InternalGetItemRect(hwnd, infoPtr, iItem, &itemRect, &selectedRect);
1511 * Make sure drawRect points to something valid; simplifies code.
1513 drawRect = &localRect;
1516 * This logic copied from the part of TAB_DrawItem which draws
1517 * the tab background. It's important to keep it in sync. I
1518 * would have liked to avoid code duplication, but couldn't figure
1519 * out how without making spaghetti of TAB_DrawItem.
1521 if (iItem == infoPtr->iSelected)
1522 *drawRect = selectedRect;
1524 *drawRect = itemRect;
1526 if (lStyle & TCS_BUTTONS)
1528 if (iItem == infoPtr->iSelected)
1530 drawRect->left += 4;
1532 drawRect->right -= 4;
1533 drawRect->bottom -= 1;
1537 drawRect->left += 2;
1539 drawRect->right -= 2;
1540 drawRect->bottom -= 2;
1545 if ((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1547 if (iItem != infoPtr->iSelected)
1549 drawRect->left += 2;
1551 drawRect->bottom -= 2;
1554 else if (lStyle & TCS_VERTICAL)
1556 if (iItem == infoPtr->iSelected)
1558 drawRect->right += 1;
1563 drawRect->right -= 2;
1564 drawRect->bottom -= 2;
1567 else if (lStyle & TCS_BOTTOM)
1569 if (iItem == infoPtr->iSelected)
1575 InflateRect(drawRect, -2, -2);
1576 drawRect->bottom += 2;
1581 if (iItem == infoPtr->iSelected)
1583 drawRect->bottom += 3;
1587 drawRect->bottom -= (infoPtr->items[iItem].rect.top != infoPtr->uNumRows-1)? 2:2;
1588 InflateRect(drawRect, -2, 0);
1593 TRACE("drawRect=(%ld,%ld)-(%ld,%ld)\n",
1594 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom);
1596 /* Clear interior */
1597 TAB_EraseTabInterior (hwnd, hdc, iItem, drawRect);
1599 /* Draw the focus rectangle */
1600 if (!(lStyle & TCS_FOCUSNEVER) &&
1601 (GetFocus() == hwnd) &&
1602 (iItem == infoPtr->uFocus) )
1604 RECT rFocus = *drawRect;
1605 InflateRect(&rFocus, -3, -3);
1606 if (lStyle & TCS_BOTTOM && !(lStyle & TCS_VERTICAL))
1608 if (lStyle & TCS_BUTTONS)
1614 DrawFocusRect(hdc, &rFocus);
1620 htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1621 holdPen = SelectObject(hdc, htextPen);
1622 hOldFont = SelectObject(hdc, infoPtr->hFont);
1625 * Setup for text output
1627 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1628 SetTextColor(hdc, (((iItem == infoPtr->iHotTracked) && !(lStyle & TCS_FLATBUTTONS)) |
1629 (infoPtr->items[iItem].dwState & TCIS_HIGHLIGHTED)) ?
1630 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1633 * if owner draw, tell the owner to draw
1635 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd))
1641 drawRect->right -= 1;
1642 if ( iItem == infoPtr->iSelected )
1644 drawRect->right -= 1;
1645 drawRect->left += 1;
1649 * get the control id
1651 id = (UINT)GetWindowLongPtrW( hwnd, GWLP_ID );
1654 * put together the DRAWITEMSTRUCT
1656 dis.CtlType = ODT_TAB;
1659 dis.itemAction = ODA_DRAWENTIRE;
1661 if ( iItem == infoPtr->iSelected )
1662 dis.itemState |= ODS_SELECTED;
1663 if (infoPtr->uFocus == iItem)
1664 dis.itemState |= ODS_FOCUS;
1665 dis.hwndItem = hwnd; /* */
1667 CopyRect(&dis.rcItem,drawRect);
1669 memcpy( &dis.itemData, infoPtr->items[iItem].extra, min(sizeof(dis.itemData),infoPtr->cbInfo) );
1672 * send the draw message
1674 SendMessageA( infoPtr->hwndNotify, WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1681 /* used to center the icon and text in the tab */
1683 INT center_offset_h, center_offset_v;
1685 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1686 rcImage = *drawRect;
1690 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1692 /* get the rectangle that the text fits in */
1693 if (infoPtr->items[iItem].pszText)
1695 DrawTextW(hdc, infoPtr->items[iItem].pszText, -1,
1696 &rcText, DT_CALCRECT);
1699 * If not owner draw, then do the drawing ourselves.
1703 if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE))
1708 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1710 if(lStyle & TCS_VERTICAL)
1712 center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1713 center_offset_v = ((drawRect->right - drawRect->left) - (cx + infoPtr->uVItemPadding)) / 2;
1717 center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1718 center_offset_v = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uVItemPadding)) / 2;
1721 if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1722 center_offset_h = infoPtr->uHItemPadding;
1724 if (center_offset_h < 2)
1725 center_offset_h = 2;
1727 if (center_offset_v < 0)
1728 center_offset_v = 0;
1730 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1731 debugstr_w(infoPtr->items[iItem].pszText), center_offset_h, center_offset_v,
1732 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1733 (rcText.right-rcText.left));
1735 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1737 rcImage.top = drawRect->top + center_offset_h;
1738 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1739 /* right side of the tab, but the image still uses the left as its x position */
1740 /* this keeps the image always drawn off of the same side of the tab */
1741 rcImage.left = drawRect->right - cx - center_offset_v;
1742 drawRect->top += cy + infoPtr->uHItemPadding;
1744 else if(lStyle & TCS_VERTICAL)
1746 rcImage.top = drawRect->bottom - cy - center_offset_h;
1747 rcImage.left = drawRect->left + center_offset_v;
1748 drawRect->bottom -= cy + infoPtr->uHItemPadding;
1750 else /* normal style, whether TCS_BOTTOM or not */
1752 rcImage.left = drawRect->left + center_offset_h;
1753 rcImage.top = drawRect->top + center_offset_v;
1754 drawRect->left += cx + infoPtr->uHItemPadding;
1757 TRACE("drawing image=%d, left=%ld, top=%ld\n",
1758 infoPtr->items[iItem].iImage, rcImage.left, rcImage.top-1);
1762 infoPtr->items[iItem].iImage,
1770 /* Now position text */
1771 if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT)
1772 center_offset_h = infoPtr->uHItemPadding;
1774 if(lStyle & TCS_VERTICAL)
1775 center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1777 center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1779 if(lStyle & TCS_VERTICAL)
1781 if(lStyle & TCS_BOTTOM)
1782 drawRect->top+=center_offset_h;
1784 drawRect->bottom-=center_offset_h;
1786 center_offset_v = ((drawRect->right - drawRect->left) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1790 drawRect->left += center_offset_h;
1791 center_offset_v = ((drawRect->bottom - drawRect->top) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1794 if (center_offset_v < 0)
1795 center_offset_v = 0;
1797 if(lStyle & TCS_VERTICAL)
1798 drawRect->left += center_offset_v;
1800 drawRect->top += center_offset_v;
1803 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1807 INT nEscapement = 900;
1808 INT nOrientation = 900;
1810 if(lStyle & TCS_BOTTOM)
1813 nOrientation = -900;
1816 /* to get a font with the escapement and orientation we are looking for, we need to */
1817 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1818 if (!GetObjectA((infoPtr->hFont) ?
1819 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1820 sizeof(LOGFONTA),&logfont))
1824 lstrcpyA(logfont.lfFaceName, "Arial");
1825 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1827 logfont.lfWeight = FW_NORMAL;
1828 logfont.lfItalic = 0;
1829 logfont.lfUnderline = 0;
1830 logfont.lfStrikeOut = 0;
1833 logfont.lfEscapement = nEscapement;
1834 logfont.lfOrientation = nOrientation;
1835 hFont = CreateFontIndirectA(&logfont);
1836 SelectObject(hdc, hFont);
1838 if (infoPtr->items[iItem].pszText)
1841 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1842 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1845 infoPtr->items[iItem].pszText,
1846 lstrlenW(infoPtr->items[iItem].pszText),
1850 DeleteObject(hFont);
1854 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1855 debugstr_w(infoPtr->items[iItem].pszText), center_offset_h, center_offset_v,
1856 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1857 (rcText.right-rcText.left));
1858 if (infoPtr->items[iItem].pszText)
1863 infoPtr->items[iItem].pszText,
1864 lstrlenW(infoPtr->items[iItem].pszText),
1866 DT_LEFT | DT_SINGLELINE
1871 *drawRect = rcTemp; /* restore drawRect */
1877 SelectObject(hdc, hOldFont);
1878 SetBkMode(hdc, oldBkMode);
1879 SelectObject(hdc, holdPen);
1880 DeleteObject( htextPen );
1883 /******************************************************************************
1886 * This method is used to draw a single tab into the tab control.
1888 static void TAB_DrawItem(
1893 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1894 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1898 RECT r, fillRect, r1;
1901 COLORREF bkgnd, corner;
1904 * Get the rectangle for the item.
1906 isVisible = TAB_InternalGetItemRect(hwnd,
1916 /* Clip UpDown control to not draw over it */
1917 if (infoPtr->needsScrolling)
1919 GetWindowRect(hwnd, &rC);
1920 GetWindowRect(infoPtr->hwndUpDown, &rUD);
1921 ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
1924 /* If you need to see what the control is doing,
1925 * then override these variables. They will change what
1926 * fill colors are used for filling the tabs, and the
1927 * corners when drawing the edge.
1929 bkgnd = comctl32_color.clrBtnFace;
1930 corner = comctl32_color.clrBtnFace;
1932 if (lStyle & TCS_BUTTONS)
1934 /* Get item rectangle */
1937 /* Separators between flat buttons */
1938 if (lStyle & TCS_FLATBUTTONS)
1941 r1.right += (FLAT_BTN_SPACINGX -2);
1942 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1945 if (iItem == infoPtr->iSelected)
1947 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1949 OffsetRect(&r, 1, 1);
1951 else /* ! selected */
1953 if (!(lStyle & TCS_FLATBUTTONS))
1954 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1957 else /* !TCS_BUTTONS */
1959 /* We draw a rectangle of different sizes depending on the selection
1961 if (iItem == infoPtr->iSelected) {
1963 GetClientRect (hwnd, &rect);
1964 clRight = rect.right;
1965 clBottom = rect.bottom;
1972 * Erase the background. (Delay it but setup rectangle.)
1973 * This is necessary when drawing the selected item since it is larger
1974 * than the others, it might overlap with stuff already drawn by the
1979 if(lStyle & TCS_VERTICAL)
1981 /* These are for adjusting the drawing of a Selected tab */
1982 /* The initial values are for the normal case of non-Selected */
1983 int ZZ = 1; /* Do not strech if selected */
1984 if (iItem == infoPtr->iSelected) {
1987 /* if leftmost draw the line longer */
1988 if(selectedRect.top == 0)
1989 fillRect.top += CONTROL_BORDER_SIZEY;
1990 /* if rightmost draw the line longer */
1991 if(selectedRect.bottom == clBottom)
1992 fillRect.bottom -= CONTROL_BORDER_SIZEY;
1995 if (lStyle & TCS_BOTTOM)
1997 /* Adjust both rectangles to match native */
2000 TRACE("<right> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2002 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2003 r.left,r.top,r.right,r.bottom);
2005 /* Clear interior */
2006 SetBkColor(hdc, bkgnd);
2007 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2009 /* Draw rectangular edge around tab */
2010 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
2012 /* Now erase the top corner and draw diagonal edge */
2013 SetBkColor(hdc, corner);
2014 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2017 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2018 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2020 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2022 /* Now erase the bottom corner and draw diagonal edge */
2023 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2024 r1.bottom = r.bottom;
2026 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2027 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2029 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2031 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
2035 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
2041 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2043 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2044 r.left,r.top,r.right,r.bottom);
2046 /* Clear interior */
2047 SetBkColor(hdc, bkgnd);
2048 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2050 /* Draw rectangular edge around tab */
2051 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2053 /* Now erase the top corner and draw diagonal edge */
2054 SetBkColor(hdc, corner);
2057 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2058 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2059 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2061 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2063 /* Now erase the bottom corner and draw diagonal edge */
2065 r1.bottom = r.bottom;
2066 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2067 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2068 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2070 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2073 else /* ! TCS_VERTICAL */
2075 /* These are for adjusting the drawing of a Selected tab */
2076 /* The initial values are for the normal case of non-Selected */
2077 if (iItem == infoPtr->iSelected) {
2078 /* if leftmost draw the line longer */
2079 if(selectedRect.left == 0)
2080 fillRect.left += CONTROL_BORDER_SIZEX;
2081 /* if rightmost draw the line longer */
2082 if(selectedRect.right == clRight)
2083 fillRect.right -= CONTROL_BORDER_SIZEX;
2086 if (lStyle & TCS_BOTTOM)
2088 /* Adjust both rectangles for topmost row */
2089 if (infoPtr->items[iItem].rect.top == infoPtr->uNumRows-1)
2095 TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2097 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2098 r.left,r.top,r.right,r.bottom);
2100 /* Clear interior */
2101 SetBkColor(hdc, bkgnd);
2102 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2104 /* Draw rectangular edge around tab */
2105 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2107 /* Now erase the righthand corner and draw diagonal edge */
2108 SetBkColor(hdc, corner);
2109 r1.left = r.right - ROUND_CORNER_SIZE;
2110 r1.bottom = r.bottom;
2112 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2113 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2115 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2117 /* Now erase the lefthand corner and draw diagonal edge */
2119 r1.bottom = r.bottom;
2120 r1.right = r1.left + ROUND_CORNER_SIZE;
2121 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2122 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2124 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2126 if (iItem == infoPtr->iSelected)
2130 if (selectedRect.left == 0)
2135 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2142 /* Adjust both rectangles for bottommost row */
2143 if (infoPtr->items[iItem].rect.top == infoPtr->uNumRows-1)
2145 fillRect.bottom += 3;
2149 TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2151 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2152 r.left,r.top,r.right,r.bottom);
2154 /* Clear interior */
2155 SetBkColor(hdc, bkgnd);
2156 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2158 /* Draw rectangular edge around tab */
2159 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2161 /* Now erase the righthand corner and draw diagonal edge */
2162 SetBkColor(hdc, corner);
2163 r1.left = r.right - ROUND_CORNER_SIZE;
2166 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2167 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2169 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2171 /* Now erase the lefthand corner and draw diagonal edge */
2174 r1.right = r1.left + ROUND_CORNER_SIZE;
2175 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2176 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2178 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2183 TAB_DumpItemInternal(infoPtr, iItem);
2185 /* This modifies r to be the text rectangle. */
2186 TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
2190 /******************************************************************************
2193 * This method is used to draw the raised border around the tab control
2196 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
2198 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2200 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2202 GetClientRect (hwnd, &rect);
2205 * Adjust for the style
2208 if (infoPtr->uNumItem)
2210 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2211 rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2212 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2213 rect.right -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2214 else if(lStyle & TCS_VERTICAL)
2215 rect.left += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2216 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2217 rect.top += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2220 TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2221 rect.left, rect.top, rect.right, rect.bottom);
2223 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2226 /******************************************************************************
2229 * This method repaints the tab control..
2231 static void TAB_Refresh (HWND hwnd, HDC hdc)
2233 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2237 if (!infoPtr->DoRedraw)
2240 hOldFont = SelectObject (hdc, infoPtr->hFont);
2242 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
2244 for (i = 0; i < infoPtr->uNumItem; i++)
2245 TAB_DrawItem (hwnd, hdc, i);
2249 /* Draw all the non selected item first */
2250 for (i = 0; i < infoPtr->uNumItem; i++)
2252 if (i != infoPtr->iSelected)
2253 TAB_DrawItem (hwnd, hdc, i);
2256 /* Now, draw the border, draw it before the selected item
2257 * since the selected item overwrites part of the border. */
2258 TAB_DrawBorder (hwnd, hdc);
2260 /* Then, draw the selected item */
2261 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
2263 /* If we haven't set the current focus yet, set it now.
2264 * Only happens when we first paint the tab controls */
2265 if (infoPtr->uFocus == -1)
2266 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
2269 SelectObject (hdc, hOldFont);
2273 TAB_GetRowCount (HWND hwnd )
2275 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2277 return infoPtr->uNumRows;
2281 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
2283 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2285 infoPtr->DoRedraw=(BOOL) wParam;
2289 static LRESULT TAB_EraseBackground(
2296 HBRUSH brush = CreateSolidBrush(comctl32_color.clrBtnFace);
2298 hdc = givenDC ? givenDC : GetDC(hwnd);
2300 GetClientRect(hwnd, &clientRect);
2302 FillRect(hdc, &clientRect, brush);
2305 ReleaseDC(hwnd, hdc);
2307 DeleteObject(brush);
2312 /******************************************************************************
2313 * TAB_EnsureSelectionVisible
2315 * This method will make sure that the current selection is completely
2316 * visible by scrolling until it is.
2318 static void TAB_EnsureSelectionVisible(
2322 INT iSelected = infoPtr->iSelected;
2323 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2324 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2326 /* set the items row to the bottommost row or topmost row depending on
2328 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2333 if(lStyle & TCS_VERTICAL)
2334 newselected = infoPtr->items[iSelected].rect.left;
2336 newselected = infoPtr->items[iSelected].rect.top;
2338 /* the target row is always (number of rows - 1)
2339 as row 0 is furthest from the clientRect */
2340 iTargetRow = infoPtr->uNumRows - 1;
2342 if (newselected != iTargetRow)
2345 if(lStyle & TCS_VERTICAL)
2347 for (i=0; i < infoPtr->uNumItem; i++)
2349 /* move everything in the row of the selected item to the iTargetRow */
2350 if (infoPtr->items[i].rect.left == newselected )
2351 infoPtr->items[i].rect.left = iTargetRow;
2354 if (infoPtr->items[i].rect.left > newselected)
2355 infoPtr->items[i].rect.left-=1;
2361 for (i=0; i < infoPtr->uNumItem; i++)
2363 if (infoPtr->items[i].rect.top == newselected )
2364 infoPtr->items[i].rect.top = iTargetRow;
2367 if (infoPtr->items[i].rect.top > newselected)
2368 infoPtr->items[i].rect.top-=1;
2372 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2377 * Do the trivial cases first.
2379 if ( (!infoPtr->needsScrolling) ||
2380 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2383 if (infoPtr->leftmostVisible >= iSelected)
2385 infoPtr->leftmostVisible = iSelected;
2393 /* Calculate the part of the client area that is visible */
2394 GetClientRect(hwnd, &r);
2397 GetClientRect(infoPtr->hwndUpDown, &r);
2400 if ((infoPtr->items[iSelected].rect.right -
2401 infoPtr->items[iSelected].rect.left) >= width )
2403 /* Special case: width of selected item is greater than visible
2406 infoPtr->leftmostVisible = iSelected;
2410 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2412 if ((infoPtr->items[iSelected].rect.right -
2413 infoPtr->items[i].rect.left) < width)
2416 infoPtr->leftmostVisible = i;
2420 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2421 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2423 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2424 MAKELONG(infoPtr->leftmostVisible, 0));
2427 /******************************************************************************
2428 * TAB_InvalidateTabArea
2430 * This method will invalidate the portion of the control that contains the
2431 * tabs. It is called when the state of the control changes and needs
2434 static void TAB_InvalidateTabArea(
2438 RECT clientRect, rInvalidate, rAdjClient;
2439 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2440 INT lastRow = infoPtr->uNumRows - 1;
2443 if (lastRow < 0) return;
2445 GetClientRect(hwnd, &clientRect);
2446 rInvalidate = clientRect;
2447 rAdjClient = clientRect;
2449 TAB_AdjustRect(hwnd, 0, &rAdjClient);
2451 TAB_InternalGetItemRect(hwnd, infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2452 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2454 rInvalidate.left = rAdjClient.right;
2455 if (infoPtr->uNumRows == 1)
2456 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2458 else if(lStyle & TCS_VERTICAL)
2460 rInvalidate.right = rAdjClient.left;
2461 if (infoPtr->uNumRows == 1)
2462 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2464 else if (lStyle & TCS_BOTTOM)
2466 rInvalidate.top = rAdjClient.bottom;
2467 if (infoPtr->uNumRows == 1)
2468 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2472 rInvalidate.bottom = rAdjClient.top;
2473 if (infoPtr->uNumRows == 1)
2474 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2477 /* Punch out the updown control */
2478 if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2480 GetClientRect(infoPtr->hwndUpDown, &r);
2481 if (rInvalidate.right > clientRect.right - r.left)
2482 rInvalidate.right = rInvalidate.right - (r.right - r.left);
2484 rInvalidate.right = clientRect.right - r.left;
2487 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2488 rInvalidate.left, rInvalidate.top,
2489 rInvalidate.right, rInvalidate.bottom);
2491 InvalidateRect(hwnd, &rInvalidate, TRUE);
2495 TAB_Paint (HWND hwnd, WPARAM wParam)
2502 hdc = BeginPaint (hwnd, &ps);
2503 TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2505 ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2508 TAB_EraseBackground (hwnd, hdc);
2514 TAB_Refresh (hwnd, hdc);
2517 EndPaint (hwnd, &ps);
2523 TAB_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2525 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2530 GetClientRect (hwnd, &rect);
2531 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2532 rect.top, rect.left, rect.bottom, rect.right);
2534 pti = (TCITEMA *)lParam;
2535 iItem = (INT)wParam;
2537 if (iItem < 0) return -1;
2538 if (iItem > infoPtr->uNumItem)
2539 iItem = infoPtr->uNumItem;
2541 TAB_DumpItemExternalA(pti, iItem);
2544 if (infoPtr->uNumItem == 0) {
2545 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2546 infoPtr->uNumItem++;
2547 infoPtr->iSelected = 0;
2550 TAB_ITEM *oldItems = infoPtr->items;
2552 infoPtr->uNumItem++;
2553 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2555 /* pre insert copy */
2557 memcpy (&infoPtr->items[0], &oldItems[0],
2558 iItem * TAB_ITEM_SIZE(infoPtr));
2561 /* post insert copy */
2562 if (iItem < infoPtr->uNumItem - 1) {
2563 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2564 (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2568 if (iItem <= infoPtr->iSelected)
2569 infoPtr->iSelected++;
2574 infoPtr->items[iItem].mask = pti->mask;
2575 if (pti->mask & TCIF_TEXT)
2576 Str_SetPtrAtoW (&infoPtr->items[iItem].pszText, pti->pszText);
2578 if (pti->mask & TCIF_IMAGE)
2579 infoPtr->items[iItem].iImage = pti->iImage;
2581 if (pti->mask & TCIF_PARAM)
2582 memcpy(infoPtr->items[iItem].extra, &pti->lParam, infoPtr->cbInfo);
2584 memset(infoPtr->items[iItem].extra, 0, infoPtr->cbInfo);
2586 TAB_SetItemBounds(hwnd);
2587 if (infoPtr->uNumItem > 1)
2588 TAB_InvalidateTabArea(hwnd, infoPtr);
2590 InvalidateRect(hwnd, NULL, TRUE);
2592 TRACE("[%p]: added item %d %s\n",
2593 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2600 TAB_InsertItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2602 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2607 GetClientRect (hwnd, &rect);
2608 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2609 rect.top, rect.left, rect.bottom, rect.right);
2611 pti = (TCITEMW *)lParam;
2612 iItem = (INT)wParam;
2614 if (iItem < 0) return -1;
2615 if (iItem > infoPtr->uNumItem)
2616 iItem = infoPtr->uNumItem;
2618 TAB_DumpItemExternalW(pti, iItem);
2620 if (infoPtr->uNumItem == 0) {
2621 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2622 infoPtr->uNumItem++;
2623 infoPtr->iSelected = 0;
2626 TAB_ITEM *oldItems = infoPtr->items;
2628 infoPtr->uNumItem++;
2629 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2631 /* pre insert copy */
2633 memcpy (&infoPtr->items[0], &oldItems[0],
2634 iItem * TAB_ITEM_SIZE(infoPtr));
2637 /* post insert copy */
2638 if (iItem < infoPtr->uNumItem - 1) {
2639 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2640 (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2644 if (iItem <= infoPtr->iSelected)
2645 infoPtr->iSelected++;
2650 infoPtr->items[iItem].mask = pti->mask;
2651 if (pti->mask & TCIF_TEXT)
2652 Str_SetPtrW (&infoPtr->items[iItem].pszText, pti->pszText);
2654 if (pti->mask & TCIF_IMAGE)
2655 infoPtr->items[iItem].iImage = pti->iImage;
2657 if (pti->mask & TCIF_PARAM)
2658 memcpy(infoPtr->items[iItem].extra, &pti->lParam, infoPtr->cbInfo);
2660 memset(infoPtr->items[iItem].extra, 0, infoPtr->cbInfo);
2662 TAB_SetItemBounds(hwnd);
2663 if (infoPtr->uNumItem > 1)
2664 TAB_InvalidateTabArea(hwnd, infoPtr);
2666 InvalidateRect(hwnd, NULL, TRUE);
2668 TRACE("[%p]: added item %d %s\n",
2669 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2676 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2678 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2679 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2681 BOOL bNeedPaint = FALSE;
2683 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2685 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2686 if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2688 infoPtr->tabWidth = max((INT)LOWORD(lParam), infoPtr->tabMinWidth);
2692 if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2694 if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2695 infoPtr->tabHeight = (INT)HIWORD(lParam);
2699 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2700 HIWORD(lResult), LOWORD(lResult),
2701 infoPtr->tabHeight, infoPtr->tabWidth);
2705 TAB_SetItemBounds(hwnd);
2706 RedrawWindow(hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2713 TAB_SetMinTabWidth (HWND hwnd, LPARAM lParam)
2715 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2716 INT cx = (INT)lParam;
2720 oldcx = infoPtr->tabMinWidth;
2721 infoPtr->tabMinWidth = (cx==-1)?DEFAULT_TAB_WIDTH:cx;
2729 TAB_HighlightItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2731 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2732 INT iItem = (INT)wParam;
2733 BOOL fHighlight = (BOOL)LOWORD(lParam);
2735 if ((infoPtr) && (iItem>=0) && (iItem<infoPtr->uNumItem)) {
2737 infoPtr->items[iItem].dwState |= TCIS_HIGHLIGHTED;
2739 infoPtr->items[iItem].dwState &= ~TCIS_HIGHLIGHTED;
2747 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2749 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2754 iItem = (INT)wParam;
2755 tabItem = (LPTCITEMA)lParam;
2757 TRACE("%d %p\n", iItem, tabItem);
2758 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2760 TAB_DumpItemExternalA(tabItem, iItem);
2762 wineItem = &infoPtr->items[iItem];
2764 if (tabItem->mask & TCIF_IMAGE)
2765 wineItem->iImage = tabItem->iImage;
2767 if (tabItem->mask & TCIF_PARAM)
2768 memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2770 if (tabItem->mask & TCIF_RTLREADING)
2771 FIXME("TCIF_RTLREADING\n");
2773 if (tabItem->mask & TCIF_STATE)
2774 wineItem->dwState = tabItem->dwState;
2776 if (tabItem->mask & TCIF_TEXT)
2777 Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2779 /* Update and repaint tabs */
2780 TAB_SetItemBounds(hwnd);
2781 TAB_InvalidateTabArea(hwnd,infoPtr);
2788 TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2790 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2795 iItem = (INT)wParam;
2796 tabItem = (LPTCITEMW)lParam;
2798 TRACE("%d %p\n", iItem, tabItem);
2799 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2801 TAB_DumpItemExternalW(tabItem, iItem);
2803 wineItem = &infoPtr->items[iItem];
2805 if (tabItem->mask & TCIF_IMAGE)
2806 wineItem->iImage = tabItem->iImage;
2808 if (tabItem->mask & TCIF_PARAM)
2809 memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2811 if (tabItem->mask & TCIF_RTLREADING)
2812 FIXME("TCIF_RTLREADING\n");
2814 if (tabItem->mask & TCIF_STATE)
2815 wineItem->dwState = tabItem->dwState;
2817 if (tabItem->mask & TCIF_TEXT)
2818 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2820 /* Update and repaint tabs */
2821 TAB_SetItemBounds(hwnd);
2822 TAB_InvalidateTabArea(hwnd,infoPtr);
2829 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2831 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2833 return infoPtr->uNumItem;
2838 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2840 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2845 iItem = (INT)wParam;
2846 tabItem = (LPTCITEMA)lParam;
2848 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2851 wineItem = &infoPtr->items[iItem];
2853 if (tabItem->mask & TCIF_IMAGE)
2854 tabItem->iImage = wineItem->iImage;
2856 if (tabItem->mask & TCIF_PARAM)
2857 memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2859 if (tabItem->mask & TCIF_RTLREADING)
2860 FIXME("TCIF_RTLREADING\n");
2862 if (tabItem->mask & TCIF_STATE)
2863 tabItem->dwState = wineItem->dwState;
2865 if (tabItem->mask & TCIF_TEXT)
2866 Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2868 TAB_DumpItemExternalA(tabItem, iItem);
2875 TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2877 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2882 iItem = (INT)wParam;
2883 tabItem = (LPTCITEMW)lParam;
2885 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2888 wineItem=& infoPtr->items[iItem];
2890 if (tabItem->mask & TCIF_IMAGE)
2891 tabItem->iImage = wineItem->iImage;
2893 if (tabItem->mask & TCIF_PARAM)
2894 memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2896 if (tabItem->mask & TCIF_RTLREADING)
2897 FIXME("TCIF_RTLREADING\n");
2899 if (tabItem->mask & TCIF_STATE)
2900 tabItem->dwState = wineItem->dwState;
2902 if (tabItem->mask & TCIF_TEXT)
2903 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2905 TAB_DumpItemExternalW(tabItem, iItem);
2912 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2914 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2915 INT iItem = (INT) wParam;
2916 BOOL bResult = FALSE;
2918 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2920 TAB_ITEM *oldItems = infoPtr->items;
2922 TAB_InvalidateTabArea(hwnd, infoPtr);
2924 infoPtr->uNumItem--;
2925 infoPtr->items = Alloc(TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2928 memcpy(&infoPtr->items[0], &oldItems[0], iItem * TAB_ITEM_SIZE(infoPtr));
2930 if (iItem < infoPtr->uNumItem)
2931 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
2932 (infoPtr->uNumItem - iItem) * TAB_ITEM_SIZE(infoPtr));
2936 /* Readjust the selected index */
2937 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2938 infoPtr->iSelected--;
2940 if (iItem < infoPtr->iSelected)
2941 infoPtr->iSelected--;
2943 if (infoPtr->uNumItem == 0)
2944 infoPtr->iSelected = -1;
2946 /* Reposition and repaint tabs */
2947 TAB_SetItemBounds(hwnd);
2956 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2958 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2960 TAB_InvalidateTabArea(hwnd,infoPtr);
2962 Free (infoPtr->items);
2963 infoPtr->uNumItem = 0;
2964 infoPtr->iSelected = -1;
2965 if (infoPtr->iHotTracked >= 0)
2966 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2967 infoPtr->iHotTracked = -1;
2969 TAB_SetItemBounds(hwnd);
2975 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2977 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2980 return (LRESULT)infoPtr->hFont;
2984 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2987 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2989 TRACE("%x %lx\n",wParam, lParam);
2991 infoPtr->hFont = (HFONT)wParam;
2993 TAB_SetItemBounds(hwnd);
2995 TAB_InvalidateTabArea(hwnd, infoPtr);
3002 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
3004 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3007 return (LRESULT)infoPtr->himl;
3011 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
3013 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3014 HIMAGELIST himlPrev;
3017 himlPrev = infoPtr->himl;
3018 infoPtr->himl= (HIMAGELIST)lParam;
3019 return (LRESULT)himlPrev;
3023 TAB_GetUnicodeFormat (HWND hwnd)
3025 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
3026 return infoPtr->bUnicode;
3030 TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
3032 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
3033 BOOL bTemp = infoPtr->bUnicode;
3035 infoPtr->bUnicode = (BOOL)wParam;
3041 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
3044 /* I'm not really sure what the following code was meant to do.
3045 This is what it is doing:
3046 When WM_SIZE is sent with SIZE_RESTORED, the control
3047 gets positioned in the top left corner.
3051 UINT uPosFlags,cx,cy;
3055 parent = GetParent (hwnd);
3056 GetClientRect(parent, &parent_rect);
3059 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
3060 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
3062 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
3063 cx, cy, uPosFlags | SWP_NOZORDER);
3065 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
3068 /* Recompute the size/position of the tabs. */
3069 TAB_SetItemBounds (hwnd);
3071 /* Force a repaint of the control. */
3072 InvalidateRect(hwnd, NULL, TRUE);
3079 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
3082 TEXTMETRICA fontMetrics;
3087 infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
3089 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
3091 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
3092 infoPtr->uNumItem = 0;
3093 infoPtr->uNumRows = 0;
3094 infoPtr->uHItemPadding = 6;
3095 infoPtr->uVItemPadding = 3;
3096 infoPtr->uHItemPadding_s = 6;
3097 infoPtr->uVItemPadding_s = 3;
3100 infoPtr->hcurArrow = LoadCursorA (0, (LPSTR)IDC_ARROW);
3101 infoPtr->iSelected = -1;
3102 infoPtr->iHotTracked = -1;
3103 infoPtr->uFocus = -1;
3104 infoPtr->hwndToolTip = 0;
3105 infoPtr->DoRedraw = TRUE;
3106 infoPtr->needsScrolling = FALSE;
3107 infoPtr->hwndUpDown = 0;
3108 infoPtr->leftmostVisible = 0;
3109 infoPtr->fHeightSet = FALSE;
3110 infoPtr->bUnicode = IsWindowUnicode (hwnd);
3111 infoPtr->cbInfo = sizeof(LPARAM);
3113 TRACE("Created tab control, hwnd [%p]\n", hwnd);
3115 /* The tab control always has the WS_CLIPSIBLINGS style. Even
3116 if you don't specify it in CreateWindow. This is necessary in
3117 order for paint to work correctly. This follows windows behaviour. */
3118 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
3119 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
3121 if (dwStyle & TCS_TOOLTIPS) {
3122 /* Create tooltip control */
3123 infoPtr->hwndToolTip =
3124 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
3125 CW_USEDEFAULT, CW_USEDEFAULT,
3126 CW_USEDEFAULT, CW_USEDEFAULT,
3129 /* Send NM_TOOLTIPSCREATED notification */
3130 if (infoPtr->hwndToolTip) {
3131 NMTOOLTIPSCREATED nmttc;
3133 nmttc.hdr.hwndFrom = hwnd;
3134 nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
3135 nmttc.hdr.code = NM_TOOLTIPSCREATED;
3136 nmttc.hwndToolTips = infoPtr->hwndToolTip;
3138 SendMessageA (infoPtr->hwndNotify, WM_NOTIFY,
3139 (WPARAM)GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc);
3144 * We need to get text information so we need a DC and we need to select
3148 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3150 /* Use the system font to determine the initial height of a tab. */
3151 GetTextMetricsA(hdc, &fontMetrics);
3154 * Make sure there is enough space for the letters + growing the
3155 * selected item + extra space for the selected item.
3157 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3158 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
3159 infoPtr->uVItemPadding;
3161 /* Initialize the width of a tab. */
3162 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
3163 infoPtr->tabMinWidth = 0;
3165 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3167 SelectObject (hdc, hOldFont);
3168 ReleaseDC(hwnd, hdc);
3174 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
3176 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3182 if (infoPtr->items) {
3183 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3184 if (infoPtr->items[iItem].pszText)
3185 Free (infoPtr->items[iItem].pszText);
3187 Free (infoPtr->items);
3190 if (infoPtr->hwndToolTip)
3191 DestroyWindow (infoPtr->hwndToolTip);
3193 if (infoPtr->hwndUpDown)
3194 DestroyWindow(infoPtr->hwndUpDown);
3196 if (infoPtr->iHotTracked >= 0)
3197 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
3200 SetWindowLongA(hwnd, 0, 0);
3205 TAB_SetItemExtra (HWND hwnd, WPARAM wParam, LPARAM lParam)
3207 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3208 INT cbInfo = wParam;
3210 if (!infoPtr || cbInfo <= 0)
3213 if (infoPtr->uNumItem)
3215 /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
3219 infoPtr->cbInfo = cbInfo;
3223 static LRESULT WINAPI
3224 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3226 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3228 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3229 if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
3230 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3234 case TCM_GETIMAGELIST:
3235 return TAB_GetImageList (hwnd, wParam, lParam);
3237 case TCM_SETIMAGELIST:
3238 return TAB_SetImageList (hwnd, wParam, lParam);
3240 case TCM_GETITEMCOUNT:
3241 return TAB_GetItemCount (hwnd, wParam, lParam);
3244 return TAB_GetItemA (hwnd, wParam, lParam);
3247 return TAB_GetItemW (hwnd, wParam, lParam);
3250 return TAB_SetItemA (hwnd, wParam, lParam);
3253 return TAB_SetItemW (hwnd, wParam, lParam);
3255 case TCM_DELETEITEM:
3256 return TAB_DeleteItem (hwnd, wParam, lParam);
3258 case TCM_DELETEALLITEMS:
3259 return TAB_DeleteAllItems (hwnd, wParam, lParam);
3261 case TCM_GETITEMRECT:
3262 return TAB_GetItemRect (hwnd, wParam, lParam);
3265 return TAB_GetCurSel (hwnd);
3268 return TAB_HitTest (hwnd, wParam, lParam);
3271 return TAB_SetCurSel (hwnd, wParam);
3273 case TCM_INSERTITEMA:
3274 return TAB_InsertItemA (hwnd, wParam, lParam);
3276 case TCM_INSERTITEMW:
3277 return TAB_InsertItemW (hwnd, wParam, lParam);
3279 case TCM_SETITEMEXTRA:
3280 return TAB_SetItemExtra (hwnd, wParam, lParam);
3282 case TCM_ADJUSTRECT:
3283 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
3285 case TCM_SETITEMSIZE:
3286 return TAB_SetItemSize (hwnd, wParam, lParam);
3288 case TCM_REMOVEIMAGE:
3289 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3292 case TCM_SETPADDING:
3293 return TAB_SetPadding (hwnd, wParam, lParam);
3295 case TCM_GETROWCOUNT:
3296 return TAB_GetRowCount(hwnd);
3298 case TCM_GETUNICODEFORMAT:
3299 return TAB_GetUnicodeFormat (hwnd);
3301 case TCM_SETUNICODEFORMAT:
3302 return TAB_SetUnicodeFormat (hwnd, wParam);
3304 case TCM_HIGHLIGHTITEM:
3305 return TAB_HighlightItem (hwnd, wParam, lParam);
3307 case TCM_GETTOOLTIPS:
3308 return TAB_GetToolTips (hwnd, wParam, lParam);
3310 case TCM_SETTOOLTIPS:
3311 return TAB_SetToolTips (hwnd, wParam, lParam);
3313 case TCM_GETCURFOCUS:
3314 return TAB_GetCurFocus (hwnd);
3316 case TCM_SETCURFOCUS:
3317 return TAB_SetCurFocus (hwnd, wParam);
3319 case TCM_SETMINTABWIDTH:
3320 return TAB_SetMinTabWidth(hwnd, lParam);
3322 case TCM_DESELECTALL:
3323 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3326 case TCM_GETEXTENDEDSTYLE:
3327 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3330 case TCM_SETEXTENDEDSTYLE:
3331 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3335 return TAB_GetFont (hwnd, wParam, lParam);
3338 return TAB_SetFont (hwnd, wParam, lParam);
3341 return TAB_Create (hwnd, wParam, lParam);
3344 return TAB_Destroy (hwnd, wParam, lParam);
3347 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3349 case WM_LBUTTONDOWN:
3350 return TAB_LButtonDown (hwnd, wParam, lParam);
3353 return TAB_LButtonUp (hwnd, wParam, lParam);
3356 return SendMessageA(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3358 case WM_RBUTTONDOWN:
3359 return TAB_RButtonDown (hwnd, wParam, lParam);
3362 return TAB_MouseMove (hwnd, wParam, lParam);
3365 return TAB_EraseBackground (hwnd, (HDC)wParam);
3368 return TAB_Paint (hwnd, wParam);
3371 return TAB_Size (hwnd, wParam, lParam);
3374 return TAB_SetRedraw (hwnd, wParam);
3377 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3379 case WM_STYLECHANGED:
3380 TAB_SetItemBounds (hwnd);
3381 InvalidateRect(hwnd, NULL, TRUE);
3384 case WM_SYSCOLORCHANGE:
3385 COMCTL32_RefreshSysColors();
3390 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
3393 return TAB_KeyUp(hwnd, wParam);
3395 return TAB_NCHitTest(hwnd, lParam);
3398 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
3399 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3400 uMsg, wParam, lParam);
3401 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
3413 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3414 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3415 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
3416 wndClass.cbClsExtra = 0;
3417 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3418 wndClass.hCursor = LoadCursorA (0, (LPSTR)IDC_ARROW);
3419 wndClass.hbrBackground = NULL;
3420 wndClass.lpszClassName = WC_TABCONTROLA;
3422 RegisterClassA (&wndClass);
3427 TAB_Unregister (void)
3429 UnregisterClassA (WC_TABCONTROLA, NULL);