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 holds 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 hwnd; /* Tab control window */
73 HWND hwndNotify; /* notification window (parent) */
74 UINT uNumItem; /* number of tab items */
75 UINT uNumRows; /* number of tab rows */
76 INT tabHeight; /* height of the tab row */
77 INT tabWidth; /* width of tabs */
78 INT tabMinWidth; /* minimum width of items */
79 USHORT uHItemPadding; /* amount of horizontal padding, in pixels */
80 USHORT uVItemPadding; /* amount of vertical padding, in pixels */
81 USHORT uHItemPadding_s; /* Set amount of horizontal padding, in pixels */
82 USHORT uVItemPadding_s; /* Set amount of vertical padding, in pixels */
83 HFONT hFont; /* handle to the current font */
84 HCURSOR hcurArrow; /* handle to the current cursor */
85 HIMAGELIST himl; /* handle to a image list (may be 0) */
86 HWND hwndToolTip; /* handle to tab's tooltip */
87 INT leftmostVisible; /* Used for scrolling, this member contains
88 * the index of the first visible item */
89 INT iSelected; /* the currently selected item */
90 INT iHotTracked; /* the highlighted item under the mouse */
91 INT uFocus; /* item which has the focus */
92 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
93 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
94 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
95 * the size of the control */
96 BOOL fHeightSet; /* was the height of the tabs explicitly set? */
97 BOOL bUnicode; /* Unicode control? */
98 HWND hwndUpDown; /* Updown control used for scrolling */
99 INT cbInfo; /* Number of bytes of caller supplied info per tab */
102 /******************************************************************************
103 * Positioning constants
105 #define SELECTED_TAB_OFFSET 2
106 #define ROUND_CORNER_SIZE 2
107 #define DISPLAY_AREA_PADDINGX 2
108 #define DISPLAY_AREA_PADDINGY 2
109 #define CONTROL_BORDER_SIZEX 2
110 #define CONTROL_BORDER_SIZEY 2
111 #define BUTTON_SPACINGX 3
112 #define BUTTON_SPACINGY 3
113 #define FLAT_BTN_SPACINGX 8
114 #define DEFAULT_TAB_WIDTH 96
116 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongPtrW(hwnd,0))
117 /* Since items are variable sized, cannot directly access them */
118 #define TAB_GetItem(info,i) \
119 ((TAB_ITEM*)((LPBYTE)info->items + (i) * TAB_ITEM_SIZE(info)))
121 /******************************************************************************
122 * Hot-tracking timer constants
124 #define TAB_HOTTRACK_TIMER 1
125 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
127 /******************************************************************************
130 static void TAB_InvalidateTabArea(TAB_INFO *);
131 static void TAB_EnsureSelectionVisible(TAB_INFO *);
132 static void TAB_DrawItemInterior(TAB_INFO *, HDC, INT, RECT*);
135 TAB_SendSimpleNotify (const TAB_INFO *infoPtr, UINT code)
139 nmhdr.hwndFrom = infoPtr->hwnd;
140 nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID);
143 return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
144 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
148 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
149 WPARAM wParam, LPARAM lParam)
157 msg.time = GetMessageTime ();
158 msg.pt.x = LOWORD(GetMessagePos ());
159 msg.pt.y = HIWORD(GetMessagePos ());
161 SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
165 TAB_DumpItemExternalA(TCITEMA *pti, UINT iItem)
168 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
169 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
170 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextA=%s\n",
171 iItem, pti->iImage, pti->lParam, debugstr_a(pti->pszText));
177 TAB_DumpItemExternalW(TCITEMW *pti, UINT iItem)
180 TRACE("external tab %d, mask=0x%08x, dwState=0x%08lx, dwStateMask=0x%08lx, cchTextMax=0x%08x\n",
181 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
182 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
183 iItem, pti->iImage, pti->lParam, debugstr_w(pti->pszText));
188 TAB_DumpItemInternal(TAB_INFO *infoPtr, UINT iItem)
193 ti = TAB_GetItem(infoPtr, iItem);
194 TRACE("tab %d, mask=0x%08x, dwState=0x%08lx, pszText=%s, iImage=%d\n",
195 iItem, ti->mask, ti->dwState, debugstr_w(ti->pszText),
197 TRACE("tab %d, rect.left=%ld, rect.top(row)=%ld\n",
198 iItem, ti->rect.left, ti->rect.top);
203 * the index of the selected tab, or -1 if no tab is selected. */
204 static inline LRESULT TAB_GetCurSel (const TAB_INFO *infoPtr)
206 return infoPtr->iSelected;
210 * the index of the tab item that has the focus
212 * we have not to return negative value
214 * test for windows */
215 static inline LRESULT
216 TAB_GetCurFocus (const TAB_INFO *infoPtr)
218 if (infoPtr->uFocus<0)
220 FIXME("we have not to return negative value");
223 return infoPtr->uFocus;
226 static inline LRESULT TAB_GetToolTips (const TAB_INFO *infoPtr)
228 if (infoPtr == NULL) return 0;
229 return (LRESULT)infoPtr->hwndToolTip;
232 static inline LRESULT TAB_SetCurSel (TAB_INFO *infoPtr, INT iItem)
236 if (iItem >= 0 && iItem < infoPtr->uNumItem) {
237 prevItem=infoPtr->iSelected;
238 infoPtr->iSelected=iItem;
239 TAB_EnsureSelectionVisible(infoPtr);
240 TAB_InvalidateTabArea(infoPtr);
245 static LRESULT TAB_SetCurFocus (TAB_INFO *infoPtr, INT iItem)
247 if (iItem < 0 || iItem >= infoPtr->uNumItem) return 0;
249 if (GetWindowLongA(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS) {
250 FIXME("Should set input focus\n");
252 int oldFocus = infoPtr->uFocus;
253 if (infoPtr->iSelected != iItem || oldFocus == -1 ) {
254 infoPtr->uFocus = iItem;
255 if (oldFocus != -1) {
256 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING)) {
257 infoPtr->iSelected = iItem;
258 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
261 infoPtr->iSelected = iItem;
262 TAB_EnsureSelectionVisible(infoPtr);
263 TAB_InvalidateTabArea(infoPtr);
270 static inline LRESULT
271 TAB_SetToolTips (TAB_INFO *infoPtr, HWND hwndToolTip)
274 infoPtr->hwndToolTip = hwndToolTip;
278 static inline LRESULT
279 TAB_SetPadding (TAB_INFO *infoPtr, LPARAM lParam)
283 infoPtr->uHItemPadding_s=LOWORD(lParam);
284 infoPtr->uVItemPadding_s=HIWORD(lParam);
289 /******************************************************************************
290 * TAB_InternalGetItemRect
292 * This method will calculate the rectangle representing a given tab item in
293 * client coordinates. This method takes scrolling into account.
295 * This method returns TRUE if the item is visible in the window and FALSE
296 * if it is completely outside the client area.
298 static BOOL TAB_InternalGetItemRect(
299 const TAB_INFO* infoPtr,
304 RECT tmpItemRect,clientRect;
305 LONG lStyle = GetWindowLongA(infoPtr->hwnd, GWL_STYLE);
307 /* Perform a sanity check and a trivial visibility check. */
308 if ( (infoPtr->uNumItem <= 0) ||
309 (itemIndex >= infoPtr->uNumItem) ||
310 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
314 * Avoid special cases in this procedure by assigning the "out"
315 * parameters if the caller didn't supply them
317 if (itemRect == NULL)
318 itemRect = &tmpItemRect;
320 /* Retrieve the unmodified item rect. */
321 *itemRect = TAB_GetItem(infoPtr,itemIndex)->rect;
323 /* calculate the times bottom and top based on the row */
324 GetClientRect(infoPtr->hwnd, &clientRect);
326 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
328 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * infoPtr->tabHeight -
329 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
330 itemRect->left = itemRect->right - infoPtr->tabHeight;
332 else if (lStyle & TCS_VERTICAL)
334 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * infoPtr->tabHeight +
335 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
336 itemRect->right = itemRect->left + infoPtr->tabHeight;
338 else if (lStyle & TCS_BOTTOM)
340 itemRect->bottom = clientRect.bottom - itemRect->top * infoPtr->tabHeight -
341 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
342 itemRect->top = itemRect->bottom - infoPtr->tabHeight;
344 else /* not TCS_BOTTOM and not TCS_VERTICAL */
346 itemRect->top = clientRect.top + itemRect->top * infoPtr->tabHeight +
347 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
348 itemRect->bottom = itemRect->top + infoPtr->tabHeight;
352 * "scroll" it to make sure the item at the very left of the
353 * tab control is the leftmost visible tab.
355 if(lStyle & TCS_VERTICAL)
359 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.top);
362 * Move the rectangle so the first item is slightly offset from
363 * the bottom of the tab control.
367 SELECTED_TAB_OFFSET);
372 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.left,
376 * Move the rectangle so the first item is slightly offset from
377 * the left of the tab control.
383 TRACE("item %d tab h=%d, rect=(%ld,%ld)-(%ld,%ld)\n",
384 itemIndex, infoPtr->tabHeight,
385 itemRect->left, itemRect->top, itemRect->right, itemRect->bottom);
387 /* Now, calculate the position of the item as if it were selected. */
388 if (selectedRect!=NULL)
390 CopyRect(selectedRect, itemRect);
392 /* The rectangle of a selected item is a bit wider. */
393 if(lStyle & TCS_VERTICAL)
394 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
396 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
398 /* If it also a bit higher. */
399 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
401 selectedRect->left -= 2; /* the border is thicker on the right */
402 selectedRect->right += SELECTED_TAB_OFFSET;
404 else if (lStyle & TCS_VERTICAL)
406 selectedRect->left -= SELECTED_TAB_OFFSET;
407 selectedRect->right += 1;
409 else if (lStyle & TCS_BOTTOM)
411 selectedRect->bottom += SELECTED_TAB_OFFSET;
413 else /* not TCS_BOTTOM and not TCS_VERTICAL */
415 selectedRect->top -= SELECTED_TAB_OFFSET;
416 selectedRect->bottom -= 1;
424 TAB_GetItemRect(TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
426 return TAB_InternalGetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam, (LPRECT)NULL);
429 /******************************************************************************
432 * This method is called to handle keyboard input
434 static LRESULT TAB_KeyUp(TAB_INFO* infoPtr, WPARAM keyCode)
441 newItem = infoPtr->uFocus - 1;
444 newItem = infoPtr->uFocus + 1;
449 * If we changed to a valid item, change the selection
452 newItem < infoPtr->uNumItem &&
453 infoPtr->uFocus != newItem)
455 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
457 infoPtr->iSelected = newItem;
458 infoPtr->uFocus = newItem;
459 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
461 TAB_EnsureSelectionVisible(infoPtr);
462 TAB_InvalidateTabArea(infoPtr);
469 /******************************************************************************
472 * This method is called whenever the focus goes in or out of this control
473 * it is used to update the visual state of the control.
475 static VOID TAB_FocusChanging(const TAB_INFO *infoPtr)
481 * Get the rectangle for the item.
483 isVisible = TAB_InternalGetItemRect(infoPtr,
489 * If the rectangle is not completely invisible, invalidate that
490 * portion of the window.
494 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
495 selectedRect.left,selectedRect.top,
496 selectedRect.right,selectedRect.bottom);
497 InvalidateRect(infoPtr->hwnd, &selectedRect, TRUE);
501 static INT TAB_InternalHitTest (
510 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
512 TAB_InternalGetItemRect(infoPtr, iCount, &rect, NULL);
514 if (PtInRect(&rect, pt))
516 *flags = TCHT_ONITEM;
521 *flags = TCHT_NOWHERE;
525 static inline LRESULT
526 TAB_HitTest (TAB_INFO *infoPtr, LPTCHITTESTINFO lptest)
528 return TAB_InternalHitTest (infoPtr, lptest->pt, &lptest->flags);
531 /******************************************************************************
534 * Napster v2b5 has a tab control for its main navigation which has a client
535 * area that covers the whole area of the dialog pages.
536 * That's why it receives all msgs for that area and the underlying dialog ctrls
538 * So I decided that we should handle WM_NCHITTEST here and return
539 * HTTRANSPARENT if we don't hit the tab control buttons.
540 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
541 * doesn't do it that way. Maybe depends on tab control styles ?
543 static inline LRESULT
544 TAB_NCHitTest (TAB_INFO *infoPtr, LPARAM lParam)
549 pt.x = LOWORD(lParam);
550 pt.y = HIWORD(lParam);
551 ScreenToClient(infoPtr->hwnd, &pt);
553 if (TAB_InternalHitTest(infoPtr, pt, &dummyflag) == -1)
554 return HTTRANSPARENT;
560 TAB_LButtonDown (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
565 if (infoPtr->hwndToolTip)
566 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
567 WM_LBUTTONDOWN, wParam, lParam);
569 if (GetWindowLongA(infoPtr->hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
570 SetFocus (infoPtr->hwnd);
573 if (infoPtr->hwndToolTip)
574 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
575 WM_LBUTTONDOWN, wParam, lParam);
577 pt.x = (INT)LOWORD(lParam);
578 pt.y = (INT)HIWORD(lParam);
580 newItem = TAB_InternalHitTest (infoPtr, pt, &dummy);
582 TRACE("On Tab, item %d\n", newItem);
584 if (newItem != -1 && infoPtr->iSelected != newItem)
586 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
588 infoPtr->iSelected = newItem;
589 infoPtr->uFocus = newItem;
590 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
592 TAB_EnsureSelectionVisible(infoPtr);
594 TAB_InvalidateTabArea(infoPtr);
600 static inline LRESULT
601 TAB_LButtonUp (const TAB_INFO *infoPtr)
603 TAB_SendSimpleNotify(infoPtr, NM_CLICK);
608 static inline LRESULT
609 TAB_RButtonDown (const TAB_INFO *infoPtr)
611 TAB_SendSimpleNotify(infoPtr, NM_RCLICK);
615 /******************************************************************************
616 * TAB_DrawLoneItemInterior
618 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
619 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
620 * up the device context and font. This routine does the same setup but
621 * only calls TAB_DrawItemInterior for the single specified item.
624 TAB_DrawLoneItemInterior(TAB_INFO* infoPtr, int iItem)
626 HDC hdc = GetDC(infoPtr->hwnd);
629 /* Clip UpDown control to not draw over it */
630 if (infoPtr->needsScrolling)
632 GetWindowRect(infoPtr->hwnd, &rC);
633 GetWindowRect(infoPtr->hwndUpDown, &r);
634 ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top);
636 TAB_DrawItemInterior(infoPtr, hdc, iItem, NULL);
637 ReleaseDC(infoPtr->hwnd, hdc);
640 /******************************************************************************
641 * TAB_HotTrackTimerProc
643 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
644 * timer is setup so we can check if the mouse is moved out of our window.
645 * (We don't get an event when the mouse leaves, the mouse-move events just
646 * stop being delivered to our window and just start being delivered to
647 * another window.) This function is called when the timer triggers so
648 * we can check if the mouse has left our window. If so, we un-highlight
649 * the hot-tracked tab.
652 TAB_HotTrackTimerProc
654 HWND hwnd, /* handle of window for timer messages */
655 UINT uMsg, /* WM_TIMER message */
656 UINT idEvent, /* timer identifier */
657 DWORD dwTime /* current system time */
660 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
662 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
667 ** If we can't get the cursor position, or if the cursor is outside our
668 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
669 ** "outside" even if it is within our bounding rect if another window
670 ** overlaps. Note also that the case where the cursor stayed within our
671 ** window but has moved off the hot-tracked tab will be handled by the
672 ** WM_MOUSEMOVE event.
674 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
676 /* Redraw iHotTracked to look normal */
677 INT iRedraw = infoPtr->iHotTracked;
678 infoPtr->iHotTracked = -1;
679 TAB_DrawLoneItemInterior(infoPtr, iRedraw);
681 /* Kill this timer */
682 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
687 /******************************************************************************
690 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
691 * should be highlighted. This function determines which tab in a tab control,
692 * if any, is under the mouse and records that information. The caller may
693 * supply output parameters to receive the item number of the tab item which
694 * was highlighted but isn't any longer and of the tab item which is now
695 * highlighted but wasn't previously. The caller can use this information to
696 * selectively redraw those tab items.
698 * If the caller has a mouse position, it can supply it through the pos
699 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
700 * supplies NULL and this function determines the current mouse position
708 int* out_redrawLeave,
715 if (out_redrawLeave != NULL)
716 *out_redrawLeave = -1;
717 if (out_redrawEnter != NULL)
718 *out_redrawEnter = -1;
720 if (GetWindowLongA(infoPtr->hwnd, GWL_STYLE) & TCS_HOTTRACK)
728 ScreenToClient(infoPtr->hwnd, &pt);
736 item = TAB_InternalHitTest(infoPtr, pt, &flags);
739 if (item != infoPtr->iHotTracked)
741 if (infoPtr->iHotTracked >= 0)
743 /* Mark currently hot-tracked to be redrawn to look normal */
744 if (out_redrawLeave != NULL)
745 *out_redrawLeave = infoPtr->iHotTracked;
749 /* Kill timer which forces recheck of mouse pos */
750 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
755 /* Start timer so we recheck mouse pos */
756 UINT timerID = SetTimer
760 TAB_HOTTRACK_TIMER_INTERVAL,
761 TAB_HotTrackTimerProc
765 return; /* Hot tracking not available */
768 infoPtr->iHotTracked = item;
772 /* Mark new hot-tracked to be redrawn to look highlighted */
773 if (out_redrawEnter != NULL)
774 *out_redrawEnter = item;
779 /******************************************************************************
782 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
785 TAB_MouseMove (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
790 if (infoPtr->hwndToolTip)
791 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
792 WM_LBUTTONDOWN, wParam, lParam);
794 /* Determine which tab to highlight. Redraw tabs which change highlight
796 TAB_RecalcHotTrack(infoPtr, &lParam, &redrawLeave, &redrawEnter);
798 if (redrawLeave != -1)
799 TAB_DrawLoneItemInterior(infoPtr, redrawLeave);
800 if (redrawEnter != -1)
801 TAB_DrawLoneItemInterior(infoPtr, redrawEnter);
806 /******************************************************************************
809 * Calculates the tab control's display area given the window rectangle or
810 * the window rectangle given the requested display rectangle.
812 static LRESULT TAB_AdjustRect(
817 DWORD lStyle = GetWindowLongA(infoPtr->hwnd, GWL_STYLE);
818 LONG *iRightBottom, *iLeftTop;
820 TRACE ("hwnd=%p fLarger=%d (%ld,%ld)-(%ld,%ld)\n", infoPtr->hwnd, fLarger, prc->left, prc->top, prc->right, prc->bottom);
822 if(lStyle & TCS_VERTICAL)
824 iRightBottom = &(prc->right);
825 iLeftTop = &(prc->left);
829 iRightBottom = &(prc->bottom);
830 iLeftTop = &(prc->top);
833 if (fLarger) /* Go from display rectangle */
835 /* Add the height of the tabs. */
836 if (lStyle & TCS_BOTTOM)
837 *iRightBottom += infoPtr->tabHeight * infoPtr->uNumRows;
839 *iLeftTop -= infoPtr->tabHeight * infoPtr->uNumRows +
840 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
842 /* Inflate the rectangle for the padding */
843 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
845 /* Inflate for the border */
846 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEY);
848 else /* Go from window rectangle. */
850 /* Deflate the rectangle for the border */
851 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEY);
853 /* Deflate the rectangle for the padding */
854 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
856 /* Remove the height of the tabs. */
857 if (lStyle & TCS_BOTTOM)
858 *iRightBottom -= infoPtr->tabHeight * infoPtr->uNumRows;
860 *iLeftTop += (infoPtr->tabHeight) * infoPtr->uNumRows +
861 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
867 /******************************************************************************
870 * This method will handle the notification from the scroll control and
871 * perform the scrolling operation on the tab control.
873 static LRESULT TAB_OnHScroll(
879 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
881 if(nPos < infoPtr->leftmostVisible)
882 infoPtr->leftmostVisible--;
884 infoPtr->leftmostVisible++;
886 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
887 TAB_InvalidateTabArea(infoPtr);
888 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
889 MAKELONG(infoPtr->leftmostVisible, 0));
895 /******************************************************************************
898 * This method will check the current scrolling state and make sure the
899 * scrolling control is displayed (or not).
901 static void TAB_SetupScrolling(
904 const RECT* clientRect)
907 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
909 if (infoPtr->needsScrolling)
915 * Calculate the position of the scroll control.
917 if(lStyle & TCS_VERTICAL)
919 controlPos.right = clientRect->right;
920 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
922 if (lStyle & TCS_BOTTOM)
924 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
925 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
929 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
930 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
935 controlPos.right = clientRect->right;
936 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
938 if (lStyle & TCS_BOTTOM)
940 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
941 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
945 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
946 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
951 * If we don't have a scroll control yet, we want to create one.
952 * If we have one, we want to make sure it's positioned properly.
954 if (infoPtr->hwndUpDown==0)
956 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
958 WS_VISIBLE | WS_CHILD | UDS_HORZ,
959 controlPos.left, controlPos.top,
960 controlPos.right - controlPos.left,
961 controlPos.bottom - controlPos.top,
969 SetWindowPos(infoPtr->hwndUpDown,
971 controlPos.left, controlPos.top,
972 controlPos.right - controlPos.left,
973 controlPos.bottom - controlPos.top,
974 SWP_SHOWWINDOW | SWP_NOZORDER);
977 /* Now calculate upper limit of the updown control range.
978 * We do this by calculating how many tabs will be offscreen when the
979 * last tab is visible.
981 if(infoPtr->uNumItem)
983 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
984 maxRange = infoPtr->uNumItem;
985 tabwidth = TAB_GetItem(infoPtr, infoPtr->uNumItem - 1)->rect.right;
987 for(; maxRange > 0; maxRange--)
989 if(tabwidth - TAB_GetItem(infoPtr,maxRange - 1)->rect.left > vsize)
993 if(maxRange == infoPtr->uNumItem)
999 /* If we once had a scroll control... hide it */
1000 if (infoPtr->hwndUpDown!=0)
1001 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1003 if (infoPtr->hwndUpDown)
1004 SendMessageW(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1007 /******************************************************************************
1010 * This method will calculate the position rectangles of all the items in the
1011 * control. The rectangle calculated starts at 0 for the first item in the
1012 * list and ignores scrolling and selection.
1013 * It also uses the current font to determine the height of the tab row and
1014 * it checks if all the tabs fit in the client area of the window. If they
1015 * don't, a scrolling control is added.
1017 static void TAB_SetItemBounds (TAB_INFO *infoPtr)
1019 LONG lStyle = GetWindowLongA(infoPtr->hwnd, GWL_STYLE);
1020 TEXTMETRICA fontMetrics;
1023 INT curItemRowCount;
1024 HFONT hFont, hOldFont;
1034 * We need to get text information so we need a DC and we need to select
1037 hdc = GetDC(infoPtr->hwnd);
1039 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1040 hOldFont = SelectObject (hdc, hFont);
1043 * We will base the rectangle calculations on the client rectangle
1046 GetClientRect(infoPtr->hwnd, &clientRect);
1048 /* if TCS_VERTICAL then swap the height and width so this code places the
1049 tabs along the top of the rectangle and we can just rotate them after
1050 rather than duplicate all of the below code */
1051 if(lStyle & TCS_VERTICAL)
1053 iTemp = clientRect.bottom;
1054 clientRect.bottom = clientRect.right;
1055 clientRect.right = iTemp;
1058 /* Now use hPadding and vPadding */
1059 infoPtr->uHItemPadding = infoPtr->uHItemPadding_s;
1060 infoPtr->uVItemPadding = infoPtr->uVItemPadding_s;
1062 /* The leftmost item will be "0" aligned */
1064 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1066 if (!(infoPtr->fHeightSet))
1069 int icon_height = 0;
1071 /* Use the current font to determine the height of a tab. */
1072 GetTextMetricsA(hdc, &fontMetrics);
1074 /* Get the icon height */
1076 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1078 /* Take the highest between font or icon */
1079 if (fontMetrics.tmHeight > icon_height)
1080 item_height = fontMetrics.tmHeight + 2;
1082 item_height = icon_height;
1085 * Make sure there is enough space for the letters + icon + growing the
1086 * selected item + extra space for the selected item.
1088 infoPtr->tabHeight = item_height +
1089 ((lStyle & TCS_BUTTONS) ? 2 : 1) *
1090 infoPtr->uVItemPadding;
1092 TRACE("tabH=%d, tmH=%ld, iconh=%d\n",
1093 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1096 TRACE("client right=%ld\n", clientRect.right);
1098 /* Get the icon width */
1101 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1103 if (lStyle & TCS_FIXEDWIDTH)
1106 /* Add padding if icon is present */
1107 icon_width += infoPtr->uHItemPadding;
1110 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1112 TAB_ITEM *curr = TAB_GetItem(infoPtr, curItem);
1114 /* Set the leftmost position of the tab. */
1115 curr->rect.left = curItemLeftPos;
1117 if ((lStyle & TCS_FIXEDWIDTH) || !curr->pszText)
1119 curr->rect.right = curr->rect.left +
1120 max(infoPtr->tabWidth, icon_width);
1126 /* Calculate how wide the tab is depending on the text it contains */
1127 GetTextExtentPoint32W(hdc, curr->pszText,
1128 lstrlenW(curr->pszText), &size);
1130 curr->rect.right = curr->rect.left + size.cx + icon_width +
1131 num * infoPtr->uHItemPadding;
1132 TRACE("for <%s>, l,r=%ld,%ld, num=%d\n",
1133 debugstr_w(curr->pszText), curr->rect.left, curr->rect.right, num);
1137 * Check if this is a multiline tab control and if so
1138 * check to see if we should wrap the tabs
1140 * Wrap all these tabs. We will arrange them evenly later.
1144 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1146 (clientRect.right - CONTROL_BORDER_SIZEX - DISPLAY_AREA_PADDINGX)))
1148 curr->rect.right -= curr->rect.left;
1150 curr->rect.left = 0;
1152 TRACE("wrapping <%s>, l,r=%ld,%ld\n", debugstr_w(curr->pszText),
1153 curr->rect.left, curr->rect.right);
1156 curr->rect.bottom = 0;
1157 curr->rect.top = curItemRowCount - 1;
1159 TRACE("TextSize: %li\n", size.cx);
1160 TRACE("Rect: T %li, L %li, B %li, R %li\n", curr->rect.top,
1161 curr->rect.left, curr->rect.bottom, curr->rect.right);
1164 * The leftmost position of the next item is the rightmost position
1167 if (lStyle & TCS_BUTTONS)
1169 curItemLeftPos = curr->rect.right + BUTTON_SPACINGX;
1170 if (lStyle & TCS_FLATBUTTONS)
1171 curItemLeftPos += FLAT_BTN_SPACINGX;
1174 curItemLeftPos = curr->rect.right;
1177 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1180 * Check if we need a scrolling control.
1182 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1185 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1186 if(!infoPtr->needsScrolling)
1187 infoPtr->leftmostVisible = 0;
1192 * No scrolling in Multiline or Vertical styles.
1194 infoPtr->needsScrolling = FALSE;
1195 infoPtr->leftmostVisible = 0;
1197 TAB_SetupScrolling(infoPtr->hwnd, infoPtr, &clientRect);
1199 /* Set the number of rows */
1200 infoPtr->uNumRows = curItemRowCount;
1202 /* Arrange all tabs evenly if style says so */
1203 if (!(lStyle & TCS_RAGGEDRIGHT) && ((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->uNumItem > 0))
1205 INT tabPerRow,remTab,iRow;
1210 * Ok windows tries to even out the rows. place the same
1211 * number of tabs in each row. So lets give that a shot
1214 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1215 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1217 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1218 iItm<infoPtr->uNumItem;
1221 /* normalize the current rect */
1222 TAB_ITEM *curr = TAB_GetItem(infoPtr, iItm);
1224 /* shift the item to the left side of the clientRect */
1225 curr->rect.right -= curr->rect.left;
1226 curr->rect.left = 0;
1228 TRACE("r=%ld, cl=%d, cl.r=%ld, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1229 curr->rect.right, curItemLeftPos, clientRect.right,
1230 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1232 /* if we have reached the maximum number of tabs on this row */
1233 /* move to the next row, reset our current item left position and */
1234 /* the count of items on this row */
1236 if (lStyle & TCS_VERTICAL) {
1237 /* Vert: Add the remaining tabs in the *last* remainder rows */
1238 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1244 /* Horz: Add the remaining tabs in the *first* remainder rows */
1245 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1252 /* shift the item to the right to place it as the next item in this row */
1253 curr->rect.left += curItemLeftPos;
1254 curr->rect.right += curItemLeftPos;
1255 curr->rect.top = iRow;
1256 if (lStyle & TCS_BUTTONS)
1258 curItemLeftPos = curr->rect.right + 1;
1259 if (lStyle & TCS_FLATBUTTONS)
1260 curItemLeftPos += FLAT_BTN_SPACINGX;
1263 curItemLeftPos = curr->rect.right;
1265 TRACE("arranging <%s>, l,r=%ld,%ld, row=%ld\n",
1266 debugstr_w(curr->pszText), curr->rect.left,
1267 curr->rect.right, curr->rect.top);
1274 INT widthDiff, iIndexStart=0, iIndexEnd=0;
1278 while(iIndexStart < infoPtr->uNumItem)
1280 TAB_ITEM *start = TAB_GetItem(infoPtr, iIndexStart);
1283 * find the index of the row
1285 /* find the first item on the next row */
1286 for (iIndexEnd=iIndexStart;
1287 (iIndexEnd < infoPtr->uNumItem) &&
1288 (TAB_GetItem(infoPtr, iIndexEnd)->rect.top ==
1291 /* intentionally blank */;
1294 * we need to justify these tabs so they fill the whole given
1298 /* find the amount of space remaining on this row */
1299 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1300 TAB_GetItem(infoPtr, iIndexEnd - 1)->rect.right;
1302 /* iCount is the number of tab items on this row */
1303 iCount = iIndexEnd - iIndexStart;
1307 remainder = widthDiff % iCount;
1308 widthDiff = widthDiff / iCount;
1309 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1310 for (iIndex=iIndexStart, iCount=0; iIndex < iIndexEnd; iIndex++, iCount++)
1312 TAB_ITEM *item = TAB_GetItem(infoPtr, iIndex);
1314 item->rect.left += iCount * widthDiff;
1315 item->rect.right += (iCount + 1) * widthDiff;
1317 TRACE("adjusting 1 <%s>, l,r=%ld,%ld\n",
1318 debugstr_w(item->pszText),
1319 item->rect.left, item->rect.right);
1322 TAB_GetItem(infoPtr, iIndex - 1)->rect.right += remainder;
1324 else /* we have only one item on this row, make it take up the entire row */
1326 start->rect.left = clientRect.left;
1327 start->rect.right = clientRect.right - 4;
1329 TRACE("adjusting 2 <%s>, l,r=%ld,%ld\n",
1330 debugstr_w(start->pszText),
1331 start->rect.left, start->rect.right);
1336 iIndexStart = iIndexEnd;
1341 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1342 if(lStyle & TCS_VERTICAL)
1345 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1347 rcItem = &TAB_GetItem(infoPtr, iIndex)->rect;
1349 rcOriginal = *rcItem;
1351 /* this is rotating the items by 90 degrees clockwise around the center of the control */
1352 rcItem->top = (rcOriginal.left - clientRect.left);
1353 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1354 rcItem->left = rcOriginal.top;
1355 rcItem->right = rcOriginal.bottom;
1359 TAB_EnsureSelectionVisible(infoPtr);
1360 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
1363 SelectObject (hdc, hOldFont);
1364 ReleaseDC (infoPtr->hwnd, hdc);
1369 TAB_EraseTabInterior
1377 LONG lStyle = GetWindowLongA(infoPtr->hwnd, GWL_STYLE);
1378 HBRUSH hbr = CreateSolidBrush (comctl32_color.clrBtnFace);
1379 BOOL deleteBrush = TRUE;
1380 RECT rTemp = *drawRect;
1382 InflateRect(&rTemp, -2, -2);
1383 if (lStyle & TCS_BUTTONS)
1385 if (iItem == infoPtr->iSelected)
1387 /* Background color */
1388 if (!(lStyle & TCS_OWNERDRAWFIXED))
1391 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1393 SetTextColor(hdc, comctl32_color.clr3dFace);
1394 SetBkColor(hdc, comctl32_color.clr3dHilight);
1396 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1397 * we better use 0x55aa bitmap brush to make scrollbar's background
1398 * look different from the window background.
1400 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1401 hbr = COMCTL32_hPattern55AABrush;
1403 deleteBrush = FALSE;
1405 FillRect(hdc, &rTemp, hbr);
1407 else /* ! selected */
1409 if (lStyle & TCS_FLATBUTTONS)
1411 FillRect(hdc, drawRect, hbr);
1412 if (iItem == infoPtr->iHotTracked)
1413 DrawEdge(hdc, drawRect, EDGE_RAISED, BF_SOFT|BF_RECT);
1416 FillRect(hdc, &rTemp, hbr);
1420 else /* !TCS_BUTTONS */
1422 FillRect(hdc, &rTemp, hbr);
1426 if (deleteBrush) DeleteObject(hbr);
1429 /******************************************************************************
1430 * TAB_DrawItemInterior
1432 * This method is used to draw the interior (text and icon) of a single tab
1433 * into the tab control.
1436 TAB_DrawItemInterior
1444 LONG lStyle = GetWindowLongA(infoPtr->hwnd, GWL_STYLE);
1453 /* if (drawRect == NULL) */
1460 * Get the rectangle for the item.
1462 isVisible = TAB_InternalGetItemRect(infoPtr, iItem, &itemRect, &selectedRect);
1467 * Make sure drawRect points to something valid; simplifies code.
1469 drawRect = &localRect;
1472 * This logic copied from the part of TAB_DrawItem which draws
1473 * the tab background. It's important to keep it in sync. I
1474 * would have liked to avoid code duplication, but couldn't figure
1475 * out how without making spaghetti of TAB_DrawItem.
1477 if (iItem == infoPtr->iSelected)
1478 *drawRect = selectedRect;
1480 *drawRect = itemRect;
1482 if (lStyle & TCS_BUTTONS)
1484 if (iItem == infoPtr->iSelected)
1486 drawRect->left += 4;
1488 drawRect->right -= 4;
1489 drawRect->bottom -= 1;
1493 drawRect->left += 2;
1495 drawRect->right -= 2;
1496 drawRect->bottom -= 2;
1501 if ((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1503 if (iItem != infoPtr->iSelected)
1505 drawRect->left += 2;
1507 drawRect->bottom -= 2;
1510 else if (lStyle & TCS_VERTICAL)
1512 if (iItem == infoPtr->iSelected)
1514 drawRect->right += 1;
1519 drawRect->right -= 2;
1520 drawRect->bottom -= 2;
1523 else if (lStyle & TCS_BOTTOM)
1525 if (iItem == infoPtr->iSelected)
1531 InflateRect(drawRect, -2, -2);
1532 drawRect->bottom += 2;
1537 if (iItem == infoPtr->iSelected)
1539 drawRect->bottom += 3;
1543 drawRect->bottom -= 2;
1544 InflateRect(drawRect, -2, 0);
1549 TRACE("drawRect=(%ld,%ld)-(%ld,%ld)\n",
1550 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom);
1552 /* Clear interior */
1553 TAB_EraseTabInterior (infoPtr, hdc, iItem, drawRect);
1555 /* Draw the focus rectangle */
1556 if (!(lStyle & TCS_FOCUSNEVER) &&
1557 (GetFocus() == infoPtr->hwnd) &&
1558 (iItem == infoPtr->uFocus) )
1560 RECT rFocus = *drawRect;
1561 InflateRect(&rFocus, -3, -3);
1562 if (lStyle & TCS_BOTTOM && !(lStyle & TCS_VERTICAL))
1564 if (lStyle & TCS_BUTTONS)
1570 DrawFocusRect(hdc, &rFocus);
1576 htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1577 holdPen = SelectObject(hdc, htextPen);
1578 hOldFont = SelectObject(hdc, infoPtr->hFont);
1581 * Setup for text output
1583 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1584 SetTextColor(hdc, (((iItem == infoPtr->iHotTracked) && !(lStyle & TCS_FLATBUTTONS)) |
1585 (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED)) ?
1586 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1589 * if owner draw, tell the owner to draw
1591 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(infoPtr->hwnd))
1597 drawRect->right -= 1;
1598 if ( iItem == infoPtr->iSelected )
1600 drawRect->right -= 1;
1601 drawRect->left += 1;
1605 * get the control id
1607 id = (UINT)GetWindowLongPtrW( infoPtr->hwnd, GWLP_ID );
1610 * put together the DRAWITEMSTRUCT
1612 dis.CtlType = ODT_TAB;
1615 dis.itemAction = ODA_DRAWENTIRE;
1617 if ( iItem == infoPtr->iSelected )
1618 dis.itemState |= ODS_SELECTED;
1619 if (infoPtr->uFocus == iItem)
1620 dis.itemState |= ODS_FOCUS;
1621 dis.hwndItem = infoPtr->hwnd;
1623 CopyRect(&dis.rcItem,drawRect);
1624 dis.itemData = (ULONG_PTR)TAB_GetItem(infoPtr, iItem)->extra;
1627 * send the draw message
1629 SendMessageW( infoPtr->hwndNotify, WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1633 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
1637 /* used to center the icon and text in the tab */
1639 INT center_offset_h, center_offset_v;
1641 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1642 rcImage = *drawRect;
1646 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1648 /* get the rectangle that the text fits in */
1651 DrawTextW(hdc, item->pszText, -1, &rcText, DT_CALCRECT);
1654 * If not owner draw, then do the drawing ourselves.
1658 if (infoPtr->himl && (item->mask & TCIF_IMAGE))
1663 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1665 if(lStyle & TCS_VERTICAL)
1667 center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1668 center_offset_v = ((drawRect->right - drawRect->left) - (cx + infoPtr->uVItemPadding)) / 2;
1672 center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1673 center_offset_v = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uVItemPadding)) / 2;
1676 if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1677 center_offset_h = infoPtr->uHItemPadding;
1679 if (center_offset_h < 2)
1680 center_offset_h = 2;
1682 if (center_offset_v < 0)
1683 center_offset_v = 0;
1685 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1686 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1687 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1688 (rcText.right-rcText.left));
1690 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1692 rcImage.top = drawRect->top + center_offset_h;
1693 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1694 /* right side of the tab, but the image still uses the left as its x position */
1695 /* this keeps the image always drawn off of the same side of the tab */
1696 rcImage.left = drawRect->right - cx - center_offset_v;
1697 drawRect->top += cy + infoPtr->uHItemPadding;
1699 else if(lStyle & TCS_VERTICAL)
1701 rcImage.top = drawRect->bottom - cy - center_offset_h;
1702 rcImage.left = drawRect->left + center_offset_v;
1703 drawRect->bottom -= cy + infoPtr->uHItemPadding;
1705 else /* normal style, whether TCS_BOTTOM or not */
1707 rcImage.left = drawRect->left + center_offset_h;
1708 rcImage.top = drawRect->top + center_offset_v;
1709 drawRect->left += cx + infoPtr->uHItemPadding;
1712 TRACE("drawing image=%d, left=%ld, top=%ld\n",
1713 item->iImage, rcImage.left, rcImage.top-1);
1725 /* Now position text */
1726 if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT)
1727 center_offset_h = infoPtr->uHItemPadding;
1729 if(lStyle & TCS_VERTICAL)
1730 center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1732 center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1734 if(lStyle & TCS_VERTICAL)
1736 if(lStyle & TCS_BOTTOM)
1737 drawRect->top+=center_offset_h;
1739 drawRect->bottom-=center_offset_h;
1741 center_offset_v = ((drawRect->right - drawRect->left) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1745 drawRect->left += center_offset_h;
1746 center_offset_v = ((drawRect->bottom - drawRect->top) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1749 if (center_offset_v < 0)
1750 center_offset_v = 0;
1752 if(lStyle & TCS_VERTICAL)
1753 drawRect->left += center_offset_v;
1755 drawRect->top += center_offset_v;
1758 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1762 INT nEscapement = 900;
1763 INT nOrientation = 900;
1765 if(lStyle & TCS_BOTTOM)
1768 nOrientation = -900;
1771 /* to get a font with the escapement and orientation we are looking for, we need to */
1772 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1773 if (!GetObjectA((infoPtr->hFont) ?
1774 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1775 sizeof(LOGFONTA),&logfont))
1779 lstrcpyA(logfont.lfFaceName, "Arial");
1780 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1782 logfont.lfWeight = FW_NORMAL;
1783 logfont.lfItalic = 0;
1784 logfont.lfUnderline = 0;
1785 logfont.lfStrikeOut = 0;
1788 logfont.lfEscapement = nEscapement;
1789 logfont.lfOrientation = nOrientation;
1790 hFont = CreateFontIndirectA(&logfont);
1791 SelectObject(hdc, hFont);
1796 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1797 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1801 lstrlenW(item->pszText),
1805 DeleteObject(hFont);
1809 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1810 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1811 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1812 (rcText.right-rcText.left));
1819 lstrlenW(item->pszText),
1821 DT_LEFT | DT_SINGLELINE
1826 *drawRect = rcTemp; /* restore drawRect */
1832 SelectObject(hdc, hOldFont);
1833 SetBkMode(hdc, oldBkMode);
1834 SelectObject(hdc, holdPen);
1835 DeleteObject( htextPen );
1838 /******************************************************************************
1841 * This method is used to draw a single tab into the tab control.
1843 static void TAB_DrawItem(
1848 LONG lStyle = GetWindowLongA(infoPtr->hwnd, GWL_STYLE);
1852 RECT r, fillRect, r1;
1855 COLORREF bkgnd, corner;
1858 * Get the rectangle for the item.
1860 isVisible = TAB_InternalGetItemRect(infoPtr,
1869 /* Clip UpDown control to not draw over it */
1870 if (infoPtr->needsScrolling)
1872 GetWindowRect(infoPtr->hwnd, &rC);
1873 GetWindowRect(infoPtr->hwndUpDown, &rUD);
1874 ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
1877 /* If you need to see what the control is doing,
1878 * then override these variables. They will change what
1879 * fill colors are used for filling the tabs, and the
1880 * corners when drawing the edge.
1882 bkgnd = comctl32_color.clrBtnFace;
1883 corner = comctl32_color.clrBtnFace;
1885 if (lStyle & TCS_BUTTONS)
1887 /* Get item rectangle */
1890 /* Separators between flat buttons */
1891 if (lStyle & TCS_FLATBUTTONS)
1894 r1.right += (FLAT_BTN_SPACINGX -2);
1895 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1898 if (iItem == infoPtr->iSelected)
1900 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1902 OffsetRect(&r, 1, 1);
1904 else /* ! selected */
1906 if (!(lStyle & TCS_FLATBUTTONS))
1907 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1910 else /* !TCS_BUTTONS */
1912 /* We draw a rectangle of different sizes depending on the selection
1914 if (iItem == infoPtr->iSelected) {
1916 GetClientRect (infoPtr->hwnd, &rect);
1917 clRight = rect.right;
1918 clBottom = rect.bottom;
1925 * Erase the background. (Delay it but setup rectangle.)
1926 * This is necessary when drawing the selected item since it is larger
1927 * than the others, it might overlap with stuff already drawn by the
1932 if(lStyle & TCS_VERTICAL)
1934 /* These are for adjusting the drawing of a Selected tab */
1935 /* The initial values are for the normal case of non-Selected */
1936 int ZZ = 1; /* Do not strech if selected */
1937 if (iItem == infoPtr->iSelected) {
1940 /* if leftmost draw the line longer */
1941 if(selectedRect.top == 0)
1942 fillRect.top += CONTROL_BORDER_SIZEY;
1943 /* if rightmost draw the line longer */
1944 if(selectedRect.bottom == clBottom)
1945 fillRect.bottom -= CONTROL_BORDER_SIZEY;
1948 if (lStyle & TCS_BOTTOM)
1950 /* Adjust both rectangles to match native */
1953 TRACE("<right> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1955 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1956 r.left,r.top,r.right,r.bottom);
1958 /* Clear interior */
1959 SetBkColor(hdc, bkgnd);
1960 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1962 /* Draw rectangular edge around tab */
1963 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
1965 /* Now erase the top corner and draw diagonal edge */
1966 SetBkColor(hdc, corner);
1967 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1970 r1.bottom = r1.top + ROUND_CORNER_SIZE;
1971 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1973 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
1975 /* Now erase the bottom corner and draw diagonal edge */
1976 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1977 r1.bottom = r.bottom;
1979 r1.top = r1.bottom - ROUND_CORNER_SIZE;
1980 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1982 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
1984 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
1988 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
1994 TRACE("<left> 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_LEFT|BF_TOP|BF_BOTTOM);
2006 /* Now erase the top corner and draw diagonal edge */
2007 SetBkColor(hdc, corner);
2010 r1.right = r1.left + 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_ENDTOPRIGHT);
2016 /* Now erase the bottom corner and draw diagonal edge */
2018 r1.bottom = r.bottom;
2019 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2020 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2021 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2023 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2026 else /* ! TCS_VERTICAL */
2028 /* These are for adjusting the drawing of a Selected tab */
2029 /* The initial values are for the normal case of non-Selected */
2030 if (iItem == infoPtr->iSelected) {
2031 /* if leftmost draw the line longer */
2032 if(selectedRect.left == 0)
2033 fillRect.left += CONTROL_BORDER_SIZEX;
2034 /* if rightmost draw the line longer */
2035 if(selectedRect.right == clRight)
2036 fillRect.right -= CONTROL_BORDER_SIZEX;
2039 if (lStyle & TCS_BOTTOM)
2041 /* Adjust both rectangles for topmost row */
2042 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2048 TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2050 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2051 r.left,r.top,r.right,r.bottom);
2053 /* Clear interior */
2054 SetBkColor(hdc, bkgnd);
2055 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2057 /* Draw rectangular edge around tab */
2058 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2060 /* Now erase the righthand corner and draw diagonal edge */
2061 SetBkColor(hdc, corner);
2062 r1.left = r.right - ROUND_CORNER_SIZE;
2063 r1.bottom = r.bottom;
2065 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2066 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2068 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2070 /* Now erase the lefthand corner and draw diagonal edge */
2072 r1.bottom = r.bottom;
2073 r1.right = r1.left + ROUND_CORNER_SIZE;
2074 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2075 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2077 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2079 if (iItem == infoPtr->iSelected)
2083 if (selectedRect.left == 0)
2088 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2095 /* Adjust both rectangles for bottommost row */
2096 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2098 fillRect.bottom += 3;
2102 TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2104 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2105 r.left,r.top,r.right,r.bottom);
2107 /* Clear interior */
2108 SetBkColor(hdc, bkgnd);
2109 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2111 /* Draw rectangular edge around tab */
2112 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2114 /* Now erase the righthand corner and draw diagonal edge */
2115 SetBkColor(hdc, corner);
2116 r1.left = r.right - ROUND_CORNER_SIZE;
2119 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2120 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2122 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2124 /* Now erase the lefthand corner and draw diagonal edge */
2127 r1.right = r1.left + ROUND_CORNER_SIZE;
2128 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2129 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2131 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2136 TAB_DumpItemInternal(infoPtr, iItem);
2138 /* This modifies r to be the text rectangle. */
2139 TAB_DrawItemInterior(infoPtr, hdc, iItem, &r);
2143 /******************************************************************************
2146 * This method is used to draw the raised border around the tab control
2149 static void TAB_DrawBorder (TAB_INFO *infoPtr, HDC hdc)
2152 DWORD lStyle = GetWindowLongA(infoPtr->hwnd, GWL_STYLE);
2154 GetClientRect (infoPtr->hwnd, &rect);
2157 * Adjust for the style
2160 if (infoPtr->uNumItem)
2162 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2163 rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2164 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2165 rect.right -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2166 else if(lStyle & TCS_VERTICAL)
2167 rect.left += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2168 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2169 rect.top += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2172 TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2173 rect.left, rect.top, rect.right, rect.bottom);
2175 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2178 /******************************************************************************
2181 * This method repaints the tab control..
2183 static void TAB_Refresh (TAB_INFO *infoPtr, HDC hdc)
2188 if (!infoPtr->DoRedraw)
2191 hOldFont = SelectObject (hdc, infoPtr->hFont);
2193 if (GetWindowLongA(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS)
2195 for (i = 0; i < infoPtr->uNumItem; i++)
2196 TAB_DrawItem (infoPtr, hdc, i);
2200 /* Draw all the non selected item first */
2201 for (i = 0; i < infoPtr->uNumItem; i++)
2203 if (i != infoPtr->iSelected)
2204 TAB_DrawItem (infoPtr, hdc, i);
2207 /* Now, draw the border, draw it before the selected item
2208 * since the selected item overwrites part of the border. */
2209 TAB_DrawBorder (infoPtr, hdc);
2211 /* Then, draw the selected item */
2212 TAB_DrawItem (infoPtr, hdc, infoPtr->iSelected);
2214 /* If we haven't set the current focus yet, set it now.
2215 * Only happens when we first paint the tab controls */
2216 if (infoPtr->uFocus == -1)
2217 TAB_SetCurFocus(infoPtr, infoPtr->iSelected);
2220 SelectObject (hdc, hOldFont);
2223 static inline DWORD TAB_GetRowCount (const TAB_INFO *infoPtr)
2225 return infoPtr->uNumRows;
2228 static inline LRESULT TAB_SetRedraw (TAB_INFO *infoPtr, BOOL doRedraw)
2230 infoPtr->DoRedraw = doRedraw;
2234 /******************************************************************************
2235 * TAB_EnsureSelectionVisible
2237 * This method will make sure that the current selection is completely
2238 * visible by scrolling until it is.
2240 static void TAB_EnsureSelectionVisible(
2243 INT iSelected = infoPtr->iSelected;
2244 LONG lStyle = GetWindowLongA(infoPtr->hwnd, GWL_STYLE);
2245 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2247 /* set the items row to the bottommost row or topmost row depending on
2249 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2251 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2255 if(lStyle & TCS_VERTICAL)
2256 newselected = selected->rect.left;
2258 newselected = selected->rect.top;
2260 /* the target row is always (number of rows - 1)
2261 as row 0 is furthest from the clientRect */
2262 iTargetRow = infoPtr->uNumRows - 1;
2264 if (newselected != iTargetRow)
2267 if(lStyle & TCS_VERTICAL)
2269 for (i=0; i < infoPtr->uNumItem; i++)
2271 /* move everything in the row of the selected item to the iTargetRow */
2272 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2274 if (item->rect.left == newselected )
2275 item->rect.left = iTargetRow;
2278 if (item->rect.left > newselected)
2285 for (i=0; i < infoPtr->uNumItem; i++)
2287 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2289 if (item->rect.top == newselected )
2290 item->rect.top = iTargetRow;
2293 if (item->rect.top > newselected)
2298 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2303 * Do the trivial cases first.
2305 if ( (!infoPtr->needsScrolling) ||
2306 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2309 if (infoPtr->leftmostVisible >= iSelected)
2311 infoPtr->leftmostVisible = iSelected;
2315 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2320 /* Calculate the part of the client area that is visible */
2321 GetClientRect(infoPtr->hwnd, &r);
2324 GetClientRect(infoPtr->hwndUpDown, &r);
2327 if ((selected->rect.right -
2328 selected->rect.left) >= width )
2330 /* Special case: width of selected item is greater than visible
2333 infoPtr->leftmostVisible = iSelected;
2337 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2339 if ((selected->rect.right - TAB_GetItem(infoPtr, i)->rect.left) < width)
2342 infoPtr->leftmostVisible = i;
2346 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2347 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2349 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2350 MAKELONG(infoPtr->leftmostVisible, 0));
2353 /******************************************************************************
2354 * TAB_InvalidateTabArea
2356 * This method will invalidate the portion of the control that contains the
2357 * tabs. It is called when the state of the control changes and needs
2360 static void TAB_InvalidateTabArea(TAB_INFO* infoPtr)
2362 RECT clientRect, rInvalidate, rAdjClient;
2363 DWORD lStyle = GetWindowLongA(infoPtr->hwnd, GWL_STYLE);
2364 INT lastRow = infoPtr->uNumRows - 1;
2367 if (lastRow < 0) return;
2369 GetClientRect(infoPtr->hwnd, &clientRect);
2370 rInvalidate = clientRect;
2371 rAdjClient = clientRect;
2373 TAB_AdjustRect(infoPtr, 0, &rAdjClient);
2375 TAB_InternalGetItemRect(infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2376 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2378 rInvalidate.left = rAdjClient.right;
2379 if (infoPtr->uNumRows == 1)
2380 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2382 else if(lStyle & TCS_VERTICAL)
2384 rInvalidate.right = rAdjClient.left;
2385 if (infoPtr->uNumRows == 1)
2386 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2388 else if (lStyle & TCS_BOTTOM)
2390 rInvalidate.top = rAdjClient.bottom;
2391 if (infoPtr->uNumRows == 1)
2392 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2396 rInvalidate.bottom = rAdjClient.top;
2397 if (infoPtr->uNumRows == 1)
2398 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2401 /* Punch out the updown control */
2402 if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2404 GetClientRect(infoPtr->hwndUpDown, &r);
2405 if (rInvalidate.right > clientRect.right - r.left)
2406 rInvalidate.right = rInvalidate.right - (r.right - r.left);
2408 rInvalidate.right = clientRect.right - r.left;
2411 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2412 rInvalidate.left, rInvalidate.top,
2413 rInvalidate.right, rInvalidate.bottom);
2415 InvalidateRect(infoPtr->hwnd, &rInvalidate, TRUE);
2418 static inline LRESULT TAB_Paint (TAB_INFO *infoPtr, HDC hdcPaint)
2427 hdc = BeginPaint (infoPtr->hwnd, &ps);
2428 TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2430 ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2433 TAB_Refresh (infoPtr, hdc);
2436 EndPaint (infoPtr->hwnd, &ps);
2442 TAB_InsertItemAW (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam, BOOL bUnicode)
2449 GetClientRect (infoPtr->hwnd, &rect);
2450 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", infoPtr->hwnd,
2451 rect.top, rect.left, rect.bottom, rect.right);
2453 pti = (TCITEMA *)lParam;
2454 iItem = (INT)wParam;
2456 if (iItem < 0) return -1;
2457 if (iItem > infoPtr->uNumItem)
2458 iItem = infoPtr->uNumItem;
2461 TAB_DumpItemExternalW((TCITEMW*)pti, iItem);
2463 TAB_DumpItemExternalA(pti, iItem);
2466 if (infoPtr->uNumItem == 0) {
2467 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2468 infoPtr->uNumItem++;
2469 infoPtr->iSelected = 0;
2472 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2474 infoPtr->uNumItem++;
2475 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2477 /* pre insert copy */
2479 memcpy (infoPtr->items, oldItems,
2480 iItem * TAB_ITEM_SIZE(infoPtr));
2483 /* post insert copy */
2484 if (iItem < infoPtr->uNumItem - 1) {
2485 memcpy (TAB_GetItem(infoPtr, iItem + 1),
2486 oldItems + iItem * TAB_ITEM_SIZE(infoPtr),
2487 (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2491 if (iItem <= infoPtr->iSelected)
2492 infoPtr->iSelected++;
2497 item = TAB_GetItem(infoPtr, iItem);
2499 item->mask = pti->mask;
2500 item->pszText = NULL;
2502 if (pti->mask & TCIF_TEXT)
2505 Str_SetPtrW (&item->pszText, (WCHAR*)pti->pszText);
2507 Str_SetPtrAtoW (&item->pszText, pti->pszText);
2510 if (pti->mask & TCIF_IMAGE)
2511 item->iImage = pti->iImage;
2515 if (pti->mask & TCIF_PARAM)
2516 memcpy(item->extra, &pti->lParam, infoPtr->cbInfo);
2518 memset(item->extra, 0, infoPtr->cbInfo);
2520 TAB_SetItemBounds(infoPtr);
2521 if (infoPtr->uNumItem > 1)
2522 TAB_InvalidateTabArea(infoPtr);
2524 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2526 TRACE("[%p]: added item %d %s\n",
2527 infoPtr->hwnd, iItem, debugstr_w(item->pszText));
2533 TAB_SetItemSize (TAB_INFO *infoPtr, LPARAM lParam)
2535 LONG lStyle = GetWindowLongA(infoPtr->hwnd, GWL_STYLE);
2537 BOOL bNeedPaint = FALSE;
2539 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2541 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2542 if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2544 infoPtr->tabWidth = max((INT)LOWORD(lParam), infoPtr->tabMinWidth);
2548 if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2550 if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2551 infoPtr->tabHeight = (INT)HIWORD(lParam);
2555 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2556 HIWORD(lResult), LOWORD(lResult),
2557 infoPtr->tabHeight, infoPtr->tabWidth);
2561 TAB_SetItemBounds(infoPtr);
2562 RedrawWindow(infoPtr->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2568 static inline LRESULT TAB_SetMinTabWidth (TAB_INFO *infoPtr, INT cx)
2572 TRACE("(%p,%d)\n", infoPtr, cx);
2575 oldcx = infoPtr->tabMinWidth;
2576 infoPtr->tabMinWidth = (cx==-1)?DEFAULT_TAB_WIDTH:cx;
2582 static inline LRESULT
2583 TAB_HighlightItem (TAB_INFO *infoPtr, INT iItem, BOOL fHighlight)
2587 TRACE("(%p,%d,%s)\n", infoPtr, iItem, fHighlight ? "true" : "false");
2589 if (!infoPtr || iItem < 0 || iItem >= infoPtr->uNumItem)
2592 lpState = &TAB_GetItem(infoPtr, iItem)->dwState;
2595 *lpState |= TCIS_HIGHLIGHTED;
2597 *lpState &= ~TCIS_HIGHLIGHTED;
2603 TAB_SetItemAW (TAB_INFO *infoPtr, INT iItem, LPTCITEMA tabItem, BOOL bUnicode)
2607 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2609 if (iItem < 0 || iItem >= infoPtr->uNumItem)
2613 TAB_DumpItemExternalW((TCITEMW *)tabItem, iItem);
2615 TAB_DumpItemExternalA(tabItem, iItem);
2617 wineItem = TAB_GetItem(infoPtr, iItem);
2619 if (tabItem->mask & TCIF_IMAGE)
2620 wineItem->iImage = tabItem->iImage;
2622 if (tabItem->mask & TCIF_PARAM)
2623 memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2625 if (tabItem->mask & TCIF_RTLREADING)
2626 FIXME("TCIF_RTLREADING\n");
2628 if (tabItem->mask & TCIF_STATE)
2629 wineItem->dwState = tabItem->dwState;
2631 if (tabItem->mask & TCIF_TEXT)
2633 if (wineItem->pszText)
2635 Free(wineItem->pszText);
2636 wineItem->pszText = NULL;
2639 Str_SetPtrW(&wineItem->pszText, (WCHAR*)tabItem->pszText);
2641 Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2644 /* Update and repaint tabs */
2645 TAB_SetItemBounds(infoPtr);
2646 TAB_InvalidateTabArea(infoPtr);
2651 static inline LRESULT TAB_GetItemCount (const TAB_INFO *infoPtr)
2653 return infoPtr->uNumItem;
2658 TAB_GetItemAW (TAB_INFO *infoPtr, INT iItem, LPTCITEMA tabItem, BOOL bUnicode)
2662 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2664 if (iItem < 0 || iItem >= infoPtr->uNumItem)
2667 wineItem = TAB_GetItem(infoPtr, iItem);
2669 if (tabItem->mask & TCIF_IMAGE)
2670 tabItem->iImage = wineItem->iImage;
2672 if (tabItem->mask & TCIF_PARAM)
2673 memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2675 if (tabItem->mask & TCIF_RTLREADING)
2676 FIXME("TCIF_RTLREADING\n");
2678 if (tabItem->mask & TCIF_STATE)
2679 tabItem->dwState = wineItem->dwState;
2681 if (tabItem->mask & TCIF_TEXT)
2684 Str_GetPtrW (wineItem->pszText, (WCHAR*)tabItem->pszText, tabItem->cchTextMax);
2686 Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2690 TAB_DumpItemExternalW((TCITEMW*)tabItem, iItem);
2692 TAB_DumpItemExternalA(tabItem, iItem);
2698 static LRESULT TAB_DeleteItem (TAB_INFO *infoPtr, INT iItem)
2700 BOOL bResult = FALSE;
2702 TRACE("(%p, %d)\n", infoPtr, iItem);
2704 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2706 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
2707 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2709 TAB_InvalidateTabArea(infoPtr);
2711 if ((item->mask & TCIF_TEXT) && item->pszText)
2712 Free(item->pszText);
2714 infoPtr->uNumItem--;
2716 if (!infoPtr->uNumItem)
2718 infoPtr->items = NULL;
2719 if (infoPtr->iHotTracked >= 0)
2721 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
2722 infoPtr->iHotTracked = -1;
2727 infoPtr->items = Alloc(TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2730 memcpy(infoPtr->items, oldItems, iItem * TAB_ITEM_SIZE(infoPtr));
2732 if (iItem < infoPtr->uNumItem)
2733 memcpy(TAB_GetItem(infoPtr, iItem),
2734 oldItems + (iItem + 1) * TAB_ITEM_SIZE(infoPtr),
2735 (infoPtr->uNumItem - iItem) * TAB_ITEM_SIZE(infoPtr));
2737 if (iItem <= infoPtr->iHotTracked)
2739 /* When tabs move left/up, the hot track item may change */
2740 FIXME("Recalc hot track");
2745 /* Readjust the selected index */
2746 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2747 infoPtr->iSelected--;
2749 if (iItem < infoPtr->iSelected)
2750 infoPtr->iSelected--;
2752 if (infoPtr->uNumItem == 0)
2753 infoPtr->iSelected = -1;
2755 /* Reposition and repaint tabs */
2756 TAB_SetItemBounds(infoPtr);
2764 static inline LRESULT TAB_DeleteAllItems (TAB_INFO *infoPtr)
2766 TRACE("(%p)\n", infoPtr);
2767 while (infoPtr->uNumItem)
2768 TAB_DeleteItem (infoPtr, 0);
2773 static inline LRESULT TAB_GetFont (const TAB_INFO *infoPtr)
2775 TRACE("(%p) returning %p\n", infoPtr, infoPtr->hFont);
2776 return (LRESULT)infoPtr->hFont;
2779 static inline LRESULT TAB_SetFont (TAB_INFO *infoPtr, HFONT hNewFont)
2781 TRACE("(%p,%p)\n", infoPtr, hNewFont);
2783 infoPtr->hFont = hNewFont;
2785 TAB_SetItemBounds(infoPtr);
2787 TAB_InvalidateTabArea(infoPtr);
2793 static inline LRESULT TAB_GetImageList (const TAB_INFO *infoPtr)
2796 return (LRESULT)infoPtr->himl;
2799 static inline LRESULT TAB_SetImageList (TAB_INFO *infoPtr, HIMAGELIST himlNew)
2801 HIMAGELIST himlPrev = infoPtr->himl;
2803 infoPtr->himl = himlNew;
2804 return (LRESULT)himlPrev;
2807 static inline LRESULT TAB_GetUnicodeFormat (const TAB_INFO *infoPtr)
2809 return infoPtr->bUnicode;
2812 static inline LRESULT TAB_SetUnicodeFormat (TAB_INFO *infoPtr, BOOL bUnicode)
2814 BOOL bTemp = infoPtr->bUnicode;
2816 infoPtr->bUnicode = bUnicode;
2821 static inline LRESULT TAB_Size (TAB_INFO *infoPtr)
2823 /* I'm not really sure what the following code was meant to do.
2824 This is what it is doing:
2825 When WM_SIZE is sent with SIZE_RESTORED, the control
2826 gets positioned in the top left corner.
2830 UINT uPosFlags,cx,cy;
2834 parent = GetParent (hwnd);
2835 GetClientRect(parent, &parent_rect);
2838 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2839 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2841 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2842 cx, cy, uPosFlags | SWP_NOZORDER);
2844 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2847 /* Recompute the size/position of the tabs. */
2848 TAB_SetItemBounds (infoPtr);
2850 /* Force a repaint of the control. */
2851 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2857 static LRESULT TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2860 TEXTMETRICA fontMetrics;
2865 infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
2867 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
2869 infoPtr->hwnd = hwnd;
2870 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
2871 infoPtr->uNumItem = 0;
2872 infoPtr->uNumRows = 0;
2873 infoPtr->uHItemPadding = 6;
2874 infoPtr->uVItemPadding = 3;
2875 infoPtr->uHItemPadding_s = 6;
2876 infoPtr->uVItemPadding_s = 3;
2879 infoPtr->hcurArrow = LoadCursorA (0, (LPSTR)IDC_ARROW);
2880 infoPtr->iSelected = -1;
2881 infoPtr->iHotTracked = -1;
2882 infoPtr->uFocus = -1;
2883 infoPtr->hwndToolTip = 0;
2884 infoPtr->DoRedraw = TRUE;
2885 infoPtr->needsScrolling = FALSE;
2886 infoPtr->hwndUpDown = 0;
2887 infoPtr->leftmostVisible = 0;
2888 infoPtr->fHeightSet = FALSE;
2889 infoPtr->bUnicode = IsWindowUnicode (hwnd);
2890 infoPtr->cbInfo = sizeof(LPARAM);
2892 TRACE("Created tab control, hwnd [%p]\n", hwnd);
2894 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2895 if you don't specify it in CreateWindow. This is necessary in
2896 order for paint to work correctly. This follows windows behaviour. */
2897 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
2898 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2900 if (dwStyle & TCS_TOOLTIPS) {
2901 /* Create tooltip control */
2902 infoPtr->hwndToolTip =
2903 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2904 CW_USEDEFAULT, CW_USEDEFAULT,
2905 CW_USEDEFAULT, CW_USEDEFAULT,
2908 /* Send NM_TOOLTIPSCREATED notification */
2909 if (infoPtr->hwndToolTip) {
2910 NMTOOLTIPSCREATED nmttc;
2912 nmttc.hdr.hwndFrom = hwnd;
2913 nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
2914 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2915 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2917 SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
2918 (WPARAM)GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc);
2923 * We need to get text information so we need a DC and we need to select
2927 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
2929 /* Use the system font to determine the initial height of a tab. */
2930 GetTextMetricsA(hdc, &fontMetrics);
2933 * Make sure there is enough space for the letters + growing the
2934 * selected item + extra space for the selected item.
2936 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
2937 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
2938 infoPtr->uVItemPadding;
2940 /* Initialize the width of a tab. */
2941 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
2942 infoPtr->tabMinWidth = 0;
2944 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
2946 SelectObject (hdc, hOldFont);
2947 ReleaseDC(hwnd, hdc);
2953 TAB_Destroy (TAB_INFO *infoPtr)
2960 if (infoPtr->items) {
2961 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
2962 if (TAB_GetItem(infoPtr, iItem)->pszText)
2963 Free (TAB_GetItem(infoPtr, iItem)->pszText);
2965 Free (infoPtr->items);
2968 if (infoPtr->hwndToolTip)
2969 DestroyWindow (infoPtr->hwndToolTip);
2971 if (infoPtr->hwndUpDown)
2972 DestroyWindow(infoPtr->hwndUpDown);
2974 if (infoPtr->iHotTracked >= 0)
2975 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
2978 SetWindowLongA(infoPtr->hwnd, 0, 0);
2982 static inline LRESULT
2983 TAB_SetItemExtra (TAB_INFO *infoPtr, INT cbInfo)
2985 if (!infoPtr || cbInfo <= 0)
2988 if (infoPtr->uNumItem)
2990 /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
2994 infoPtr->cbInfo = cbInfo;
2998 static LRESULT WINAPI
2999 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3001 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3003 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3004 if (!infoPtr && (uMsg != WM_CREATE))
3005 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
3009 case TCM_GETIMAGELIST:
3010 return TAB_GetImageList (infoPtr);
3012 case TCM_SETIMAGELIST:
3013 return TAB_SetImageList (infoPtr, (HIMAGELIST)lParam);
3015 case TCM_GETITEMCOUNT:
3016 return TAB_GetItemCount (infoPtr);
3020 return TAB_GetItemAW (infoPtr, (INT)wParam, (LPTCITEMA)lParam, uMsg == TCM_GETITEMW);
3024 return TAB_SetItemAW (infoPtr, (INT)wParam, (LPTCITEMA)lParam, uMsg == TCM_SETITEMW);
3026 case TCM_DELETEITEM:
3027 return TAB_DeleteItem (infoPtr, (INT)wParam);
3029 case TCM_DELETEALLITEMS:
3030 return TAB_DeleteAllItems (infoPtr);
3032 case TCM_GETITEMRECT:
3033 return TAB_GetItemRect (infoPtr, wParam, lParam);
3036 return TAB_GetCurSel (infoPtr);
3039 return TAB_HitTest (infoPtr, (LPTCHITTESTINFO)lParam);
3042 return TAB_SetCurSel (infoPtr, (INT)wParam);
3044 case TCM_INSERTITEMA:
3045 case TCM_INSERTITEMW:
3046 return TAB_InsertItemAW (infoPtr, wParam, lParam, uMsg == TCM_INSERTITEMW);
3048 case TCM_SETITEMEXTRA:
3049 return TAB_SetItemExtra (infoPtr, (int)wParam);
3051 case TCM_ADJUSTRECT:
3052 return TAB_AdjustRect (infoPtr, (BOOL)wParam, (LPRECT)lParam);
3054 case TCM_SETITEMSIZE:
3055 return TAB_SetItemSize (infoPtr, lParam);
3057 case TCM_REMOVEIMAGE:
3058 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3061 case TCM_SETPADDING:
3062 return TAB_SetPadding (infoPtr, lParam);
3064 case TCM_GETROWCOUNT:
3065 return TAB_GetRowCount(infoPtr);
3067 case TCM_GETUNICODEFORMAT:
3068 return TAB_GetUnicodeFormat (infoPtr);
3070 case TCM_SETUNICODEFORMAT:
3071 return TAB_SetUnicodeFormat (infoPtr, (BOOL)wParam);
3073 case TCM_HIGHLIGHTITEM:
3074 return TAB_HighlightItem (infoPtr, (INT)wParam, (BOOL)LOWORD(lParam));
3076 case TCM_GETTOOLTIPS:
3077 return TAB_GetToolTips (infoPtr);
3079 case TCM_SETTOOLTIPS:
3080 return TAB_SetToolTips (infoPtr, (HWND)wParam);
3082 case TCM_GETCURFOCUS:
3083 return TAB_GetCurFocus (infoPtr);
3085 case TCM_SETCURFOCUS:
3086 return TAB_SetCurFocus (infoPtr, (INT)wParam);
3088 case TCM_SETMINTABWIDTH:
3089 return TAB_SetMinTabWidth(infoPtr, (INT)lParam);
3091 case TCM_DESELECTALL:
3092 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3095 case TCM_GETEXTENDEDSTYLE:
3096 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3099 case TCM_SETEXTENDEDSTYLE:
3100 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3104 return TAB_GetFont (infoPtr);
3107 return TAB_SetFont (infoPtr, (HFONT)wParam);
3110 return TAB_Create (hwnd, wParam, lParam);
3113 return TAB_Destroy (infoPtr);
3116 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3118 case WM_LBUTTONDOWN:
3119 return TAB_LButtonDown (infoPtr, wParam, lParam);
3122 return TAB_LButtonUp (infoPtr);
3125 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3127 case WM_RBUTTONDOWN:
3128 return TAB_RButtonDown (infoPtr);
3131 return TAB_MouseMove (infoPtr, wParam, lParam);
3134 return TAB_Paint (infoPtr, (HDC)wParam);
3137 return TAB_Size (infoPtr);
3140 return TAB_SetRedraw (infoPtr, (BOOL)wParam);
3143 return TAB_OnHScroll(infoPtr, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3145 case WM_STYLECHANGED:
3146 TAB_SetItemBounds (infoPtr);
3147 InvalidateRect(hwnd, NULL, TRUE);
3150 case WM_SYSCOLORCHANGE:
3151 COMCTL32_RefreshSysColors();
3156 TAB_FocusChanging(infoPtr);
3157 break; /* Don't disturb normal focus behavior */
3160 return TAB_KeyUp(infoPtr, wParam);
3162 return TAB_NCHitTest(infoPtr, lParam);
3165 if (uMsg >= WM_USER && uMsg < WM_APP)
3166 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3167 uMsg, wParam, lParam);
3170 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
3179 ZeroMemory (&wndClass, sizeof(WNDCLASSW));
3180 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3181 wndClass.lpfnWndProc = TAB_WindowProc;
3182 wndClass.cbClsExtra = 0;
3183 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3184 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3185 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
3186 wndClass.lpszClassName = WC_TABCONTROLW;
3188 RegisterClassW (&wndClass);
3193 TAB_Unregister (void)
3195 UnregisterClassW (WC_TABCONTROLW, NULL);