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))
116 /* Since items are variable sized, cannot directly access them */
117 #define TAB_GetItem(info,i) \
118 ((TAB_ITEM*)((LPBYTE)info->items + (i) * TAB_ITEM_SIZE(info)))
120 /******************************************************************************
121 * Hot-tracking timer constants
123 #define TAB_HOTTRACK_TIMER 1
124 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
126 /******************************************************************************
129 static void TAB_Refresh (HWND hwnd, HDC hdc);
130 static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
131 static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
132 static void TAB_DrawItem(HWND hwnd, HDC hdc, INT iItem);
133 static void TAB_DrawItemInterior(HWND hwnd, HDC hdc, INT iItem, RECT* drawRect);
136 TAB_SendSimpleNotify (HWND hwnd, UINT code)
138 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
141 nmhdr.hwndFrom = hwnd;
142 nmhdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
145 return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
146 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
150 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
151 WPARAM wParam, LPARAM lParam)
159 msg.time = GetMessageTime ();
160 msg.pt.x = LOWORD(GetMessagePos ());
161 msg.pt.y = HIWORD(GetMessagePos ());
163 SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
167 TAB_DumpItemExternalA(TCITEMA *pti, UINT iItem)
170 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
171 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
172 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextA=%s\n",
173 iItem, pti->iImage, pti->lParam, debugstr_a(pti->pszText));
179 TAB_DumpItemExternalW(TCITEMW *pti, UINT iItem)
182 TRACE("external tab %d, mask=0x%08x, dwState=0x%08lx, dwStateMask=0x%08lx, cchTextMax=0x%08x\n",
183 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
184 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
185 iItem, pti->iImage, pti->lParam, debugstr_w(pti->pszText));
190 TAB_DumpItemInternal(TAB_INFO *infoPtr, UINT iItem)
195 ti = TAB_GetItem(infoPtr, iItem);
196 TRACE("tab %d, mask=0x%08x, dwState=0x%08lx, pszText=%s, iImage=%d\n",
197 iItem, ti->mask, ti->dwState, debugstr_w(ti->pszText),
199 TRACE("tab %d, rect.left=%ld, rect.top(row)=%ld\n",
200 iItem, ti->rect.left, ti->rect.top);
205 TAB_GetCurSel (HWND hwnd)
207 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
209 return infoPtr->iSelected;
213 TAB_GetCurFocus (HWND hwnd)
215 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
217 return infoPtr->uFocus;
221 TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
223 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
225 if (infoPtr == NULL) return 0;
226 return (LRESULT)infoPtr->hwndToolTip;
230 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
232 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
233 INT iItem = (INT)wParam;
237 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
238 prevItem=infoPtr->iSelected;
239 infoPtr->iSelected=iItem;
240 TAB_EnsureSelectionVisible(hwnd, infoPtr);
241 TAB_InvalidateTabArea(hwnd, infoPtr);
247 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
249 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
250 INT iItem=(INT) wParam;
252 if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
254 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
255 FIXME("Should set input focus\n");
257 int oldFocus = infoPtr->uFocus;
258 if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) {
259 infoPtr->uFocus = iItem;
260 if (oldFocus != -1) {
261 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
262 infoPtr->iSelected = iItem;
263 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
266 infoPtr->iSelected = iItem;
267 TAB_EnsureSelectionVisible(hwnd, infoPtr);
268 TAB_InvalidateTabArea(hwnd, infoPtr);
276 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
278 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
280 if (infoPtr == NULL) return 0;
281 infoPtr->hwndToolTip = (HWND)wParam;
286 TAB_SetPadding (HWND hwnd, WPARAM wParam, LPARAM lParam)
288 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
290 if (infoPtr == NULL) return 0;
291 infoPtr->uHItemPadding_s=LOWORD(lParam);
292 infoPtr->uVItemPadding_s=HIWORD(lParam);
296 /******************************************************************************
297 * TAB_InternalGetItemRect
299 * This method will calculate the rectangle representing a given tab item in
300 * client coordinates. This method takes scrolling into account.
302 * This method returns TRUE if the item is visible in the window and FALSE
303 * if it is completely outside the client area.
305 static BOOL TAB_InternalGetItemRect(
312 RECT tmpItemRect,clientRect;
313 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
315 /* Perform a sanity check and a trivial visibility check. */
316 if ( (infoPtr->uNumItem <= 0) ||
317 (itemIndex >= infoPtr->uNumItem) ||
318 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
322 * Avoid special cases in this procedure by assigning the "out"
323 * parameters if the caller didn't supply them
325 if (itemRect == NULL)
326 itemRect = &tmpItemRect;
328 /* Retrieve the unmodified item rect. */
329 *itemRect = TAB_GetItem(infoPtr,itemIndex)->rect;
331 /* calculate the times bottom and top based on the row */
332 GetClientRect(hwnd, &clientRect);
334 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
336 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * infoPtr->tabHeight -
337 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
338 itemRect->left = itemRect->right - infoPtr->tabHeight;
340 else if (lStyle & TCS_VERTICAL)
342 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * infoPtr->tabHeight +
343 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
344 itemRect->right = itemRect->left + infoPtr->tabHeight;
346 else if (lStyle & TCS_BOTTOM)
348 itemRect->bottom = clientRect.bottom - itemRect->top * infoPtr->tabHeight -
349 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
350 itemRect->top = itemRect->bottom - infoPtr->tabHeight;
352 else /* not TCS_BOTTOM and not TCS_VERTICAL */
354 itemRect->top = clientRect.top + itemRect->top * infoPtr->tabHeight +
355 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
356 itemRect->bottom = itemRect->top + infoPtr->tabHeight;
360 * "scroll" it to make sure the item at the very left of the
361 * tab control is the leftmost visible tab.
363 if(lStyle & TCS_VERTICAL)
367 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.top);
370 * Move the rectangle so the first item is slightly offset from
371 * the bottom of the tab control.
375 SELECTED_TAB_OFFSET);
380 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.left,
384 * Move the rectangle so the first item is slightly offset from
385 * the left of the tab control.
391 TRACE("item %d tab h=%d, rect=(%ld,%ld)-(%ld,%ld)\n",
392 itemIndex, infoPtr->tabHeight,
393 itemRect->left, itemRect->top, itemRect->right, itemRect->bottom);
395 /* Now, calculate the position of the item as if it were selected. */
396 if (selectedRect!=NULL)
398 CopyRect(selectedRect, itemRect);
400 /* The rectangle of a selected item is a bit wider. */
401 if(lStyle & TCS_VERTICAL)
402 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
404 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
406 /* If it also a bit higher. */
407 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
409 selectedRect->left -= 2; /* the border is thicker on the right */
410 selectedRect->right += SELECTED_TAB_OFFSET;
412 else if (lStyle & TCS_VERTICAL)
414 selectedRect->left -= SELECTED_TAB_OFFSET;
415 selectedRect->right += 1;
417 else if (lStyle & TCS_BOTTOM)
419 selectedRect->bottom += SELECTED_TAB_OFFSET;
421 else /* not TCS_BOTTOM and not TCS_VERTICAL */
423 selectedRect->top -= SELECTED_TAB_OFFSET;
424 selectedRect->bottom -= 1;
431 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
433 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
434 (LPRECT)lParam, (LPRECT)NULL);
437 /******************************************************************************
440 * This method is called to handle keyboard input
442 static LRESULT TAB_KeyUp(
446 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
452 newItem = infoPtr->uFocus - 1;
455 newItem = infoPtr->uFocus + 1;
460 * If we changed to a valid item, change the selection
462 if ((newItem >= 0) &&
463 (newItem < infoPtr->uNumItem) &&
464 (infoPtr->uFocus != newItem))
466 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
468 infoPtr->iSelected = newItem;
469 infoPtr->uFocus = newItem;
470 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
472 TAB_EnsureSelectionVisible(hwnd, infoPtr);
473 TAB_InvalidateTabArea(hwnd, infoPtr);
480 /******************************************************************************
483 * This method is called whenever the focus goes in or out of this control
484 * it is used to update the visual state of the control.
486 static LRESULT TAB_FocusChanging(
492 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
497 * Get the rectangle for the item.
499 isVisible = TAB_InternalGetItemRect(hwnd,
506 * If the rectangle is not completely invisible, invalidate that
507 * portion of the window.
511 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
512 selectedRect.left,selectedRect.top,
513 selectedRect.right,selectedRect.bottom);
514 InvalidateRect(hwnd, &selectedRect, TRUE);
518 * Don't otherwise disturb normal behavior.
520 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
523 static INT TAB_InternalHitTest (
533 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
535 TAB_InternalGetItemRect(hwnd, infoPtr, iCount, &rect, NULL);
537 if (PtInRect(&rect, pt))
539 *flags = TCHT_ONITEM;
544 *flags = TCHT_NOWHERE;
549 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
551 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
552 LPTCHITTESTINFO lptest = (LPTCHITTESTINFO) lParam;
554 return TAB_InternalHitTest (hwnd, infoPtr, lptest->pt, &lptest->flags);
557 /******************************************************************************
560 * Napster v2b5 has a tab control for its main navigation which has a client
561 * area that covers the whole area of the dialog pages.
562 * That's why it receives all msgs for that area and the underlying dialog ctrls
564 * So I decided that we should handle WM_NCHITTEST here and return
565 * HTTRANSPARENT if we don't hit the tab control buttons.
566 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
567 * doesn't do it that way. Maybe depends on tab control styles ?
570 TAB_NCHitTest (HWND hwnd, LPARAM lParam)
572 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
576 pt.x = LOWORD(lParam);
577 pt.y = HIWORD(lParam);
578 ScreenToClient(hwnd, &pt);
580 if (TAB_InternalHitTest(hwnd, infoPtr, pt, &dummyflag) == -1)
581 return HTTRANSPARENT;
587 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
589 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
593 if (infoPtr->hwndToolTip)
594 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
595 WM_LBUTTONDOWN, wParam, lParam);
597 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
601 if (infoPtr->hwndToolTip)
602 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
603 WM_LBUTTONDOWN, wParam, lParam);
605 pt.x = (INT)LOWORD(lParam);
606 pt.y = (INT)HIWORD(lParam);
608 newItem = TAB_InternalHitTest (hwnd, infoPtr, pt, &dummy);
610 TRACE("On Tab, item %d\n", newItem);
612 if ((newItem != -1) && (infoPtr->iSelected != newItem))
614 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING) != TRUE)
616 infoPtr->iSelected = newItem;
617 infoPtr->uFocus = newItem;
618 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
620 TAB_EnsureSelectionVisible(hwnd, infoPtr);
622 TAB_InvalidateTabArea(hwnd, infoPtr);
629 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
631 TAB_SendSimpleNotify(hwnd, NM_CLICK);
637 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
639 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
643 /******************************************************************************
644 * TAB_DrawLoneItemInterior
646 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
647 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
648 * up the device context and font. This routine does the same setup but
649 * only calls TAB_DrawItemInterior for the single specified item.
652 TAB_DrawLoneItemInterior(HWND hwnd, TAB_INFO* infoPtr, int iItem)
654 HDC hdc = GetDC(hwnd);
657 /* Clip UpDown control to not draw over it */
658 if (infoPtr->needsScrolling)
660 GetWindowRect(hwnd, &rC);
661 GetWindowRect(infoPtr->hwndUpDown, &r);
662 ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top);
664 TAB_DrawItemInterior(hwnd, hdc, iItem, NULL);
665 ReleaseDC(hwnd, hdc);
668 /******************************************************************************
669 * TAB_HotTrackTimerProc
671 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
672 * timer is setup so we can check if the mouse is moved out of our window.
673 * (We don't get an event when the mouse leaves, the mouse-move events just
674 * stop being delivered to our window and just start being delivered to
675 * another window.) This function is called when the timer triggers so
676 * we can check if the mouse has left our window. If so, we un-highlight
677 * the hot-tracked tab.
680 TAB_HotTrackTimerProc
682 HWND hwnd, /* handle of window for timer messages */
683 UINT uMsg, /* WM_TIMER message */
684 UINT idEvent, /* timer identifier */
685 DWORD dwTime /* current system time */
688 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
690 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
695 ** If we can't get the cursor position, or if the cursor is outside our
696 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
697 ** "outside" even if it is within our bounding rect if another window
698 ** overlaps. Note also that the case where the cursor stayed within our
699 ** window but has moved off the hot-tracked tab will be handled by the
700 ** WM_MOUSEMOVE event.
702 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
704 /* Redraw iHotTracked to look normal */
705 INT iRedraw = infoPtr->iHotTracked;
706 infoPtr->iHotTracked = -1;
707 TAB_DrawLoneItemInterior(hwnd, infoPtr, iRedraw);
709 /* Kill this timer */
710 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
715 /******************************************************************************
718 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
719 * should be highlighted. This function determines which tab in a tab control,
720 * if any, is under the mouse and records that information. The caller may
721 * supply output parameters to receive the item number of the tab item which
722 * was highlighted but isn't any longer and of the tab item which is now
723 * highlighted but wasn't previously. The caller can use this information to
724 * selectively redraw those tab items.
726 * If the caller has a mouse position, it can supply it through the pos
727 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
728 * supplies NULL and this function determines the current mouse position
736 int* out_redrawLeave,
740 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
745 if (out_redrawLeave != NULL)
746 *out_redrawLeave = -1;
747 if (out_redrawEnter != NULL)
748 *out_redrawEnter = -1;
750 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_HOTTRACK)
758 ScreenToClient(hwnd, &pt);
766 item = TAB_InternalHitTest(hwnd, infoPtr, pt, &flags);
769 if (item != infoPtr->iHotTracked)
771 if (infoPtr->iHotTracked >= 0)
773 /* Mark currently hot-tracked to be redrawn to look normal */
774 if (out_redrawLeave != NULL)
775 *out_redrawLeave = infoPtr->iHotTracked;
779 /* Kill timer which forces recheck of mouse pos */
780 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
785 /* Start timer so we recheck mouse pos */
786 UINT timerID = SetTimer
790 TAB_HOTTRACK_TIMER_INTERVAL,
791 TAB_HotTrackTimerProc
795 return; /* Hot tracking not available */
798 infoPtr->iHotTracked = item;
802 /* Mark new hot-tracked to be redrawn to look highlighted */
803 if (out_redrawEnter != NULL)
804 *out_redrawEnter = item;
809 /******************************************************************************
812 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
815 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
820 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
822 if (infoPtr->hwndToolTip)
823 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
824 WM_LBUTTONDOWN, wParam, lParam);
826 /* Determine which tab to highlight. Redraw tabs which change highlight
828 TAB_RecalcHotTrack(hwnd, &lParam, &redrawLeave, &redrawEnter);
830 if (redrawLeave != -1)
831 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawLeave);
832 if (redrawEnter != -1)
833 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawEnter);
838 /******************************************************************************
841 * Calculates the tab control's display area given the window rectangle or
842 * the window rectangle given the requested display rectangle.
844 static LRESULT TAB_AdjustRect(
849 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
850 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
851 LONG *iRightBottom, *iLeftTop;
853 TRACE ("hwnd=%p fLarger=%d (%ld,%ld)-(%ld,%ld)\n", hwnd, fLarger, prc->left, prc->top, prc->right, prc->bottom);
855 if(lStyle & TCS_VERTICAL)
857 iRightBottom = &(prc->right);
858 iLeftTop = &(prc->left);
862 iRightBottom = &(prc->bottom);
863 iLeftTop = &(prc->top);
866 if (fLarger) /* Go from display rectangle */
868 /* Add the height of the tabs. */
869 if (lStyle & TCS_BOTTOM)
870 *iRightBottom += infoPtr->tabHeight * infoPtr->uNumRows;
872 *iLeftTop -= infoPtr->tabHeight * infoPtr->uNumRows +
873 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
875 /* Inflate the rectangle for the padding */
876 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
878 /* Inflate for the border */
879 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEY);
881 else /* Go from window rectangle. */
883 /* Deflate the rectangle for the border */
884 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEY);
886 /* Deflate the rectangle for the padding */
887 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
889 /* Remove the height of the tabs. */
890 if (lStyle & TCS_BOTTOM)
891 *iRightBottom -= infoPtr->tabHeight * infoPtr->uNumRows;
893 *iLeftTop += (infoPtr->tabHeight) * infoPtr->uNumRows +
894 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
900 /******************************************************************************
903 * This method will handle the notification from the scroll control and
904 * perform the scrolling operation on the tab control.
906 static LRESULT TAB_OnHScroll(
912 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
914 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
916 if(nPos < infoPtr->leftmostVisible)
917 infoPtr->leftmostVisible--;
919 infoPtr->leftmostVisible++;
921 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
922 TAB_InvalidateTabArea(hwnd, infoPtr);
923 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
924 MAKELONG(infoPtr->leftmostVisible, 0));
930 /******************************************************************************
933 * This method will check the current scrolling state and make sure the
934 * scrolling control is displayed (or not).
936 static void TAB_SetupScrolling(
939 const RECT* clientRect)
942 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
944 if (infoPtr->needsScrolling)
950 * Calculate the position of the scroll control.
952 if(lStyle & TCS_VERTICAL)
954 controlPos.right = clientRect->right;
955 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
957 if (lStyle & TCS_BOTTOM)
959 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
960 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
964 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
965 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
970 controlPos.right = clientRect->right;
971 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
973 if (lStyle & TCS_BOTTOM)
975 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
976 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
980 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
981 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
986 * If we don't have a scroll control yet, we want to create one.
987 * If we have one, we want to make sure it's positioned properly.
989 if (infoPtr->hwndUpDown==0)
991 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
993 WS_VISIBLE | WS_CHILD | UDS_HORZ,
994 controlPos.left, controlPos.top,
995 controlPos.right - controlPos.left,
996 controlPos.bottom - controlPos.top,
1004 SetWindowPos(infoPtr->hwndUpDown,
1006 controlPos.left, controlPos.top,
1007 controlPos.right - controlPos.left,
1008 controlPos.bottom - controlPos.top,
1009 SWP_SHOWWINDOW | SWP_NOZORDER);
1012 /* Now calculate upper limit of the updown control range.
1013 * We do this by calculating how many tabs will be offscreen when the
1014 * last tab is visible.
1016 if(infoPtr->uNumItem)
1018 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
1019 maxRange = infoPtr->uNumItem;
1020 tabwidth = TAB_GetItem(infoPtr, infoPtr->uNumItem - 1)->rect.right;
1022 for(; maxRange > 0; maxRange--)
1024 if(tabwidth - TAB_GetItem(infoPtr,maxRange - 1)->rect.left > vsize)
1028 if(maxRange == infoPtr->uNumItem)
1034 /* If we once had a scroll control... hide it */
1035 if (infoPtr->hwndUpDown!=0)
1036 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1038 if (infoPtr->hwndUpDown)
1039 SendMessageW(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1042 /******************************************************************************
1045 * This method will calculate the position rectangles of all the items in the
1046 * control. The rectangle calculated starts at 0 for the first item in the
1047 * list and ignores scrolling and selection.
1048 * It also uses the current font to determine the height of the tab row and
1049 * it checks if all the tabs fit in the client area of the window. If they
1050 * don't, a scrolling control is added.
1052 static void TAB_SetItemBounds (HWND hwnd)
1054 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1055 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1056 TEXTMETRICA fontMetrics;
1059 INT curItemRowCount;
1060 HFONT hFont, hOldFont;
1070 * We need to get text information so we need a DC and we need to select
1075 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1076 hOldFont = SelectObject (hdc, hFont);
1079 * We will base the rectangle calculations on the client rectangle
1082 GetClientRect(hwnd, &clientRect);
1084 /* if TCS_VERTICAL then swap the height and width so this code places the
1085 tabs along the top of the rectangle and we can just rotate them after
1086 rather than duplicate all of the below code */
1087 if(lStyle & TCS_VERTICAL)
1089 iTemp = clientRect.bottom;
1090 clientRect.bottom = clientRect.right;
1091 clientRect.right = iTemp;
1094 /* Now use hPadding and vPadding */
1095 infoPtr->uHItemPadding = infoPtr->uHItemPadding_s;
1096 infoPtr->uVItemPadding = infoPtr->uVItemPadding_s;
1098 /* The leftmost item will be "0" aligned */
1100 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1102 if (!(infoPtr->fHeightSet))
1105 int icon_height = 0;
1107 /* Use the current font to determine the height of a tab. */
1108 GetTextMetricsA(hdc, &fontMetrics);
1110 /* Get the icon height */
1112 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1114 /* Take the highest between font or icon */
1115 if (fontMetrics.tmHeight > icon_height)
1116 item_height = fontMetrics.tmHeight + 2;
1118 item_height = icon_height;
1121 * Make sure there is enough space for the letters + icon + growing the
1122 * selected item + extra space for the selected item.
1124 infoPtr->tabHeight = item_height +
1125 ((lStyle & TCS_BUTTONS) ? 2 : 1) *
1126 infoPtr->uVItemPadding;
1128 TRACE("tabH=%d, tmH=%ld, iconh=%d\n",
1129 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1132 TRACE("client right=%ld\n", clientRect.right);
1134 /* Get the icon width */
1137 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1139 if (lStyle & TCS_FIXEDWIDTH)
1142 /* Add padding if icon is present */
1143 icon_width += infoPtr->uHItemPadding;
1146 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1148 TAB_ITEM *curr = TAB_GetItem(infoPtr, curItem);
1150 /* Set the leftmost position of the tab. */
1151 curr->rect.left = curItemLeftPos;
1153 if ((lStyle & TCS_FIXEDWIDTH) || !curr->pszText)
1155 curr->rect.right = curr->rect.left +
1156 max(infoPtr->tabWidth, icon_width);
1162 /* Calculate how wide the tab is depending on the text it contains */
1163 GetTextExtentPoint32W(hdc, curr->pszText,
1164 lstrlenW(curr->pszText), &size);
1166 curr->rect.right = curr->rect.left + size.cx + icon_width +
1167 num * infoPtr->uHItemPadding;
1168 TRACE("for <%s>, l,r=%ld,%ld, num=%d\n",
1169 debugstr_w(curr->pszText), curr->rect.left, curr->rect.right, num);
1173 * Check if this is a multiline tab control and if so
1174 * check to see if we should wrap the tabs
1176 * Wrap all these tabs. We will arrange them evenly later.
1180 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1182 (clientRect.right - CONTROL_BORDER_SIZEX - DISPLAY_AREA_PADDINGX)))
1184 curr->rect.right -= curr->rect.left;
1186 curr->rect.left = 0;
1188 TRACE("wrapping <%s>, l,r=%ld,%ld\n", debugstr_w(curr->pszText),
1189 curr->rect.left, curr->rect.right);
1192 curr->rect.bottom = 0;
1193 curr->rect.top = curItemRowCount - 1;
1195 TRACE("TextSize: %li\n", size.cx);
1196 TRACE("Rect: T %li, L %li, B %li, R %li\n", curr->rect.top,
1197 curr->rect.left, curr->rect.bottom, curr->rect.right);
1200 * The leftmost position of the next item is the rightmost position
1203 if (lStyle & TCS_BUTTONS)
1205 curItemLeftPos = curr->rect.right + BUTTON_SPACINGX;
1206 if (lStyle & TCS_FLATBUTTONS)
1207 curItemLeftPos += FLAT_BTN_SPACINGX;
1210 curItemLeftPos = curr->rect.right;
1213 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1216 * Check if we need a scrolling control.
1218 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1221 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1222 if(!infoPtr->needsScrolling)
1223 infoPtr->leftmostVisible = 0;
1228 * No scrolling in Multiline or Vertical styles.
1230 infoPtr->needsScrolling = FALSE;
1231 infoPtr->leftmostVisible = 0;
1233 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
1235 /* Set the number of rows */
1236 infoPtr->uNumRows = curItemRowCount;
1238 /* Arrange all tabs evenly if style says so */
1239 if (!(lStyle & TCS_RAGGEDRIGHT) && ((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->uNumItem > 0))
1241 INT tabPerRow,remTab,iRow;
1246 * Ok windows tries to even out the rows. place the same
1247 * number of tabs in each row. So lets give that a shot
1250 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1251 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1253 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1254 iItm<infoPtr->uNumItem;
1257 /* normalize the current rect */
1258 TAB_ITEM *curr = TAB_GetItem(infoPtr, iItm);
1260 /* shift the item to the left side of the clientRect */
1261 curr->rect.right -= curr->rect.left;
1262 curr->rect.left = 0;
1264 TRACE("r=%ld, cl=%d, cl.r=%ld, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1265 curr->rect.right, curItemLeftPos, clientRect.right,
1266 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1268 /* if we have reached the maximum number of tabs on this row */
1269 /* move to the next row, reset our current item left position and */
1270 /* the count of items on this row */
1272 if (lStyle & TCS_VERTICAL) {
1273 /* Vert: Add the remaining tabs in the *last* remainder rows */
1274 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1280 /* Horz: Add the remaining tabs in the *first* remainder rows */
1281 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1288 /* shift the item to the right to place it as the next item in this row */
1289 curr->rect.left += curItemLeftPos;
1290 curr->rect.right += curItemLeftPos;
1291 curr->rect.top = iRow;
1292 if (lStyle & TCS_BUTTONS)
1294 curItemLeftPos = curr->rect.right + 1;
1295 if (lStyle & TCS_FLATBUTTONS)
1296 curItemLeftPos += FLAT_BTN_SPACINGX;
1299 curItemLeftPos = curr->rect.right;
1301 TRACE("arranging <%s>, l,r=%ld,%ld, row=%ld\n",
1302 debugstr_w(curr->pszText), curr->rect.left,
1303 curr->rect.right, curr->rect.top);
1310 INT widthDiff, iIndexStart=0, iIndexEnd=0;
1314 while(iIndexStart < infoPtr->uNumItem)
1316 TAB_ITEM *start = TAB_GetItem(infoPtr, iIndexStart);
1319 * find the index of the row
1321 /* find the first item on the next row */
1322 for (iIndexEnd=iIndexStart;
1323 (iIndexEnd < infoPtr->uNumItem) &&
1324 (TAB_GetItem(infoPtr, iIndexEnd)->rect.top ==
1327 /* intentionally blank */;
1330 * we need to justify these tabs so they fill the whole given
1334 /* find the amount of space remaining on this row */
1335 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1336 TAB_GetItem(infoPtr, iIndexEnd - 1)->rect.right;
1338 /* iCount is the number of tab items on this row */
1339 iCount = iIndexEnd - iIndexStart;
1343 remainder = widthDiff % iCount;
1344 widthDiff = widthDiff / iCount;
1345 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1346 for (iIndex=iIndexStart, iCount=0; iIndex < iIndexEnd; iIndex++, iCount++)
1348 TAB_ITEM *item = TAB_GetItem(infoPtr, iIndex);
1350 item->rect.left += iCount * widthDiff;
1351 item->rect.right += (iCount + 1) * widthDiff;
1353 TRACE("adjusting 1 <%s>, l,r=%ld,%ld\n",
1354 debugstr_w(item->pszText),
1355 item->rect.left, item->rect.right);
1358 TAB_GetItem(infoPtr, iIndex - 1)->rect.right += remainder;
1360 else /* we have only one item on this row, make it take up the entire row */
1362 start->rect.left = clientRect.left;
1363 start->rect.right = clientRect.right - 4;
1365 TRACE("adjusting 2 <%s>, l,r=%ld,%ld\n",
1366 debugstr_w(start->pszText),
1367 start->rect.left, start->rect.right);
1372 iIndexStart = iIndexEnd;
1377 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1378 if(lStyle & TCS_VERTICAL)
1381 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1383 rcItem = &TAB_GetItem(infoPtr, iIndex)->rect;
1385 rcOriginal = *rcItem;
1387 /* this is rotating the items by 90 degrees clockwise around the center of the control */
1388 rcItem->top = (rcOriginal.left - clientRect.left);
1389 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1390 rcItem->left = rcOriginal.top;
1391 rcItem->right = rcOriginal.bottom;
1395 TAB_EnsureSelectionVisible(hwnd,infoPtr);
1396 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1399 SelectObject (hdc, hOldFont);
1400 ReleaseDC (hwnd, hdc);
1405 TAB_EraseTabInterior
1413 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1414 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1415 HBRUSH hbr = CreateSolidBrush (comctl32_color.clrBtnFace);
1416 BOOL deleteBrush = TRUE;
1417 RECT rTemp = *drawRect;
1419 InflateRect(&rTemp, -2, -2);
1420 if (lStyle & TCS_BUTTONS)
1422 if (iItem == infoPtr->iSelected)
1424 /* Background color */
1425 if (!(lStyle & TCS_OWNERDRAWFIXED))
1428 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1430 SetTextColor(hdc, comctl32_color.clr3dFace);
1431 SetBkColor(hdc, comctl32_color.clr3dHilight);
1433 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1434 * we better use 0x55aa bitmap brush to make scrollbar's background
1435 * look different from the window background.
1437 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1438 hbr = COMCTL32_hPattern55AABrush;
1440 deleteBrush = FALSE;
1442 FillRect(hdc, &rTemp, hbr);
1444 else /* ! selected */
1446 if (lStyle & TCS_FLATBUTTONS)
1448 FillRect(hdc, drawRect, hbr);
1449 if (iItem == infoPtr->iHotTracked)
1450 DrawEdge(hdc, drawRect, EDGE_RAISED, BF_SOFT|BF_RECT);
1453 FillRect(hdc, &rTemp, hbr);
1457 else /* !TCS_BUTTONS */
1459 FillRect(hdc, &rTemp, hbr);
1463 if (deleteBrush) DeleteObject(hbr);
1466 /******************************************************************************
1467 * TAB_DrawItemInterior
1469 * This method is used to draw the interior (text and icon) of a single tab
1470 * into the tab control.
1473 TAB_DrawItemInterior
1481 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1482 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1491 /* if (drawRect == NULL) */
1498 * Get the rectangle for the item.
1500 isVisible = TAB_InternalGetItemRect(hwnd, infoPtr, iItem, &itemRect, &selectedRect);
1505 * Make sure drawRect points to something valid; simplifies code.
1507 drawRect = &localRect;
1510 * This logic copied from the part of TAB_DrawItem which draws
1511 * the tab background. It's important to keep it in sync. I
1512 * would have liked to avoid code duplication, but couldn't figure
1513 * out how without making spaghetti of TAB_DrawItem.
1515 if (iItem == infoPtr->iSelected)
1516 *drawRect = selectedRect;
1518 *drawRect = itemRect;
1520 if (lStyle & TCS_BUTTONS)
1522 if (iItem == infoPtr->iSelected)
1524 drawRect->left += 4;
1526 drawRect->right -= 4;
1527 drawRect->bottom -= 1;
1531 drawRect->left += 2;
1533 drawRect->right -= 2;
1534 drawRect->bottom -= 2;
1539 if ((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1541 if (iItem != infoPtr->iSelected)
1543 drawRect->left += 2;
1545 drawRect->bottom -= 2;
1548 else if (lStyle & TCS_VERTICAL)
1550 if (iItem == infoPtr->iSelected)
1552 drawRect->right += 1;
1557 drawRect->right -= 2;
1558 drawRect->bottom -= 2;
1561 else if (lStyle & TCS_BOTTOM)
1563 if (iItem == infoPtr->iSelected)
1569 InflateRect(drawRect, -2, -2);
1570 drawRect->bottom += 2;
1575 if (iItem == infoPtr->iSelected)
1577 drawRect->bottom += 3;
1581 drawRect->bottom -= 2;
1582 InflateRect(drawRect, -2, 0);
1587 TRACE("drawRect=(%ld,%ld)-(%ld,%ld)\n",
1588 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom);
1590 /* Clear interior */
1591 TAB_EraseTabInterior (hwnd, hdc, iItem, drawRect);
1593 /* Draw the focus rectangle */
1594 if (!(lStyle & TCS_FOCUSNEVER) &&
1595 (GetFocus() == hwnd) &&
1596 (iItem == infoPtr->uFocus) )
1598 RECT rFocus = *drawRect;
1599 InflateRect(&rFocus, -3, -3);
1600 if (lStyle & TCS_BOTTOM && !(lStyle & TCS_VERTICAL))
1602 if (lStyle & TCS_BUTTONS)
1608 DrawFocusRect(hdc, &rFocus);
1614 htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1615 holdPen = SelectObject(hdc, htextPen);
1616 hOldFont = SelectObject(hdc, infoPtr->hFont);
1619 * Setup for text output
1621 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1622 SetTextColor(hdc, (((iItem == infoPtr->iHotTracked) && !(lStyle & TCS_FLATBUTTONS)) |
1623 (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED)) ?
1624 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1627 * if owner draw, tell the owner to draw
1629 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd))
1635 drawRect->right -= 1;
1636 if ( iItem == infoPtr->iSelected )
1638 drawRect->right -= 1;
1639 drawRect->left += 1;
1643 * get the control id
1645 id = (UINT)GetWindowLongPtrW( hwnd, GWLP_ID );
1648 * put together the DRAWITEMSTRUCT
1650 dis.CtlType = ODT_TAB;
1653 dis.itemAction = ODA_DRAWENTIRE;
1655 if ( iItem == infoPtr->iSelected )
1656 dis.itemState |= ODS_SELECTED;
1657 if (infoPtr->uFocus == iItem)
1658 dis.itemState |= ODS_FOCUS;
1659 dis.hwndItem = hwnd; /* */
1661 CopyRect(&dis.rcItem,drawRect);
1663 memcpy( &dis.itemData, TAB_GetItem(infoPtr, iItem)->extra, min(sizeof(dis.itemData),infoPtr->cbInfo) );
1666 * send the draw message
1668 SendMessageW( infoPtr->hwndNotify, WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1672 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
1676 /* used to center the icon and text in the tab */
1678 INT center_offset_h, center_offset_v;
1680 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1681 rcImage = *drawRect;
1685 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1687 /* get the rectangle that the text fits in */
1690 DrawTextW(hdc, item->pszText, -1, &rcText, DT_CALCRECT);
1693 * If not owner draw, then do the drawing ourselves.
1697 if (infoPtr->himl && (item->mask & TCIF_IMAGE))
1702 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1704 if(lStyle & TCS_VERTICAL)
1706 center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1707 center_offset_v = ((drawRect->right - drawRect->left) - (cx + infoPtr->uVItemPadding)) / 2;
1711 center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1712 center_offset_v = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uVItemPadding)) / 2;
1715 if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1716 center_offset_h = infoPtr->uHItemPadding;
1718 if (center_offset_h < 2)
1719 center_offset_h = 2;
1721 if (center_offset_v < 0)
1722 center_offset_v = 0;
1724 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1725 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1726 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1727 (rcText.right-rcText.left));
1729 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1731 rcImage.top = drawRect->top + center_offset_h;
1732 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1733 /* right side of the tab, but the image still uses the left as its x position */
1734 /* this keeps the image always drawn off of the same side of the tab */
1735 rcImage.left = drawRect->right - cx - center_offset_v;
1736 drawRect->top += cy + infoPtr->uHItemPadding;
1738 else if(lStyle & TCS_VERTICAL)
1740 rcImage.top = drawRect->bottom - cy - center_offset_h;
1741 rcImage.left = drawRect->left + center_offset_v;
1742 drawRect->bottom -= cy + infoPtr->uHItemPadding;
1744 else /* normal style, whether TCS_BOTTOM or not */
1746 rcImage.left = drawRect->left + center_offset_h;
1747 rcImage.top = drawRect->top + center_offset_v;
1748 drawRect->left += cx + infoPtr->uHItemPadding;
1751 TRACE("drawing image=%d, left=%ld, top=%ld\n",
1752 item->iImage, rcImage.left, rcImage.top-1);
1764 /* Now position text */
1765 if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT)
1766 center_offset_h = infoPtr->uHItemPadding;
1768 if(lStyle & TCS_VERTICAL)
1769 center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1771 center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1773 if(lStyle & TCS_VERTICAL)
1775 if(lStyle & TCS_BOTTOM)
1776 drawRect->top+=center_offset_h;
1778 drawRect->bottom-=center_offset_h;
1780 center_offset_v = ((drawRect->right - drawRect->left) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1784 drawRect->left += center_offset_h;
1785 center_offset_v = ((drawRect->bottom - drawRect->top) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1788 if (center_offset_v < 0)
1789 center_offset_v = 0;
1791 if(lStyle & TCS_VERTICAL)
1792 drawRect->left += center_offset_v;
1794 drawRect->top += center_offset_v;
1797 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1801 INT nEscapement = 900;
1802 INT nOrientation = 900;
1804 if(lStyle & TCS_BOTTOM)
1807 nOrientation = -900;
1810 /* to get a font with the escapement and orientation we are looking for, we need to */
1811 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1812 if (!GetObjectA((infoPtr->hFont) ?
1813 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1814 sizeof(LOGFONTA),&logfont))
1818 lstrcpyA(logfont.lfFaceName, "Arial");
1819 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1821 logfont.lfWeight = FW_NORMAL;
1822 logfont.lfItalic = 0;
1823 logfont.lfUnderline = 0;
1824 logfont.lfStrikeOut = 0;
1827 logfont.lfEscapement = nEscapement;
1828 logfont.lfOrientation = nOrientation;
1829 hFont = CreateFontIndirectA(&logfont);
1830 SelectObject(hdc, hFont);
1835 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1836 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1840 lstrlenW(item->pszText),
1844 DeleteObject(hFont);
1848 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1849 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1850 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1851 (rcText.right-rcText.left));
1858 lstrlenW(item->pszText),
1860 DT_LEFT | DT_SINGLELINE
1865 *drawRect = rcTemp; /* restore drawRect */
1871 SelectObject(hdc, hOldFont);
1872 SetBkMode(hdc, oldBkMode);
1873 SelectObject(hdc, holdPen);
1874 DeleteObject( htextPen );
1877 /******************************************************************************
1880 * This method is used to draw a single tab into the tab control.
1882 static void TAB_DrawItem(
1887 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1888 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1892 RECT r, fillRect, r1;
1895 COLORREF bkgnd, corner;
1898 * Get the rectangle for the item.
1900 isVisible = TAB_InternalGetItemRect(hwnd,
1910 /* Clip UpDown control to not draw over it */
1911 if (infoPtr->needsScrolling)
1913 GetWindowRect(hwnd, &rC);
1914 GetWindowRect(infoPtr->hwndUpDown, &rUD);
1915 ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
1918 /* If you need to see what the control is doing,
1919 * then override these variables. They will change what
1920 * fill colors are used for filling the tabs, and the
1921 * corners when drawing the edge.
1923 bkgnd = comctl32_color.clrBtnFace;
1924 corner = comctl32_color.clrBtnFace;
1926 if (lStyle & TCS_BUTTONS)
1928 /* Get item rectangle */
1931 /* Separators between flat buttons */
1932 if (lStyle & TCS_FLATBUTTONS)
1935 r1.right += (FLAT_BTN_SPACINGX -2);
1936 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1939 if (iItem == infoPtr->iSelected)
1941 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1943 OffsetRect(&r, 1, 1);
1945 else /* ! selected */
1947 if (!(lStyle & TCS_FLATBUTTONS))
1948 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1951 else /* !TCS_BUTTONS */
1953 /* We draw a rectangle of different sizes depending on the selection
1955 if (iItem == infoPtr->iSelected) {
1957 GetClientRect (hwnd, &rect);
1958 clRight = rect.right;
1959 clBottom = rect.bottom;
1966 * Erase the background. (Delay it but setup rectangle.)
1967 * This is necessary when drawing the selected item since it is larger
1968 * than the others, it might overlap with stuff already drawn by the
1973 if(lStyle & TCS_VERTICAL)
1975 /* These are for adjusting the drawing of a Selected tab */
1976 /* The initial values are for the normal case of non-Selected */
1977 int ZZ = 1; /* Do not strech if selected */
1978 if (iItem == infoPtr->iSelected) {
1981 /* if leftmost draw the line longer */
1982 if(selectedRect.top == 0)
1983 fillRect.top += CONTROL_BORDER_SIZEY;
1984 /* if rightmost draw the line longer */
1985 if(selectedRect.bottom == clBottom)
1986 fillRect.bottom -= CONTROL_BORDER_SIZEY;
1989 if (lStyle & TCS_BOTTOM)
1991 /* Adjust both rectangles to match native */
1994 TRACE("<right> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1996 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1997 r.left,r.top,r.right,r.bottom);
1999 /* Clear interior */
2000 SetBkColor(hdc, bkgnd);
2001 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2003 /* Draw rectangular edge around tab */
2004 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
2006 /* Now erase the top corner and draw diagonal edge */
2007 SetBkColor(hdc, corner);
2008 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2011 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2012 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2014 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2016 /* Now erase the bottom corner and draw diagonal edge */
2017 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2018 r1.bottom = r.bottom;
2020 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2021 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2023 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2025 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
2029 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
2035 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2037 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2038 r.left,r.top,r.right,r.bottom);
2040 /* Clear interior */
2041 SetBkColor(hdc, bkgnd);
2042 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2044 /* Draw rectangular edge around tab */
2045 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2047 /* Now erase the top corner and draw diagonal edge */
2048 SetBkColor(hdc, corner);
2051 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2052 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2053 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2055 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2057 /* Now erase the bottom corner and draw diagonal edge */
2059 r1.bottom = r.bottom;
2060 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2061 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2062 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2064 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2067 else /* ! TCS_VERTICAL */
2069 /* These are for adjusting the drawing of a Selected tab */
2070 /* The initial values are for the normal case of non-Selected */
2071 if (iItem == infoPtr->iSelected) {
2072 /* if leftmost draw the line longer */
2073 if(selectedRect.left == 0)
2074 fillRect.left += CONTROL_BORDER_SIZEX;
2075 /* if rightmost draw the line longer */
2076 if(selectedRect.right == clRight)
2077 fillRect.right -= CONTROL_BORDER_SIZEX;
2080 if (lStyle & TCS_BOTTOM)
2082 /* Adjust both rectangles for topmost row */
2083 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2089 TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2091 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2092 r.left,r.top,r.right,r.bottom);
2094 /* Clear interior */
2095 SetBkColor(hdc, bkgnd);
2096 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2098 /* Draw rectangular edge around tab */
2099 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2101 /* Now erase the righthand corner and draw diagonal edge */
2102 SetBkColor(hdc, corner);
2103 r1.left = r.right - ROUND_CORNER_SIZE;
2104 r1.bottom = r.bottom;
2106 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2107 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2109 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2111 /* Now erase the lefthand corner and draw diagonal edge */
2113 r1.bottom = r.bottom;
2114 r1.right = r1.left + ROUND_CORNER_SIZE;
2115 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2116 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2118 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2120 if (iItem == infoPtr->iSelected)
2124 if (selectedRect.left == 0)
2129 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2136 /* Adjust both rectangles for bottommost row */
2137 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2139 fillRect.bottom += 3;
2143 TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2145 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2146 r.left,r.top,r.right,r.bottom);
2148 /* Clear interior */
2149 SetBkColor(hdc, bkgnd);
2150 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2152 /* Draw rectangular edge around tab */
2153 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2155 /* Now erase the righthand corner and draw diagonal edge */
2156 SetBkColor(hdc, corner);
2157 r1.left = r.right - ROUND_CORNER_SIZE;
2160 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2161 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2163 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2165 /* Now erase the lefthand corner and draw diagonal edge */
2168 r1.right = r1.left + ROUND_CORNER_SIZE;
2169 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2170 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2172 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2177 TAB_DumpItemInternal(infoPtr, iItem);
2179 /* This modifies r to be the text rectangle. */
2180 TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
2184 /******************************************************************************
2187 * This method is used to draw the raised border around the tab control
2190 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
2192 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2194 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2196 GetClientRect (hwnd, &rect);
2199 * Adjust for the style
2202 if (infoPtr->uNumItem)
2204 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2205 rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2206 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2207 rect.right -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2208 else if(lStyle & TCS_VERTICAL)
2209 rect.left += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2210 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2211 rect.top += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2214 TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2215 rect.left, rect.top, rect.right, rect.bottom);
2217 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2220 /******************************************************************************
2223 * This method repaints the tab control..
2225 static void TAB_Refresh (HWND hwnd, HDC hdc)
2227 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2231 if (!infoPtr->DoRedraw)
2234 hOldFont = SelectObject (hdc, infoPtr->hFont);
2236 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
2238 for (i = 0; i < infoPtr->uNumItem; i++)
2239 TAB_DrawItem (hwnd, hdc, i);
2243 /* Draw all the non selected item first */
2244 for (i = 0; i < infoPtr->uNumItem; i++)
2246 if (i != infoPtr->iSelected)
2247 TAB_DrawItem (hwnd, hdc, i);
2250 /* Now, draw the border, draw it before the selected item
2251 * since the selected item overwrites part of the border. */
2252 TAB_DrawBorder (hwnd, hdc);
2254 /* Then, draw the selected item */
2255 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
2257 /* If we haven't set the current focus yet, set it now.
2258 * Only happens when we first paint the tab controls */
2259 if (infoPtr->uFocus == -1)
2260 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
2263 SelectObject (hdc, hOldFont);
2267 TAB_GetRowCount (HWND hwnd )
2269 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2271 return infoPtr->uNumRows;
2275 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
2277 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2279 infoPtr->DoRedraw=(BOOL) wParam;
2283 /******************************************************************************
2284 * TAB_EnsureSelectionVisible
2286 * This method will make sure that the current selection is completely
2287 * visible by scrolling until it is.
2289 static void TAB_EnsureSelectionVisible(
2293 INT iSelected = infoPtr->iSelected;
2294 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2295 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2297 /* set the items row to the bottommost row or topmost row depending on
2299 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2301 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2305 if(lStyle & TCS_VERTICAL)
2306 newselected = selected->rect.left;
2308 newselected = selected->rect.top;
2310 /* the target row is always (number of rows - 1)
2311 as row 0 is furthest from the clientRect */
2312 iTargetRow = infoPtr->uNumRows - 1;
2314 if (newselected != iTargetRow)
2317 if(lStyle & TCS_VERTICAL)
2319 for (i=0; i < infoPtr->uNumItem; i++)
2321 /* move everything in the row of the selected item to the iTargetRow */
2322 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2324 if (item->rect.left == newselected )
2325 item->rect.left = iTargetRow;
2328 if (item->rect.left > newselected)
2335 for (i=0; i < infoPtr->uNumItem; i++)
2337 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2339 if (item->rect.top == newselected )
2340 item->rect.top = iTargetRow;
2343 if (item->rect.top > newselected)
2348 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2353 * Do the trivial cases first.
2355 if ( (!infoPtr->needsScrolling) ||
2356 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2359 if (infoPtr->leftmostVisible >= iSelected)
2361 infoPtr->leftmostVisible = iSelected;
2365 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2370 /* Calculate the part of the client area that is visible */
2371 GetClientRect(hwnd, &r);
2374 GetClientRect(infoPtr->hwndUpDown, &r);
2377 if ((selected->rect.right -
2378 selected->rect.left) >= width )
2380 /* Special case: width of selected item is greater than visible
2383 infoPtr->leftmostVisible = iSelected;
2387 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2389 if ((selected->rect.right - TAB_GetItem(infoPtr, i)->rect.left) < width)
2392 infoPtr->leftmostVisible = i;
2396 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2397 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2399 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2400 MAKELONG(infoPtr->leftmostVisible, 0));
2403 /******************************************************************************
2404 * TAB_InvalidateTabArea
2406 * This method will invalidate the portion of the control that contains the
2407 * tabs. It is called when the state of the control changes and needs
2410 static void TAB_InvalidateTabArea(
2414 RECT clientRect, rInvalidate, rAdjClient;
2415 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2416 INT lastRow = infoPtr->uNumRows - 1;
2419 if (lastRow < 0) return;
2421 GetClientRect(hwnd, &clientRect);
2422 rInvalidate = clientRect;
2423 rAdjClient = clientRect;
2425 TAB_AdjustRect(hwnd, 0, &rAdjClient);
2427 TAB_InternalGetItemRect(hwnd, infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2428 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2430 rInvalidate.left = rAdjClient.right;
2431 if (infoPtr->uNumRows == 1)
2432 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2434 else if(lStyle & TCS_VERTICAL)
2436 rInvalidate.right = rAdjClient.left;
2437 if (infoPtr->uNumRows == 1)
2438 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2440 else if (lStyle & TCS_BOTTOM)
2442 rInvalidate.top = rAdjClient.bottom;
2443 if (infoPtr->uNumRows == 1)
2444 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2448 rInvalidate.bottom = rAdjClient.top;
2449 if (infoPtr->uNumRows == 1)
2450 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2453 /* Punch out the updown control */
2454 if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2456 GetClientRect(infoPtr->hwndUpDown, &r);
2457 if (rInvalidate.right > clientRect.right - r.left)
2458 rInvalidate.right = rInvalidate.right - (r.right - r.left);
2460 rInvalidate.right = clientRect.right - r.left;
2463 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2464 rInvalidate.left, rInvalidate.top,
2465 rInvalidate.right, rInvalidate.bottom);
2467 InvalidateRect(hwnd, &rInvalidate, TRUE);
2471 TAB_Paint (HWND hwnd, WPARAM wParam)
2478 hdc = BeginPaint (hwnd, &ps);
2479 TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2481 ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2486 TAB_Refresh (hwnd, hdc);
2489 EndPaint (hwnd, &ps);
2495 TAB_InsertItemAW (HWND hwnd, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2497 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2503 GetClientRect (hwnd, &rect);
2504 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2505 rect.top, rect.left, rect.bottom, rect.right);
2507 pti = (TCITEMA *)lParam;
2508 iItem = (INT)wParam;
2510 if (iItem < 0) return -1;
2511 if (iItem > infoPtr->uNumItem)
2512 iItem = infoPtr->uNumItem;
2515 TAB_DumpItemExternalW((TCITEMW*)pti, iItem);
2517 TAB_DumpItemExternalA(pti, iItem);
2520 if (infoPtr->uNumItem == 0) {
2521 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2522 infoPtr->uNumItem++;
2523 infoPtr->iSelected = 0;
2526 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2528 infoPtr->uNumItem++;
2529 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2531 /* pre insert copy */
2533 memcpy (infoPtr->items, oldItems,
2534 iItem * TAB_ITEM_SIZE(infoPtr));
2537 /* post insert copy */
2538 if (iItem < infoPtr->uNumItem - 1) {
2539 memcpy (TAB_GetItem(infoPtr, iItem + 1),
2540 oldItems + iItem * TAB_ITEM_SIZE(infoPtr),
2541 (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2545 if (iItem <= infoPtr->iSelected)
2546 infoPtr->iSelected++;
2551 item = TAB_GetItem(infoPtr, iItem);
2553 item->mask = pti->mask;
2554 item->pszText = NULL;
2556 if (pti->mask & TCIF_TEXT)
2559 Str_SetPtrW (&item->pszText, (WCHAR*)pti->pszText);
2561 Str_SetPtrAtoW (&item->pszText, pti->pszText);
2564 if (pti->mask & TCIF_IMAGE)
2565 item->iImage = pti->iImage;
2569 if (pti->mask & TCIF_PARAM)
2570 memcpy(item->extra, &pti->lParam, infoPtr->cbInfo);
2572 memset(item->extra, 0, infoPtr->cbInfo);
2574 TAB_SetItemBounds(hwnd);
2575 if (infoPtr->uNumItem > 1)
2576 TAB_InvalidateTabArea(hwnd, infoPtr);
2578 InvalidateRect(hwnd, NULL, TRUE);
2580 TRACE("[%p]: added item %d %s\n",
2581 hwnd, iItem, debugstr_w(item->pszText));
2587 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2589 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2590 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2592 BOOL bNeedPaint = FALSE;
2594 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2596 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2597 if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2599 infoPtr->tabWidth = max((INT)LOWORD(lParam), infoPtr->tabMinWidth);
2603 if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2605 if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2606 infoPtr->tabHeight = (INT)HIWORD(lParam);
2610 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2611 HIWORD(lResult), LOWORD(lResult),
2612 infoPtr->tabHeight, infoPtr->tabWidth);
2616 TAB_SetItemBounds(hwnd);
2617 RedrawWindow(hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2624 TAB_SetMinTabWidth (HWND hwnd, LPARAM lParam)
2626 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2627 INT cx = (INT)lParam;
2631 oldcx = infoPtr->tabMinWidth;
2632 infoPtr->tabMinWidth = (cx==-1)?DEFAULT_TAB_WIDTH:cx;
2640 TAB_HighlightItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2642 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2643 INT iItem = (INT)wParam;
2644 BOOL fHighlight = (BOOL)LOWORD(lParam);
2646 if ((infoPtr) && (iItem>=0) && (iItem<infoPtr->uNumItem)) {
2648 TAB_GetItem(infoPtr, iItem)->dwState |= TCIS_HIGHLIGHTED;
2650 TAB_GetItem(infoPtr, iItem)->dwState &= ~TCIS_HIGHLIGHTED;
2658 TAB_SetItemAW (HWND hwnd, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2660 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2665 iItem = (INT)wParam;
2666 tabItem = (LPTCITEMA)lParam;
2668 TRACE("%d %p\n", iItem, tabItem);
2669 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2672 TAB_DumpItemExternalW((TCITEMW *)tabItem, iItem);
2674 TAB_DumpItemExternalA(tabItem, iItem);
2676 wineItem = TAB_GetItem(infoPtr, iItem);
2678 if (tabItem->mask & TCIF_IMAGE)
2679 wineItem->iImage = tabItem->iImage;
2681 if (tabItem->mask & TCIF_PARAM)
2682 memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2684 if (tabItem->mask & TCIF_RTLREADING)
2685 FIXME("TCIF_RTLREADING\n");
2687 if (tabItem->mask & TCIF_STATE)
2688 wineItem->dwState = tabItem->dwState;
2690 if (tabItem->mask & TCIF_TEXT)
2692 if (wineItem->pszText)
2694 Free(wineItem->pszText);
2695 wineItem->pszText = NULL;
2698 Str_SetPtrW(&wineItem->pszText, (WCHAR*)tabItem->pszText);
2700 Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2703 /* Update and repaint tabs */
2704 TAB_SetItemBounds(hwnd);
2705 TAB_InvalidateTabArea(hwnd,infoPtr);
2711 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2713 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2715 return infoPtr->uNumItem;
2720 TAB_GetItemAW (HWND hwnd, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2722 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2727 iItem = (INT)wParam;
2728 tabItem = (LPTCITEMA)lParam;
2730 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2733 wineItem = TAB_GetItem(infoPtr, iItem);
2735 if (tabItem->mask & TCIF_IMAGE)
2736 tabItem->iImage = wineItem->iImage;
2738 if (tabItem->mask & TCIF_PARAM)
2739 memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2741 if (tabItem->mask & TCIF_RTLREADING)
2742 FIXME("TCIF_RTLREADING\n");
2744 if (tabItem->mask & TCIF_STATE)
2745 tabItem->dwState = wineItem->dwState;
2747 if (tabItem->mask & TCIF_TEXT)
2750 Str_GetPtrW (wineItem->pszText, (WCHAR*)tabItem->pszText, tabItem->cchTextMax);
2752 Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2756 TAB_DumpItemExternalW((TCITEMW*)tabItem, iItem);
2758 TAB_DumpItemExternalA(tabItem, iItem);
2765 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2767 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2768 INT iItem = (INT) wParam;
2769 BOOL bResult = FALSE;
2771 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2773 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
2774 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2776 TAB_InvalidateTabArea(hwnd, infoPtr);
2778 if ((item->mask & TCIF_TEXT) && item->pszText)
2779 Free(item->pszText);
2781 infoPtr->uNumItem--;
2783 if (!infoPtr->uNumItem)
2785 infoPtr->items = NULL;
2786 if (infoPtr->iHotTracked >= 0)
2788 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2789 infoPtr->iHotTracked = -1;
2794 infoPtr->items = Alloc(TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2797 memcpy(infoPtr->items, oldItems, iItem * TAB_ITEM_SIZE(infoPtr));
2799 if (iItem < infoPtr->uNumItem)
2800 memcpy(TAB_GetItem(infoPtr, iItem),
2801 oldItems + (iItem + 1) * TAB_ITEM_SIZE(infoPtr),
2802 (infoPtr->uNumItem - iItem) * TAB_ITEM_SIZE(infoPtr));
2804 if (iItem <= infoPtr->iHotTracked)
2806 /* When tabs move left/up, the hot track item may change */
2807 FIXME("Recalc hot track");
2812 /* Readjust the selected index */
2813 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2814 infoPtr->iSelected--;
2816 if (iItem < infoPtr->iSelected)
2817 infoPtr->iSelected--;
2819 if (infoPtr->uNumItem == 0)
2820 infoPtr->iSelected = -1;
2822 /* Reposition and repaint tabs */
2823 TAB_SetItemBounds(hwnd);
2832 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2834 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2836 while (infoPtr->uNumItem)
2837 TAB_DeleteItem (hwnd, 0, 0);
2843 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2845 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2848 return (LRESULT)infoPtr->hFont;
2852 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2855 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2857 TRACE("%x %lx\n",wParam, lParam);
2859 infoPtr->hFont = (HFONT)wParam;
2861 TAB_SetItemBounds(hwnd);
2863 TAB_InvalidateTabArea(hwnd, infoPtr);
2870 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2872 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2875 return (LRESULT)infoPtr->himl;
2879 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2881 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2882 HIMAGELIST himlPrev;
2885 himlPrev = infoPtr->himl;
2886 infoPtr->himl= (HIMAGELIST)lParam;
2887 return (LRESULT)himlPrev;
2891 TAB_GetUnicodeFormat (HWND hwnd)
2893 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2894 return infoPtr->bUnicode;
2898 TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
2900 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2901 BOOL bTemp = infoPtr->bUnicode;
2903 infoPtr->bUnicode = (BOOL)wParam;
2909 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2912 /* I'm not really sure what the following code was meant to do.
2913 This is what it is doing:
2914 When WM_SIZE is sent with SIZE_RESTORED, the control
2915 gets positioned in the top left corner.
2919 UINT uPosFlags,cx,cy;
2923 parent = GetParent (hwnd);
2924 GetClientRect(parent, &parent_rect);
2927 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2928 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2930 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2931 cx, cy, uPosFlags | SWP_NOZORDER);
2933 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2936 /* Recompute the size/position of the tabs. */
2937 TAB_SetItemBounds (hwnd);
2939 /* Force a repaint of the control. */
2940 InvalidateRect(hwnd, NULL, TRUE);
2947 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2950 TEXTMETRICA fontMetrics;
2955 infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
2957 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
2959 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
2960 infoPtr->uNumItem = 0;
2961 infoPtr->uNumRows = 0;
2962 infoPtr->uHItemPadding = 6;
2963 infoPtr->uVItemPadding = 3;
2964 infoPtr->uHItemPadding_s = 6;
2965 infoPtr->uVItemPadding_s = 3;
2968 infoPtr->hcurArrow = LoadCursorA (0, (LPSTR)IDC_ARROW);
2969 infoPtr->iSelected = -1;
2970 infoPtr->iHotTracked = -1;
2971 infoPtr->uFocus = -1;
2972 infoPtr->hwndToolTip = 0;
2973 infoPtr->DoRedraw = TRUE;
2974 infoPtr->needsScrolling = FALSE;
2975 infoPtr->hwndUpDown = 0;
2976 infoPtr->leftmostVisible = 0;
2977 infoPtr->fHeightSet = FALSE;
2978 infoPtr->bUnicode = IsWindowUnicode (hwnd);
2979 infoPtr->cbInfo = sizeof(LPARAM);
2981 TRACE("Created tab control, hwnd [%p]\n", hwnd);
2983 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2984 if you don't specify it in CreateWindow. This is necessary in
2985 order for paint to work correctly. This follows windows behaviour. */
2986 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
2987 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2989 if (dwStyle & TCS_TOOLTIPS) {
2990 /* Create tooltip control */
2991 infoPtr->hwndToolTip =
2992 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2993 CW_USEDEFAULT, CW_USEDEFAULT,
2994 CW_USEDEFAULT, CW_USEDEFAULT,
2997 /* Send NM_TOOLTIPSCREATED notification */
2998 if (infoPtr->hwndToolTip) {
2999 NMTOOLTIPSCREATED nmttc;
3001 nmttc.hdr.hwndFrom = hwnd;
3002 nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
3003 nmttc.hdr.code = NM_TOOLTIPSCREATED;
3004 nmttc.hwndToolTips = infoPtr->hwndToolTip;
3006 SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
3007 (WPARAM)GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc);
3012 * We need to get text information so we need a DC and we need to select
3016 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3018 /* Use the system font to determine the initial height of a tab. */
3019 GetTextMetricsA(hdc, &fontMetrics);
3022 * Make sure there is enough space for the letters + growing the
3023 * selected item + extra space for the selected item.
3025 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3026 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
3027 infoPtr->uVItemPadding;
3029 /* Initialize the width of a tab. */
3030 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
3031 infoPtr->tabMinWidth = 0;
3033 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3035 SelectObject (hdc, hOldFont);
3036 ReleaseDC(hwnd, hdc);
3042 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
3044 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3050 if (infoPtr->items) {
3051 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3052 if (TAB_GetItem(infoPtr, iItem)->pszText)
3053 Free (TAB_GetItem(infoPtr, iItem)->pszText);
3055 Free (infoPtr->items);
3058 if (infoPtr->hwndToolTip)
3059 DestroyWindow (infoPtr->hwndToolTip);
3061 if (infoPtr->hwndUpDown)
3062 DestroyWindow(infoPtr->hwndUpDown);
3064 if (infoPtr->iHotTracked >= 0)
3065 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
3068 SetWindowLongA(hwnd, 0, 0);
3073 TAB_SetItemExtra (HWND hwnd, WPARAM wParam, LPARAM lParam)
3075 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3076 INT cbInfo = wParam;
3078 if (!infoPtr || cbInfo <= 0)
3081 if (infoPtr->uNumItem)
3083 /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
3087 infoPtr->cbInfo = cbInfo;
3091 static LRESULT WINAPI
3092 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3094 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3096 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3097 if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
3098 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
3102 case TCM_GETIMAGELIST:
3103 return TAB_GetImageList (hwnd, wParam, lParam);
3105 case TCM_SETIMAGELIST:
3106 return TAB_SetImageList (hwnd, wParam, lParam);
3108 case TCM_GETITEMCOUNT:
3109 return TAB_GetItemCount (hwnd, wParam, lParam);
3113 return TAB_GetItemAW (hwnd, wParam, lParam, uMsg == TCM_GETITEMW);
3117 return TAB_SetItemAW (hwnd, wParam, lParam, uMsg == TCM_SETITEMW);
3119 case TCM_DELETEITEM:
3120 return TAB_DeleteItem (hwnd, wParam, lParam);
3122 case TCM_DELETEALLITEMS:
3123 return TAB_DeleteAllItems (hwnd, wParam, lParam);
3125 case TCM_GETITEMRECT:
3126 return TAB_GetItemRect (hwnd, wParam, lParam);
3129 return TAB_GetCurSel (hwnd);
3132 return TAB_HitTest (hwnd, wParam, lParam);
3135 return TAB_SetCurSel (hwnd, wParam);
3137 case TCM_INSERTITEMA:
3138 case TCM_INSERTITEMW:
3139 return TAB_InsertItemAW (hwnd, wParam, lParam, uMsg == TCM_INSERTITEMW);
3141 case TCM_SETITEMEXTRA:
3142 return TAB_SetItemExtra (hwnd, wParam, lParam);
3144 case TCM_ADJUSTRECT:
3145 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
3147 case TCM_SETITEMSIZE:
3148 return TAB_SetItemSize (hwnd, wParam, lParam);
3150 case TCM_REMOVEIMAGE:
3151 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3154 case TCM_SETPADDING:
3155 return TAB_SetPadding (hwnd, wParam, lParam);
3157 case TCM_GETROWCOUNT:
3158 return TAB_GetRowCount(hwnd);
3160 case TCM_GETUNICODEFORMAT:
3161 return TAB_GetUnicodeFormat (hwnd);
3163 case TCM_SETUNICODEFORMAT:
3164 return TAB_SetUnicodeFormat (hwnd, wParam);
3166 case TCM_HIGHLIGHTITEM:
3167 return TAB_HighlightItem (hwnd, wParam, lParam);
3169 case TCM_GETTOOLTIPS:
3170 return TAB_GetToolTips (hwnd, wParam, lParam);
3172 case TCM_SETTOOLTIPS:
3173 return TAB_SetToolTips (hwnd, wParam, lParam);
3175 case TCM_GETCURFOCUS:
3176 return TAB_GetCurFocus (hwnd);
3178 case TCM_SETCURFOCUS:
3179 return TAB_SetCurFocus (hwnd, wParam);
3181 case TCM_SETMINTABWIDTH:
3182 return TAB_SetMinTabWidth(hwnd, lParam);
3184 case TCM_DESELECTALL:
3185 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3188 case TCM_GETEXTENDEDSTYLE:
3189 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3192 case TCM_SETEXTENDEDSTYLE:
3193 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3197 return TAB_GetFont (hwnd, wParam, lParam);
3200 return TAB_SetFont (hwnd, wParam, lParam);
3203 return TAB_Create (hwnd, wParam, lParam);
3206 return TAB_Destroy (hwnd, wParam, lParam);
3209 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3211 case WM_LBUTTONDOWN:
3212 return TAB_LButtonDown (hwnd, wParam, lParam);
3215 return TAB_LButtonUp (hwnd, wParam, lParam);
3218 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3220 case WM_RBUTTONDOWN:
3221 return TAB_RButtonDown (hwnd, wParam, lParam);
3224 return TAB_MouseMove (hwnd, wParam, lParam);
3227 return TAB_Paint (hwnd, wParam);
3230 return TAB_Size (hwnd, wParam, lParam);
3233 return TAB_SetRedraw (hwnd, wParam);
3236 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3238 case WM_STYLECHANGED:
3239 TAB_SetItemBounds (hwnd);
3240 InvalidateRect(hwnd, NULL, TRUE);
3243 case WM_SYSCOLORCHANGE:
3244 COMCTL32_RefreshSysColors();
3249 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
3252 return TAB_KeyUp(hwnd, wParam);
3254 return TAB_NCHitTest(hwnd, lParam);
3257 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
3258 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3259 uMsg, wParam, lParam);
3260 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
3272 ZeroMemory (&wndClass, sizeof(WNDCLASSW));
3273 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3274 wndClass.lpfnWndProc = TAB_WindowProc;
3275 wndClass.cbClsExtra = 0;
3276 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3277 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3278 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
3279 wndClass.lpszClassName = WC_TABCONTROLW;
3281 RegisterClassW (&wndClass);
3286 TAB_Unregister (void)
3288 UnregisterClassW (WC_TABCONTROLW, NULL);