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);
1662 dis.itemData = (ULONG_PTR)TAB_GetItem(infoPtr, iItem)->extra;
1665 * send the draw message
1667 SendMessageW( infoPtr->hwndNotify, WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1671 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
1675 /* used to center the icon and text in the tab */
1677 INT center_offset_h, center_offset_v;
1679 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1680 rcImage = *drawRect;
1684 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1686 /* get the rectangle that the text fits in */
1689 DrawTextW(hdc, item->pszText, -1, &rcText, DT_CALCRECT);
1692 * If not owner draw, then do the drawing ourselves.
1696 if (infoPtr->himl && (item->mask & TCIF_IMAGE))
1701 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1703 if(lStyle & TCS_VERTICAL)
1705 center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1706 center_offset_v = ((drawRect->right - drawRect->left) - (cx + infoPtr->uVItemPadding)) / 2;
1710 center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1711 center_offset_v = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uVItemPadding)) / 2;
1714 if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1715 center_offset_h = infoPtr->uHItemPadding;
1717 if (center_offset_h < 2)
1718 center_offset_h = 2;
1720 if (center_offset_v < 0)
1721 center_offset_v = 0;
1723 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1724 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1725 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1726 (rcText.right-rcText.left));
1728 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1730 rcImage.top = drawRect->top + center_offset_h;
1731 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1732 /* right side of the tab, but the image still uses the left as its x position */
1733 /* this keeps the image always drawn off of the same side of the tab */
1734 rcImage.left = drawRect->right - cx - center_offset_v;
1735 drawRect->top += cy + infoPtr->uHItemPadding;
1737 else if(lStyle & TCS_VERTICAL)
1739 rcImage.top = drawRect->bottom - cy - center_offset_h;
1740 rcImage.left = drawRect->left + center_offset_v;
1741 drawRect->bottom -= cy + infoPtr->uHItemPadding;
1743 else /* normal style, whether TCS_BOTTOM or not */
1745 rcImage.left = drawRect->left + center_offset_h;
1746 rcImage.top = drawRect->top + center_offset_v;
1747 drawRect->left += cx + infoPtr->uHItemPadding;
1750 TRACE("drawing image=%d, left=%ld, top=%ld\n",
1751 item->iImage, rcImage.left, rcImage.top-1);
1763 /* Now position text */
1764 if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT)
1765 center_offset_h = infoPtr->uHItemPadding;
1767 if(lStyle & TCS_VERTICAL)
1768 center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1770 center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1772 if(lStyle & TCS_VERTICAL)
1774 if(lStyle & TCS_BOTTOM)
1775 drawRect->top+=center_offset_h;
1777 drawRect->bottom-=center_offset_h;
1779 center_offset_v = ((drawRect->right - drawRect->left) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1783 drawRect->left += center_offset_h;
1784 center_offset_v = ((drawRect->bottom - drawRect->top) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1787 if (center_offset_v < 0)
1788 center_offset_v = 0;
1790 if(lStyle & TCS_VERTICAL)
1791 drawRect->left += center_offset_v;
1793 drawRect->top += center_offset_v;
1796 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1800 INT nEscapement = 900;
1801 INT nOrientation = 900;
1803 if(lStyle & TCS_BOTTOM)
1806 nOrientation = -900;
1809 /* to get a font with the escapement and orientation we are looking for, we need to */
1810 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1811 if (!GetObjectA((infoPtr->hFont) ?
1812 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1813 sizeof(LOGFONTA),&logfont))
1817 lstrcpyA(logfont.lfFaceName, "Arial");
1818 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1820 logfont.lfWeight = FW_NORMAL;
1821 logfont.lfItalic = 0;
1822 logfont.lfUnderline = 0;
1823 logfont.lfStrikeOut = 0;
1826 logfont.lfEscapement = nEscapement;
1827 logfont.lfOrientation = nOrientation;
1828 hFont = CreateFontIndirectA(&logfont);
1829 SelectObject(hdc, hFont);
1834 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1835 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1839 lstrlenW(item->pszText),
1843 DeleteObject(hFont);
1847 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1848 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1849 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1850 (rcText.right-rcText.left));
1857 lstrlenW(item->pszText),
1859 DT_LEFT | DT_SINGLELINE
1864 *drawRect = rcTemp; /* restore drawRect */
1870 SelectObject(hdc, hOldFont);
1871 SetBkMode(hdc, oldBkMode);
1872 SelectObject(hdc, holdPen);
1873 DeleteObject( htextPen );
1876 /******************************************************************************
1879 * This method is used to draw a single tab into the tab control.
1881 static void TAB_DrawItem(
1886 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1887 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1891 RECT r, fillRect, r1;
1894 COLORREF bkgnd, corner;
1897 * Get the rectangle for the item.
1899 isVisible = TAB_InternalGetItemRect(hwnd,
1909 /* Clip UpDown control to not draw over it */
1910 if (infoPtr->needsScrolling)
1912 GetWindowRect(hwnd, &rC);
1913 GetWindowRect(infoPtr->hwndUpDown, &rUD);
1914 ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
1917 /* If you need to see what the control is doing,
1918 * then override these variables. They will change what
1919 * fill colors are used for filling the tabs, and the
1920 * corners when drawing the edge.
1922 bkgnd = comctl32_color.clrBtnFace;
1923 corner = comctl32_color.clrBtnFace;
1925 if (lStyle & TCS_BUTTONS)
1927 /* Get item rectangle */
1930 /* Separators between flat buttons */
1931 if (lStyle & TCS_FLATBUTTONS)
1934 r1.right += (FLAT_BTN_SPACINGX -2);
1935 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1938 if (iItem == infoPtr->iSelected)
1940 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1942 OffsetRect(&r, 1, 1);
1944 else /* ! selected */
1946 if (!(lStyle & TCS_FLATBUTTONS))
1947 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1950 else /* !TCS_BUTTONS */
1952 /* We draw a rectangle of different sizes depending on the selection
1954 if (iItem == infoPtr->iSelected) {
1956 GetClientRect (hwnd, &rect);
1957 clRight = rect.right;
1958 clBottom = rect.bottom;
1965 * Erase the background. (Delay it but setup rectangle.)
1966 * This is necessary when drawing the selected item since it is larger
1967 * than the others, it might overlap with stuff already drawn by the
1972 if(lStyle & TCS_VERTICAL)
1974 /* These are for adjusting the drawing of a Selected tab */
1975 /* The initial values are for the normal case of non-Selected */
1976 int ZZ = 1; /* Do not strech if selected */
1977 if (iItem == infoPtr->iSelected) {
1980 /* if leftmost draw the line longer */
1981 if(selectedRect.top == 0)
1982 fillRect.top += CONTROL_BORDER_SIZEY;
1983 /* if rightmost draw the line longer */
1984 if(selectedRect.bottom == clBottom)
1985 fillRect.bottom -= CONTROL_BORDER_SIZEY;
1988 if (lStyle & TCS_BOTTOM)
1990 /* Adjust both rectangles to match native */
1993 TRACE("<right> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1995 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1996 r.left,r.top,r.right,r.bottom);
1998 /* Clear interior */
1999 SetBkColor(hdc, bkgnd);
2000 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2002 /* Draw rectangular edge around tab */
2003 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
2005 /* Now erase the top corner and draw diagonal edge */
2006 SetBkColor(hdc, corner);
2007 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2010 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2011 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2013 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2015 /* Now erase the bottom corner and draw diagonal edge */
2016 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2017 r1.bottom = r.bottom;
2019 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2020 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2022 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2024 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
2028 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
2034 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2036 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2037 r.left,r.top,r.right,r.bottom);
2039 /* Clear interior */
2040 SetBkColor(hdc, bkgnd);
2041 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2043 /* Draw rectangular edge around tab */
2044 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2046 /* Now erase the top corner and draw diagonal edge */
2047 SetBkColor(hdc, corner);
2050 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2051 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2052 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2054 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2056 /* Now erase the bottom corner and draw diagonal edge */
2058 r1.bottom = r.bottom;
2059 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2060 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2061 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2063 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2066 else /* ! TCS_VERTICAL */
2068 /* These are for adjusting the drawing of a Selected tab */
2069 /* The initial values are for the normal case of non-Selected */
2070 if (iItem == infoPtr->iSelected) {
2071 /* if leftmost draw the line longer */
2072 if(selectedRect.left == 0)
2073 fillRect.left += CONTROL_BORDER_SIZEX;
2074 /* if rightmost draw the line longer */
2075 if(selectedRect.right == clRight)
2076 fillRect.right -= CONTROL_BORDER_SIZEX;
2079 if (lStyle & TCS_BOTTOM)
2081 /* Adjust both rectangles for topmost row */
2082 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2088 TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2090 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2091 r.left,r.top,r.right,r.bottom);
2093 /* Clear interior */
2094 SetBkColor(hdc, bkgnd);
2095 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2097 /* Draw rectangular edge around tab */
2098 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2100 /* Now erase the righthand corner and draw diagonal edge */
2101 SetBkColor(hdc, corner);
2102 r1.left = r.right - ROUND_CORNER_SIZE;
2103 r1.bottom = r.bottom;
2105 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2106 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2108 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2110 /* Now erase the lefthand corner and draw diagonal edge */
2112 r1.bottom = r.bottom;
2113 r1.right = r1.left + ROUND_CORNER_SIZE;
2114 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2115 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2117 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2119 if (iItem == infoPtr->iSelected)
2123 if (selectedRect.left == 0)
2128 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2135 /* Adjust both rectangles for bottommost row */
2136 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2138 fillRect.bottom += 3;
2142 TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2144 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2145 r.left,r.top,r.right,r.bottom);
2147 /* Clear interior */
2148 SetBkColor(hdc, bkgnd);
2149 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2151 /* Draw rectangular edge around tab */
2152 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2154 /* Now erase the righthand corner and draw diagonal edge */
2155 SetBkColor(hdc, corner);
2156 r1.left = r.right - ROUND_CORNER_SIZE;
2159 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2160 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2162 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2164 /* Now erase the lefthand corner and draw diagonal edge */
2167 r1.right = r1.left + ROUND_CORNER_SIZE;
2168 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2169 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2171 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2176 TAB_DumpItemInternal(infoPtr, iItem);
2178 /* This modifies r to be the text rectangle. */
2179 TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
2183 /******************************************************************************
2186 * This method is used to draw the raised border around the tab control
2189 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
2191 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2193 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2195 GetClientRect (hwnd, &rect);
2198 * Adjust for the style
2201 if (infoPtr->uNumItem)
2203 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2204 rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2205 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2206 rect.right -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2207 else if(lStyle & TCS_VERTICAL)
2208 rect.left += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2209 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2210 rect.top += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2213 TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2214 rect.left, rect.top, rect.right, rect.bottom);
2216 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2219 /******************************************************************************
2222 * This method repaints the tab control..
2224 static void TAB_Refresh (HWND hwnd, HDC hdc)
2226 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2230 if (!infoPtr->DoRedraw)
2233 hOldFont = SelectObject (hdc, infoPtr->hFont);
2235 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
2237 for (i = 0; i < infoPtr->uNumItem; i++)
2238 TAB_DrawItem (hwnd, hdc, i);
2242 /* Draw all the non selected item first */
2243 for (i = 0; i < infoPtr->uNumItem; i++)
2245 if (i != infoPtr->iSelected)
2246 TAB_DrawItem (hwnd, hdc, i);
2249 /* Now, draw the border, draw it before the selected item
2250 * since the selected item overwrites part of the border. */
2251 TAB_DrawBorder (hwnd, hdc);
2253 /* Then, draw the selected item */
2254 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
2256 /* If we haven't set the current focus yet, set it now.
2257 * Only happens when we first paint the tab controls */
2258 if (infoPtr->uFocus == -1)
2259 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
2262 SelectObject (hdc, hOldFont);
2266 TAB_GetRowCount (HWND hwnd )
2268 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2270 return infoPtr->uNumRows;
2274 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
2276 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2278 infoPtr->DoRedraw=(BOOL) wParam;
2282 /******************************************************************************
2283 * TAB_EnsureSelectionVisible
2285 * This method will make sure that the current selection is completely
2286 * visible by scrolling until it is.
2288 static void TAB_EnsureSelectionVisible(
2292 INT iSelected = infoPtr->iSelected;
2293 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2294 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2296 /* set the items row to the bottommost row or topmost row depending on
2298 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2300 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2304 if(lStyle & TCS_VERTICAL)
2305 newselected = selected->rect.left;
2307 newselected = selected->rect.top;
2309 /* the target row is always (number of rows - 1)
2310 as row 0 is furthest from the clientRect */
2311 iTargetRow = infoPtr->uNumRows - 1;
2313 if (newselected != iTargetRow)
2316 if(lStyle & TCS_VERTICAL)
2318 for (i=0; i < infoPtr->uNumItem; i++)
2320 /* move everything in the row of the selected item to the iTargetRow */
2321 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2323 if (item->rect.left == newselected )
2324 item->rect.left = iTargetRow;
2327 if (item->rect.left > newselected)
2334 for (i=0; i < infoPtr->uNumItem; i++)
2336 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2338 if (item->rect.top == newselected )
2339 item->rect.top = iTargetRow;
2342 if (item->rect.top > newselected)
2347 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2352 * Do the trivial cases first.
2354 if ( (!infoPtr->needsScrolling) ||
2355 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2358 if (infoPtr->leftmostVisible >= iSelected)
2360 infoPtr->leftmostVisible = iSelected;
2364 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2369 /* Calculate the part of the client area that is visible */
2370 GetClientRect(hwnd, &r);
2373 GetClientRect(infoPtr->hwndUpDown, &r);
2376 if ((selected->rect.right -
2377 selected->rect.left) >= width )
2379 /* Special case: width of selected item is greater than visible
2382 infoPtr->leftmostVisible = iSelected;
2386 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2388 if ((selected->rect.right - TAB_GetItem(infoPtr, i)->rect.left) < width)
2391 infoPtr->leftmostVisible = i;
2395 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2396 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2398 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2399 MAKELONG(infoPtr->leftmostVisible, 0));
2402 /******************************************************************************
2403 * TAB_InvalidateTabArea
2405 * This method will invalidate the portion of the control that contains the
2406 * tabs. It is called when the state of the control changes and needs
2409 static void TAB_InvalidateTabArea(
2413 RECT clientRect, rInvalidate, rAdjClient;
2414 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2415 INT lastRow = infoPtr->uNumRows - 1;
2418 if (lastRow < 0) return;
2420 GetClientRect(hwnd, &clientRect);
2421 rInvalidate = clientRect;
2422 rAdjClient = clientRect;
2424 TAB_AdjustRect(hwnd, 0, &rAdjClient);
2426 TAB_InternalGetItemRect(hwnd, infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2427 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2429 rInvalidate.left = rAdjClient.right;
2430 if (infoPtr->uNumRows == 1)
2431 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2433 else if(lStyle & TCS_VERTICAL)
2435 rInvalidate.right = rAdjClient.left;
2436 if (infoPtr->uNumRows == 1)
2437 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2439 else if (lStyle & TCS_BOTTOM)
2441 rInvalidate.top = rAdjClient.bottom;
2442 if (infoPtr->uNumRows == 1)
2443 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2447 rInvalidate.bottom = rAdjClient.top;
2448 if (infoPtr->uNumRows == 1)
2449 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2452 /* Punch out the updown control */
2453 if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2455 GetClientRect(infoPtr->hwndUpDown, &r);
2456 if (rInvalidate.right > clientRect.right - r.left)
2457 rInvalidate.right = rInvalidate.right - (r.right - r.left);
2459 rInvalidate.right = clientRect.right - r.left;
2462 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2463 rInvalidate.left, rInvalidate.top,
2464 rInvalidate.right, rInvalidate.bottom);
2466 InvalidateRect(hwnd, &rInvalidate, TRUE);
2470 TAB_Paint (HWND hwnd, WPARAM wParam)
2477 hdc = BeginPaint (hwnd, &ps);
2478 TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2480 ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2485 TAB_Refresh (hwnd, hdc);
2488 EndPaint (hwnd, &ps);
2494 TAB_InsertItemAW (HWND hwnd, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2496 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2502 GetClientRect (hwnd, &rect);
2503 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2504 rect.top, rect.left, rect.bottom, rect.right);
2506 pti = (TCITEMA *)lParam;
2507 iItem = (INT)wParam;
2509 if (iItem < 0) return -1;
2510 if (iItem > infoPtr->uNumItem)
2511 iItem = infoPtr->uNumItem;
2514 TAB_DumpItemExternalW((TCITEMW*)pti, iItem);
2516 TAB_DumpItemExternalA(pti, iItem);
2519 if (infoPtr->uNumItem == 0) {
2520 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2521 infoPtr->uNumItem++;
2522 infoPtr->iSelected = 0;
2525 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2527 infoPtr->uNumItem++;
2528 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2530 /* pre insert copy */
2532 memcpy (infoPtr->items, oldItems,
2533 iItem * TAB_ITEM_SIZE(infoPtr));
2536 /* post insert copy */
2537 if (iItem < infoPtr->uNumItem - 1) {
2538 memcpy (TAB_GetItem(infoPtr, iItem + 1),
2539 oldItems + iItem * TAB_ITEM_SIZE(infoPtr),
2540 (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2544 if (iItem <= infoPtr->iSelected)
2545 infoPtr->iSelected++;
2550 item = TAB_GetItem(infoPtr, iItem);
2552 item->mask = pti->mask;
2553 item->pszText = NULL;
2555 if (pti->mask & TCIF_TEXT)
2558 Str_SetPtrW (&item->pszText, (WCHAR*)pti->pszText);
2560 Str_SetPtrAtoW (&item->pszText, pti->pszText);
2563 if (pti->mask & TCIF_IMAGE)
2564 item->iImage = pti->iImage;
2568 if (pti->mask & TCIF_PARAM)
2569 memcpy(item->extra, &pti->lParam, infoPtr->cbInfo);
2571 memset(item->extra, 0, infoPtr->cbInfo);
2573 TAB_SetItemBounds(hwnd);
2574 if (infoPtr->uNumItem > 1)
2575 TAB_InvalidateTabArea(hwnd, infoPtr);
2577 InvalidateRect(hwnd, NULL, TRUE);
2579 TRACE("[%p]: added item %d %s\n",
2580 hwnd, iItem, debugstr_w(item->pszText));
2586 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2588 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2589 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2591 BOOL bNeedPaint = FALSE;
2593 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2595 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2596 if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2598 infoPtr->tabWidth = max((INT)LOWORD(lParam), infoPtr->tabMinWidth);
2602 if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2604 if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2605 infoPtr->tabHeight = (INT)HIWORD(lParam);
2609 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2610 HIWORD(lResult), LOWORD(lResult),
2611 infoPtr->tabHeight, infoPtr->tabWidth);
2615 TAB_SetItemBounds(hwnd);
2616 RedrawWindow(hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2623 TAB_SetMinTabWidth (HWND hwnd, LPARAM lParam)
2625 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2626 INT cx = (INT)lParam;
2630 oldcx = infoPtr->tabMinWidth;
2631 infoPtr->tabMinWidth = (cx==-1)?DEFAULT_TAB_WIDTH:cx;
2639 TAB_HighlightItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2641 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2642 INT iItem = (INT)wParam;
2643 BOOL fHighlight = (BOOL)LOWORD(lParam);
2645 if ((infoPtr) && (iItem>=0) && (iItem<infoPtr->uNumItem)) {
2647 TAB_GetItem(infoPtr, iItem)->dwState |= TCIS_HIGHLIGHTED;
2649 TAB_GetItem(infoPtr, iItem)->dwState &= ~TCIS_HIGHLIGHTED;
2657 TAB_SetItemAW (HWND hwnd, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2659 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2664 iItem = (INT)wParam;
2665 tabItem = (LPTCITEMA)lParam;
2667 TRACE("%d %p\n", iItem, tabItem);
2668 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2671 TAB_DumpItemExternalW((TCITEMW *)tabItem, iItem);
2673 TAB_DumpItemExternalA(tabItem, iItem);
2675 wineItem = TAB_GetItem(infoPtr, iItem);
2677 if (tabItem->mask & TCIF_IMAGE)
2678 wineItem->iImage = tabItem->iImage;
2680 if (tabItem->mask & TCIF_PARAM)
2681 memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2683 if (tabItem->mask & TCIF_RTLREADING)
2684 FIXME("TCIF_RTLREADING\n");
2686 if (tabItem->mask & TCIF_STATE)
2687 wineItem->dwState = tabItem->dwState;
2689 if (tabItem->mask & TCIF_TEXT)
2691 if (wineItem->pszText)
2693 Free(wineItem->pszText);
2694 wineItem->pszText = NULL;
2697 Str_SetPtrW(&wineItem->pszText, (WCHAR*)tabItem->pszText);
2699 Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2702 /* Update and repaint tabs */
2703 TAB_SetItemBounds(hwnd);
2704 TAB_InvalidateTabArea(hwnd,infoPtr);
2710 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2712 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2714 return infoPtr->uNumItem;
2719 TAB_GetItemAW (HWND hwnd, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2721 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2726 iItem = (INT)wParam;
2727 tabItem = (LPTCITEMA)lParam;
2729 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2732 wineItem = TAB_GetItem(infoPtr, iItem);
2734 if (tabItem->mask & TCIF_IMAGE)
2735 tabItem->iImage = wineItem->iImage;
2737 if (tabItem->mask & TCIF_PARAM)
2738 memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2740 if (tabItem->mask & TCIF_RTLREADING)
2741 FIXME("TCIF_RTLREADING\n");
2743 if (tabItem->mask & TCIF_STATE)
2744 tabItem->dwState = wineItem->dwState;
2746 if (tabItem->mask & TCIF_TEXT)
2749 Str_GetPtrW (wineItem->pszText, (WCHAR*)tabItem->pszText, tabItem->cchTextMax);
2751 Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2755 TAB_DumpItemExternalW((TCITEMW*)tabItem, iItem);
2757 TAB_DumpItemExternalA(tabItem, iItem);
2764 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2766 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2767 INT iItem = (INT) wParam;
2768 BOOL bResult = FALSE;
2770 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2772 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
2773 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2775 TAB_InvalidateTabArea(hwnd, infoPtr);
2777 if ((item->mask & TCIF_TEXT) && item->pszText)
2778 Free(item->pszText);
2780 infoPtr->uNumItem--;
2782 if (!infoPtr->uNumItem)
2784 infoPtr->items = NULL;
2785 if (infoPtr->iHotTracked >= 0)
2787 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2788 infoPtr->iHotTracked = -1;
2793 infoPtr->items = Alloc(TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2796 memcpy(infoPtr->items, oldItems, iItem * TAB_ITEM_SIZE(infoPtr));
2798 if (iItem < infoPtr->uNumItem)
2799 memcpy(TAB_GetItem(infoPtr, iItem),
2800 oldItems + (iItem + 1) * TAB_ITEM_SIZE(infoPtr),
2801 (infoPtr->uNumItem - iItem) * TAB_ITEM_SIZE(infoPtr));
2803 if (iItem <= infoPtr->iHotTracked)
2805 /* When tabs move left/up, the hot track item may change */
2806 FIXME("Recalc hot track");
2811 /* Readjust the selected index */
2812 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2813 infoPtr->iSelected--;
2815 if (iItem < infoPtr->iSelected)
2816 infoPtr->iSelected--;
2818 if (infoPtr->uNumItem == 0)
2819 infoPtr->iSelected = -1;
2821 /* Reposition and repaint tabs */
2822 TAB_SetItemBounds(hwnd);
2831 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2833 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2835 while (infoPtr->uNumItem)
2836 TAB_DeleteItem (hwnd, 0, 0);
2842 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2844 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2847 return (LRESULT)infoPtr->hFont;
2851 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2854 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2856 TRACE("%x %lx\n",wParam, lParam);
2858 infoPtr->hFont = (HFONT)wParam;
2860 TAB_SetItemBounds(hwnd);
2862 TAB_InvalidateTabArea(hwnd, infoPtr);
2869 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2871 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2874 return (LRESULT)infoPtr->himl;
2878 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2880 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2881 HIMAGELIST himlPrev;
2884 himlPrev = infoPtr->himl;
2885 infoPtr->himl= (HIMAGELIST)lParam;
2886 return (LRESULT)himlPrev;
2890 TAB_GetUnicodeFormat (HWND hwnd)
2892 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2893 return infoPtr->bUnicode;
2897 TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
2899 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2900 BOOL bTemp = infoPtr->bUnicode;
2902 infoPtr->bUnicode = (BOOL)wParam;
2908 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2911 /* I'm not really sure what the following code was meant to do.
2912 This is what it is doing:
2913 When WM_SIZE is sent with SIZE_RESTORED, the control
2914 gets positioned in the top left corner.
2918 UINT uPosFlags,cx,cy;
2922 parent = GetParent (hwnd);
2923 GetClientRect(parent, &parent_rect);
2926 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2927 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2929 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2930 cx, cy, uPosFlags | SWP_NOZORDER);
2932 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2935 /* Recompute the size/position of the tabs. */
2936 TAB_SetItemBounds (hwnd);
2938 /* Force a repaint of the control. */
2939 InvalidateRect(hwnd, NULL, TRUE);
2946 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2949 TEXTMETRICA fontMetrics;
2954 infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
2956 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
2958 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
2959 infoPtr->uNumItem = 0;
2960 infoPtr->uNumRows = 0;
2961 infoPtr->uHItemPadding = 6;
2962 infoPtr->uVItemPadding = 3;
2963 infoPtr->uHItemPadding_s = 6;
2964 infoPtr->uVItemPadding_s = 3;
2967 infoPtr->hcurArrow = LoadCursorA (0, (LPSTR)IDC_ARROW);
2968 infoPtr->iSelected = -1;
2969 infoPtr->iHotTracked = -1;
2970 infoPtr->uFocus = -1;
2971 infoPtr->hwndToolTip = 0;
2972 infoPtr->DoRedraw = TRUE;
2973 infoPtr->needsScrolling = FALSE;
2974 infoPtr->hwndUpDown = 0;
2975 infoPtr->leftmostVisible = 0;
2976 infoPtr->fHeightSet = FALSE;
2977 infoPtr->bUnicode = IsWindowUnicode (hwnd);
2978 infoPtr->cbInfo = sizeof(LPARAM);
2980 TRACE("Created tab control, hwnd [%p]\n", hwnd);
2982 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2983 if you don't specify it in CreateWindow. This is necessary in
2984 order for paint to work correctly. This follows windows behaviour. */
2985 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
2986 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2988 if (dwStyle & TCS_TOOLTIPS) {
2989 /* Create tooltip control */
2990 infoPtr->hwndToolTip =
2991 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2992 CW_USEDEFAULT, CW_USEDEFAULT,
2993 CW_USEDEFAULT, CW_USEDEFAULT,
2996 /* Send NM_TOOLTIPSCREATED notification */
2997 if (infoPtr->hwndToolTip) {
2998 NMTOOLTIPSCREATED nmttc;
3000 nmttc.hdr.hwndFrom = hwnd;
3001 nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
3002 nmttc.hdr.code = NM_TOOLTIPSCREATED;
3003 nmttc.hwndToolTips = infoPtr->hwndToolTip;
3005 SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
3006 (WPARAM)GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc);
3011 * We need to get text information so we need a DC and we need to select
3015 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3017 /* Use the system font to determine the initial height of a tab. */
3018 GetTextMetricsA(hdc, &fontMetrics);
3021 * Make sure there is enough space for the letters + growing the
3022 * selected item + extra space for the selected item.
3024 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3025 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
3026 infoPtr->uVItemPadding;
3028 /* Initialize the width of a tab. */
3029 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
3030 infoPtr->tabMinWidth = 0;
3032 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3034 SelectObject (hdc, hOldFont);
3035 ReleaseDC(hwnd, hdc);
3041 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
3043 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3049 if (infoPtr->items) {
3050 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3051 if (TAB_GetItem(infoPtr, iItem)->pszText)
3052 Free (TAB_GetItem(infoPtr, iItem)->pszText);
3054 Free (infoPtr->items);
3057 if (infoPtr->hwndToolTip)
3058 DestroyWindow (infoPtr->hwndToolTip);
3060 if (infoPtr->hwndUpDown)
3061 DestroyWindow(infoPtr->hwndUpDown);
3063 if (infoPtr->iHotTracked >= 0)
3064 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
3067 SetWindowLongA(hwnd, 0, 0);
3072 TAB_SetItemExtra (HWND hwnd, WPARAM wParam, LPARAM lParam)
3074 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3075 INT cbInfo = wParam;
3077 if (!infoPtr || cbInfo <= 0)
3080 if (infoPtr->uNumItem)
3082 /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
3086 infoPtr->cbInfo = cbInfo;
3090 static LRESULT WINAPI
3091 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3093 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3095 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3096 if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
3097 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
3101 case TCM_GETIMAGELIST:
3102 return TAB_GetImageList (hwnd, wParam, lParam);
3104 case TCM_SETIMAGELIST:
3105 return TAB_SetImageList (hwnd, wParam, lParam);
3107 case TCM_GETITEMCOUNT:
3108 return TAB_GetItemCount (hwnd, wParam, lParam);
3112 return TAB_GetItemAW (hwnd, wParam, lParam, uMsg == TCM_GETITEMW);
3116 return TAB_SetItemAW (hwnd, wParam, lParam, uMsg == TCM_SETITEMW);
3118 case TCM_DELETEITEM:
3119 return TAB_DeleteItem (hwnd, wParam, lParam);
3121 case TCM_DELETEALLITEMS:
3122 return TAB_DeleteAllItems (hwnd, wParam, lParam);
3124 case TCM_GETITEMRECT:
3125 return TAB_GetItemRect (hwnd, wParam, lParam);
3128 return TAB_GetCurSel (hwnd);
3131 return TAB_HitTest (hwnd, wParam, lParam);
3134 return TAB_SetCurSel (hwnd, wParam);
3136 case TCM_INSERTITEMA:
3137 case TCM_INSERTITEMW:
3138 return TAB_InsertItemAW (hwnd, wParam, lParam, uMsg == TCM_INSERTITEMW);
3140 case TCM_SETITEMEXTRA:
3141 return TAB_SetItemExtra (hwnd, wParam, lParam);
3143 case TCM_ADJUSTRECT:
3144 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
3146 case TCM_SETITEMSIZE:
3147 return TAB_SetItemSize (hwnd, wParam, lParam);
3149 case TCM_REMOVEIMAGE:
3150 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3153 case TCM_SETPADDING:
3154 return TAB_SetPadding (hwnd, wParam, lParam);
3156 case TCM_GETROWCOUNT:
3157 return TAB_GetRowCount(hwnd);
3159 case TCM_GETUNICODEFORMAT:
3160 return TAB_GetUnicodeFormat (hwnd);
3162 case TCM_SETUNICODEFORMAT:
3163 return TAB_SetUnicodeFormat (hwnd, wParam);
3165 case TCM_HIGHLIGHTITEM:
3166 return TAB_HighlightItem (hwnd, wParam, lParam);
3168 case TCM_GETTOOLTIPS:
3169 return TAB_GetToolTips (hwnd, wParam, lParam);
3171 case TCM_SETTOOLTIPS:
3172 return TAB_SetToolTips (hwnd, wParam, lParam);
3174 case TCM_GETCURFOCUS:
3175 return TAB_GetCurFocus (hwnd);
3177 case TCM_SETCURFOCUS:
3178 return TAB_SetCurFocus (hwnd, wParam);
3180 case TCM_SETMINTABWIDTH:
3181 return TAB_SetMinTabWidth(hwnd, lParam);
3183 case TCM_DESELECTALL:
3184 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3187 case TCM_GETEXTENDEDSTYLE:
3188 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3191 case TCM_SETEXTENDEDSTYLE:
3192 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3196 return TAB_GetFont (hwnd, wParam, lParam);
3199 return TAB_SetFont (hwnd, wParam, lParam);
3202 return TAB_Create (hwnd, wParam, lParam);
3205 return TAB_Destroy (hwnd, wParam, lParam);
3208 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3210 case WM_LBUTTONDOWN:
3211 return TAB_LButtonDown (hwnd, wParam, lParam);
3214 return TAB_LButtonUp (hwnd, wParam, lParam);
3217 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3219 case WM_RBUTTONDOWN:
3220 return TAB_RButtonDown (hwnd, wParam, lParam);
3223 return TAB_MouseMove (hwnd, wParam, lParam);
3226 return TAB_Paint (hwnd, wParam);
3229 return TAB_Size (hwnd, wParam, lParam);
3232 return TAB_SetRedraw (hwnd, wParam);
3235 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3237 case WM_STYLECHANGED:
3238 TAB_SetItemBounds (hwnd);
3239 InvalidateRect(hwnd, NULL, TRUE);
3242 case WM_SYSCOLORCHANGE:
3243 COMCTL32_RefreshSysColors();
3248 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
3251 return TAB_KeyUp(hwnd, wParam);
3253 return TAB_NCHitTest(hwnd, lParam);
3256 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
3257 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3258 uMsg, wParam, lParam);
3259 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
3271 ZeroMemory (&wndClass, sizeof(WNDCLASSW));
3272 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3273 wndClass.lpfnWndProc = TAB_WindowProc;
3274 wndClass.cbClsExtra = 0;
3275 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3276 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3277 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
3278 wndClass.lpszClassName = WC_TABCONTROLW;
3280 RegisterClassW (&wndClass);
3285 TAB_Unregister (void)
3287 UnregisterClassW (WC_TABCONTROLW, NULL);