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 *)GetWindowLongA(hwnd,0))
117 /******************************************************************************
118 * Hot-tracking timer constants
120 #define TAB_HOTTRACK_TIMER 1
121 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
123 /******************************************************************************
126 static void TAB_Refresh (HWND hwnd, HDC hdc);
127 static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
128 static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
129 static void TAB_DrawItem(HWND hwnd, HDC hdc, INT iItem);
130 static void TAB_DrawItemInterior(HWND hwnd, HDC hdc, INT iItem, RECT* drawRect);
133 TAB_SendSimpleNotify (HWND hwnd, UINT code)
135 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
138 nmhdr.hwndFrom = hwnd;
139 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
142 return (BOOL) SendMessageA (infoPtr->hwndNotify, WM_NOTIFY,
143 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
147 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
148 WPARAM wParam, LPARAM lParam)
156 msg.time = GetMessageTime ();
157 msg.pt.x = LOWORD(GetMessagePos ());
158 msg.pt.y = HIWORD(GetMessagePos ());
160 SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
164 TAB_DumpItemExternalA(TCITEMA *pti, UINT iItem)
167 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
168 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
169 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextA=%s\n",
170 iItem, pti->iImage, pti->lParam, debugstr_a(pti->pszText));
176 TAB_DumpItemExternalW(TCITEMW *pti, UINT iItem)
179 TRACE("external tab %d, mask=0x%08x, dwState=0x%08lx, dwStateMask=0x%08lx, cchTextMax=0x%08x\n",
180 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
181 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
182 iItem, pti->iImage, pti->lParam, debugstr_w(pti->pszText));
187 TAB_DumpItemInternal(TAB_INFO *infoPtr, UINT iItem)
192 ti = &infoPtr->items[iItem];
193 TRACE("tab %d, mask=0x%08x, dwState=0x%08lx, pszText=%s, iImage=%d\n",
194 iItem, ti->mask, ti->dwState, debugstr_w(ti->pszText),
196 TRACE("tab %d, rect.left=%ld, rect.top(row)=%ld\n",
197 iItem, ti->rect.left, ti->rect.top);
202 TAB_GetCurSel (HWND hwnd)
204 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
206 return infoPtr->iSelected;
210 TAB_GetCurFocus (HWND hwnd)
212 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
214 return infoPtr->uFocus;
218 TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
220 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
222 if (infoPtr == NULL) return 0;
223 return (LRESULT)infoPtr->hwndToolTip;
227 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
229 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
230 INT iItem = (INT)wParam;
234 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
235 prevItem=infoPtr->iSelected;
236 infoPtr->iSelected=iItem;
237 TAB_EnsureSelectionVisible(hwnd, infoPtr);
238 TAB_InvalidateTabArea(hwnd, infoPtr);
244 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
246 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
247 INT iItem=(INT) wParam;
249 if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
251 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
252 FIXME("Should set input focus\n");
254 int oldFocus = infoPtr->uFocus;
255 if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) {
256 infoPtr->uFocus = iItem;
257 if (oldFocus != -1) {
258 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
259 infoPtr->iSelected = iItem;
260 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
263 infoPtr->iSelected = iItem;
264 TAB_EnsureSelectionVisible(hwnd, infoPtr);
265 TAB_InvalidateTabArea(hwnd, infoPtr);
273 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
275 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
277 if (infoPtr == NULL) return 0;
278 infoPtr->hwndToolTip = (HWND)wParam;
283 TAB_SetPadding (HWND hwnd, WPARAM wParam, LPARAM lParam)
285 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
287 if (infoPtr == NULL) return 0;
288 infoPtr->uHItemPadding_s=LOWORD(lParam);
289 infoPtr->uVItemPadding_s=HIWORD(lParam);
293 /******************************************************************************
294 * TAB_InternalGetItemRect
296 * This method will calculate the rectangle representing a given tab item in
297 * client coordinates. This method takes scrolling into account.
299 * This method returns TRUE if the item is visible in the window and FALSE
300 * if it is completely outside the client area.
302 static BOOL TAB_InternalGetItemRect(
309 RECT tmpItemRect,clientRect;
310 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
312 /* Perform a sanity check and a trivial visibility check. */
313 if ( (infoPtr->uNumItem <= 0) ||
314 (itemIndex >= infoPtr->uNumItem) ||
315 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
319 * Avoid special cases in this procedure by assigning the "out"
320 * parameters if the caller didn't supply them
322 if (itemRect == NULL)
323 itemRect = &tmpItemRect;
325 /* Retrieve the unmodified item rect. */
326 *itemRect = infoPtr->items[itemIndex].rect;
328 /* calculate the times bottom and top based on the row */
329 GetClientRect(hwnd, &clientRect);
331 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
333 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * infoPtr->tabHeight -
334 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
335 itemRect->left = itemRect->right - infoPtr->tabHeight;
337 else if (lStyle & TCS_VERTICAL)
339 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * infoPtr->tabHeight +
340 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
341 itemRect->right = itemRect->left + infoPtr->tabHeight;
343 else if (lStyle & TCS_BOTTOM)
345 itemRect->bottom = clientRect.bottom - itemRect->top * infoPtr->tabHeight -
346 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
347 itemRect->top = itemRect->bottom - infoPtr->tabHeight;
349 else /* not TCS_BOTTOM and not TCS_VERTICAL */
351 itemRect->top = clientRect.top + itemRect->top * infoPtr->tabHeight +
352 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
353 itemRect->bottom = itemRect->top + infoPtr->tabHeight;
357 * "scroll" it to make sure the item at the very left of the
358 * tab control is the leftmost visible tab.
360 if(lStyle & TCS_VERTICAL)
364 -infoPtr->items[infoPtr->leftmostVisible].rect.top);
367 * Move the rectangle so the first item is slightly offset from
368 * the bottom of the tab control.
372 SELECTED_TAB_OFFSET);
377 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
381 * Move the rectangle so the first item is slightly offset from
382 * the left of the tab control.
388 TRACE("item %d tab h=%d, rect=(%ld,%ld)-(%ld,%ld)\n",
389 itemIndex, infoPtr->tabHeight,
390 itemRect->left, itemRect->top, itemRect->right, itemRect->bottom);
392 /* Now, calculate the position of the item as if it were selected. */
393 if (selectedRect!=NULL)
395 CopyRect(selectedRect, itemRect);
397 /* The rectangle of a selected item is a bit wider. */
398 if(lStyle & TCS_VERTICAL)
399 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
401 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
403 /* If it also a bit higher. */
404 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
406 selectedRect->left -= 2; /* the border is thicker on the right */
407 selectedRect->right += SELECTED_TAB_OFFSET;
409 else if (lStyle & TCS_VERTICAL)
411 selectedRect->left -= SELECTED_TAB_OFFSET;
412 selectedRect->right += 1;
414 else if (lStyle & TCS_BOTTOM)
416 selectedRect->bottom += SELECTED_TAB_OFFSET;
418 else /* not TCS_BOTTOM and not TCS_VERTICAL */
420 selectedRect->top -= SELECTED_TAB_OFFSET;
421 selectedRect->bottom -= 1;
428 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
430 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
431 (LPRECT)lParam, (LPRECT)NULL);
434 /******************************************************************************
437 * This method is called to handle keyboard input
439 static LRESULT TAB_KeyUp(
443 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
449 newItem = infoPtr->uFocus - 1;
452 newItem = infoPtr->uFocus + 1;
457 * If we changed to a valid item, change the selection
459 if ((newItem >= 0) &&
460 (newItem < infoPtr->uNumItem) &&
461 (infoPtr->uFocus != newItem))
463 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
465 infoPtr->iSelected = newItem;
466 infoPtr->uFocus = newItem;
467 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
469 TAB_EnsureSelectionVisible(hwnd, infoPtr);
470 TAB_InvalidateTabArea(hwnd, infoPtr);
477 /******************************************************************************
480 * This method is called whenever the focus goes in or out of this control
481 * it is used to update the visual state of the control.
483 static LRESULT TAB_FocusChanging(
489 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
494 * Get the rectangle for the item.
496 isVisible = TAB_InternalGetItemRect(hwnd,
503 * If the rectangle is not completely invisible, invalidate that
504 * portion of the window.
508 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
509 selectedRect.left,selectedRect.top,
510 selectedRect.right,selectedRect.bottom);
511 InvalidateRect(hwnd, &selectedRect, TRUE);
515 * Don't otherwise disturb normal behavior.
517 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
520 static INT TAB_InternalHitTest (
530 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
532 TAB_InternalGetItemRect(hwnd, infoPtr, iCount, &rect, NULL);
534 if (PtInRect(&rect, pt))
536 *flags = TCHT_ONITEM;
541 *flags = TCHT_NOWHERE;
546 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
548 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
549 LPTCHITTESTINFO lptest = (LPTCHITTESTINFO) lParam;
551 return TAB_InternalHitTest (hwnd, infoPtr, lptest->pt, &lptest->flags);
554 /******************************************************************************
557 * Napster v2b5 has a tab control for its main navigation which has a client
558 * area that covers the whole area of the dialog pages.
559 * That's why it receives all msgs for that area and the underlying dialog ctrls
561 * So I decided that we should handle WM_NCHITTEST here and return
562 * HTTRANSPARENT if we don't hit the tab control buttons.
563 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
564 * doesn't do it that way. Maybe depends on tab control styles ?
567 TAB_NCHitTest (HWND hwnd, LPARAM lParam)
569 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
573 pt.x = LOWORD(lParam);
574 pt.y = HIWORD(lParam);
575 ScreenToClient(hwnd, &pt);
577 if (TAB_InternalHitTest(hwnd, infoPtr, pt, &dummyflag) == -1)
578 return HTTRANSPARENT;
584 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
586 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
590 if (infoPtr->hwndToolTip)
591 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
592 WM_LBUTTONDOWN, wParam, lParam);
594 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
598 if (infoPtr->hwndToolTip)
599 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
600 WM_LBUTTONDOWN, wParam, lParam);
602 pt.x = (INT)LOWORD(lParam);
603 pt.y = (INT)HIWORD(lParam);
605 newItem = TAB_InternalHitTest (hwnd, infoPtr, pt, &dummy);
607 TRACE("On Tab, item %d\n", newItem);
609 if ((newItem != -1) && (infoPtr->iSelected != newItem))
611 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING) != TRUE)
613 infoPtr->iSelected = newItem;
614 infoPtr->uFocus = newItem;
615 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
617 TAB_EnsureSelectionVisible(hwnd, infoPtr);
619 TAB_InvalidateTabArea(hwnd, infoPtr);
626 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
628 TAB_SendSimpleNotify(hwnd, NM_CLICK);
634 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
636 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
640 /******************************************************************************
641 * TAB_DrawLoneItemInterior
643 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
644 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
645 * up the device context and font. This routine does the same setup but
646 * only calls TAB_DrawItemInterior for the single specified item.
649 TAB_DrawLoneItemInterior(HWND hwnd, TAB_INFO* infoPtr, int iItem)
651 HDC hdc = GetDC(hwnd);
654 /* Clip UpDown control to not draw over it */
655 if (infoPtr->needsScrolling)
657 GetWindowRect(hwnd, &rC);
658 GetWindowRect(infoPtr->hwndUpDown, &r);
659 ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top);
661 TAB_DrawItemInterior(hwnd, hdc, iItem, NULL);
662 ReleaseDC(hwnd, hdc);
665 /******************************************************************************
666 * TAB_HotTrackTimerProc
668 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
669 * timer is setup so we can check if the mouse is moved out of our window.
670 * (We don't get an event when the mouse leaves, the mouse-move events just
671 * stop being delivered to our window and just start being delivered to
672 * another window.) This function is called when the timer triggers so
673 * we can check if the mouse has left our window. If so, we un-highlight
674 * the hot-tracked tab.
677 TAB_HotTrackTimerProc
679 HWND hwnd, /* handle of window for timer messages */
680 UINT uMsg, /* WM_TIMER message */
681 UINT idEvent, /* timer identifier */
682 DWORD dwTime /* current system time */
685 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
687 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
692 ** If we can't get the cursor position, or if the cursor is outside our
693 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
694 ** "outside" even if it is within our bounding rect if another window
695 ** overlaps. Note also that the case where the cursor stayed within our
696 ** window but has moved off the hot-tracked tab will be handled by the
697 ** WM_MOUSEMOVE event.
699 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
701 /* Redraw iHotTracked to look normal */
702 INT iRedraw = infoPtr->iHotTracked;
703 infoPtr->iHotTracked = -1;
704 TAB_DrawLoneItemInterior(hwnd, infoPtr, iRedraw);
706 /* Kill this timer */
707 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
712 /******************************************************************************
715 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
716 * should be highlighted. This function determines which tab in a tab control,
717 * if any, is under the mouse and records that information. The caller may
718 * supply output parameters to receive the item number of the tab item which
719 * was highlighted but isn't any longer and of the tab item which is now
720 * highlighted but wasn't previously. The caller can use this information to
721 * selectively redraw those tab items.
723 * If the caller has a mouse position, it can supply it through the pos
724 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
725 * supplies NULL and this function determines the current mouse position
733 int* out_redrawLeave,
737 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
742 if (out_redrawLeave != NULL)
743 *out_redrawLeave = -1;
744 if (out_redrawEnter != NULL)
745 *out_redrawEnter = -1;
747 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_HOTTRACK)
755 ScreenToClient(hwnd, &pt);
763 item = TAB_InternalHitTest(hwnd, infoPtr, pt, &flags);
766 if (item != infoPtr->iHotTracked)
768 if (infoPtr->iHotTracked >= 0)
770 /* Mark currently hot-tracked to be redrawn to look normal */
771 if (out_redrawLeave != NULL)
772 *out_redrawLeave = infoPtr->iHotTracked;
776 /* Kill timer which forces recheck of mouse pos */
777 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
782 /* Start timer so we recheck mouse pos */
783 UINT timerID = SetTimer
787 TAB_HOTTRACK_TIMER_INTERVAL,
788 TAB_HotTrackTimerProc
792 return; /* Hot tracking not available */
795 infoPtr->iHotTracked = item;
799 /* Mark new hot-tracked to be redrawn to look highlighted */
800 if (out_redrawEnter != NULL)
801 *out_redrawEnter = item;
806 /******************************************************************************
809 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
812 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
817 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
819 if (infoPtr->hwndToolTip)
820 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
821 WM_LBUTTONDOWN, wParam, lParam);
823 /* Determine which tab to highlight. Redraw tabs which change highlight
825 TAB_RecalcHotTrack(hwnd, &lParam, &redrawLeave, &redrawEnter);
827 if (redrawLeave != -1)
828 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawLeave);
829 if (redrawEnter != -1)
830 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawEnter);
835 /******************************************************************************
838 * Calculates the tab control's display area given the window rectangle or
839 * the window rectangle given the requested display rectangle.
841 static LRESULT TAB_AdjustRect(
846 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
847 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
848 LONG *iRightBottom, *iLeftTop;
850 TRACE ("hwnd=%p fLarger=%d (%ld,%ld)-(%ld,%ld)\n", hwnd, fLarger, prc->left, prc->top, prc->right, prc->bottom);
852 if(lStyle & TCS_VERTICAL)
854 iRightBottom = &(prc->right);
855 iLeftTop = &(prc->left);
859 iRightBottom = &(prc->bottom);
860 iLeftTop = &(prc->top);
863 if (fLarger) /* Go from display rectangle */
865 /* Add the height of the tabs. */
866 if (lStyle & TCS_BOTTOM)
867 *iRightBottom += infoPtr->tabHeight * infoPtr->uNumRows;
869 *iLeftTop -= infoPtr->tabHeight * infoPtr->uNumRows +
870 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
872 /* Inflate the rectangle for the padding */
873 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
875 /* Inflate for the border */
876 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEY);
878 else /* Go from window rectangle. */
880 /* Deflate the rectangle for the border */
881 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEY);
883 /* Deflate the rectangle for the padding */
884 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
886 /* Remove the height of the tabs. */
887 if (lStyle & TCS_BOTTOM)
888 *iRightBottom -= infoPtr->tabHeight * infoPtr->uNumRows;
890 *iLeftTop += (infoPtr->tabHeight) * infoPtr->uNumRows +
891 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
897 /******************************************************************************
900 * This method will handle the notification from the scroll control and
901 * perform the scrolling operation on the tab control.
903 static LRESULT TAB_OnHScroll(
909 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
911 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
913 if(nPos < infoPtr->leftmostVisible)
914 infoPtr->leftmostVisible--;
916 infoPtr->leftmostVisible++;
918 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
919 TAB_InvalidateTabArea(hwnd, infoPtr);
920 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
921 MAKELONG(infoPtr->leftmostVisible, 0));
927 /******************************************************************************
930 * This method will check the current scrolling state and make sure the
931 * scrolling control is displayed (or not).
933 static void TAB_SetupScrolling(
936 const RECT* clientRect)
939 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
941 if (infoPtr->needsScrolling)
947 * Calculate the position of the scroll control.
949 if(lStyle & TCS_VERTICAL)
951 controlPos.right = clientRect->right;
952 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
954 if (lStyle & TCS_BOTTOM)
956 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
957 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
961 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
962 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
967 controlPos.right = clientRect->right;
968 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
970 if (lStyle & TCS_BOTTOM)
972 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
973 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
977 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
978 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
983 * If we don't have a scroll control yet, we want to create one.
984 * If we have one, we want to make sure it's positioned properly.
986 if (infoPtr->hwndUpDown==0)
988 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
990 WS_VISIBLE | WS_CHILD | UDS_HORZ,
991 controlPos.left, controlPos.top,
992 controlPos.right - controlPos.left,
993 controlPos.bottom - controlPos.top,
1001 SetWindowPos(infoPtr->hwndUpDown,
1003 controlPos.left, controlPos.top,
1004 controlPos.right - controlPos.left,
1005 controlPos.bottom - controlPos.top,
1006 SWP_SHOWWINDOW | SWP_NOZORDER);
1009 /* Now calculate upper limit of the updown control range.
1010 * We do this by calculating how many tabs will be offscreen when the
1011 * last tab is visible.
1013 if(infoPtr->uNumItem)
1015 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
1016 maxRange = infoPtr->uNumItem;
1017 tabwidth = infoPtr->items[maxRange - 1].rect.right;
1019 for(; maxRange > 0; maxRange--)
1021 if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
1025 if(maxRange == infoPtr->uNumItem)
1031 /* If we once had a scroll control... hide it */
1032 if (infoPtr->hwndUpDown!=0)
1033 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1035 if (infoPtr->hwndUpDown)
1036 SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1039 /******************************************************************************
1042 * This method will calculate the position rectangles of all the items in the
1043 * control. The rectangle calculated starts at 0 for the first item in the
1044 * list and ignores scrolling and selection.
1045 * It also uses the current font to determine the height of the tab row and
1046 * it checks if all the tabs fit in the client area of the window. If they
1047 * don't, a scrolling control is added.
1049 static void TAB_SetItemBounds (HWND hwnd)
1051 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1052 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1053 TEXTMETRICA fontMetrics;
1056 INT curItemRowCount;
1057 HFONT hFont, hOldFont;
1067 * We need to get text information so we need a DC and we need to select
1072 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1073 hOldFont = SelectObject (hdc, hFont);
1076 * We will base the rectangle calculations on the client rectangle
1079 GetClientRect(hwnd, &clientRect);
1081 /* if TCS_VERTICAL then swap the height and width so this code places the
1082 tabs along the top of the rectangle and we can just rotate them after
1083 rather than duplicate all of the below code */
1084 if(lStyle & TCS_VERTICAL)
1086 iTemp = clientRect.bottom;
1087 clientRect.bottom = clientRect.right;
1088 clientRect.right = iTemp;
1091 /* Now use hPadding and vPadding */
1092 infoPtr->uHItemPadding = infoPtr->uHItemPadding_s;
1093 infoPtr->uVItemPadding = infoPtr->uVItemPadding_s;
1095 /* The leftmost item will be "0" aligned */
1097 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1099 if (!(infoPtr->fHeightSet))
1102 int icon_height = 0;
1104 /* Use the current font to determine the height of a tab. */
1105 GetTextMetricsA(hdc, &fontMetrics);
1107 /* Get the icon height */
1109 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1111 /* Take the highest between font or icon */
1112 if (fontMetrics.tmHeight > icon_height)
1113 item_height = fontMetrics.tmHeight + 2;
1115 item_height = icon_height;
1118 * Make sure there is enough space for the letters + icon + growing the
1119 * selected item + extra space for the selected item.
1121 infoPtr->tabHeight = item_height +
1122 ((lStyle & TCS_BUTTONS) ? 2 : 1) *
1123 infoPtr->uVItemPadding;
1125 TRACE("tabH=%d, tmH=%ld, iconh=%d\n",
1126 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1129 TRACE("client right=%ld\n", clientRect.right);
1131 /* Get the icon width */
1134 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1136 if (lStyle & TCS_FIXEDWIDTH)
1139 /* Add padding if icon is present */
1140 icon_width += infoPtr->uHItemPadding;
1143 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1145 /* Set the leftmost position of the tab. */
1146 infoPtr->items[curItem].rect.left = curItemLeftPos;
1148 if ((lStyle & TCS_FIXEDWIDTH) || !infoPtr->items[curItem].pszText)
1150 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1151 max(infoPtr->tabWidth, icon_width);
1157 /* Calculate how wide the tab is depending on the text it contains */
1158 GetTextExtentPoint32W(hdc, infoPtr->items[curItem].pszText,
1159 lstrlenW(infoPtr->items[curItem].pszText), &size);
1161 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1162 size.cx + icon_width +
1163 num * infoPtr->uHItemPadding;
1164 TRACE("for <%s>, l,r=%ld,%ld, num=%d\n",
1165 debugstr_w(infoPtr->items[curItem].pszText),
1166 infoPtr->items[curItem].rect.left,
1167 infoPtr->items[curItem].rect.right,
1172 * Check if this is a multiline tab control and if so
1173 * check to see if we should wrap the tabs
1175 * Wrap all these tabs. We will arrange them evenly later.
1179 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1180 (infoPtr->items[curItem].rect.right >
1181 (clientRect.right - CONTROL_BORDER_SIZEX - DISPLAY_AREA_PADDINGX)))
1183 infoPtr->items[curItem].rect.right -=
1184 infoPtr->items[curItem].rect.left;
1186 infoPtr->items[curItem].rect.left = 0;
1188 TRACE("wrapping <%s>, l,r=%ld,%ld\n",
1189 debugstr_w(infoPtr->items[curItem].pszText),
1190 infoPtr->items[curItem].rect.left,
1191 infoPtr->items[curItem].rect.right);
1194 infoPtr->items[curItem].rect.bottom = 0;
1195 infoPtr->items[curItem].rect.top = curItemRowCount - 1;
1197 TRACE("TextSize: %li\n", size.cx);
1198 TRACE("Rect: T %li, L %li, B %li, R %li\n",
1199 infoPtr->items[curItem].rect.top,
1200 infoPtr->items[curItem].rect.left,
1201 infoPtr->items[curItem].rect.bottom,
1202 infoPtr->items[curItem].rect.right);
1205 * The leftmost position of the next item is the rightmost position
1208 if (lStyle & TCS_BUTTONS)
1210 curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX;
1211 if (lStyle & TCS_FLATBUTTONS)
1212 curItemLeftPos += FLAT_BTN_SPACINGX;
1215 curItemLeftPos = infoPtr->items[curItem].rect.right;
1218 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1221 * Check if we need a scrolling control.
1223 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1226 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1227 if(!infoPtr->needsScrolling)
1228 infoPtr->leftmostVisible = 0;
1233 * No scrolling in Multiline or Vertical styles.
1235 infoPtr->needsScrolling = FALSE;
1236 infoPtr->leftmostVisible = 0;
1238 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
1240 /* Set the number of rows */
1241 infoPtr->uNumRows = curItemRowCount;
1243 /* Arrange all tabs evenly if style says so */
1244 if (!(lStyle & TCS_RAGGEDRIGHT) && ((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->uNumItem > 0))
1246 INT tabPerRow,remTab;
1251 * Ok windows tries to even out the rows. place the same
1252 * number of tabs in each row. So lets give that a shot
1255 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1256 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1258 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1259 iItm<infoPtr->uNumItem;
1262 /* normalize the current rect */
1264 /* shift the item to the left side of the clientRect */
1265 infoPtr->items[iItm].rect.right -=
1266 infoPtr->items[iItm].rect.left;
1267 infoPtr->items[iItm].rect.left = 0;
1269 TRACE("r=%ld, cl=%d, cl.r=%ld, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1270 infoPtr->items[iItm].rect.right,
1271 curItemLeftPos, clientRect.right,
1272 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1274 /* if we have reached the maximum number of tabs on this row */
1275 /* move to the next row, reset our current item left position and */
1276 /* the count of items on this row */
1278 if (lStyle & TCS_VERTICAL) {
1279 /* Vert: Add the remaining tabs in the *last* remainder rows */
1280 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1286 /* Horz: Add the remaining tabs in the *first* remainder rows */
1287 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1294 /* shift the item to the right to place it as the next item in this row */
1295 infoPtr->items[iItm].rect.left += curItemLeftPos;
1296 infoPtr->items[iItm].rect.right += curItemLeftPos;
1297 infoPtr->items[iItm].rect.top = iRow;
1298 if (lStyle & TCS_BUTTONS)
1300 curItemLeftPos = infoPtr->items[iItm].rect.right + 1;
1301 if (lStyle & TCS_FLATBUTTONS)
1302 curItemLeftPos += FLAT_BTN_SPACINGX;
1305 curItemLeftPos = infoPtr->items[iItm].rect.right;
1307 TRACE("arranging <%s>, l,r=%ld,%ld, row=%ld\n",
1308 debugstr_w(infoPtr->items[iItm].pszText),
1309 infoPtr->items[iItm].rect.left,
1310 infoPtr->items[iItm].rect.right,
1311 infoPtr->items[iItm].rect.top);
1318 INT widthDiff, iIndexStart=0, iIndexEnd=0;
1322 while(iIndexStart < infoPtr->uNumItem)
1325 * find the indexs of the row
1327 /* find the first item on the next row */
1328 for (iIndexEnd=iIndexStart;
1329 (iIndexEnd < infoPtr->uNumItem) &&
1330 (infoPtr->items[iIndexEnd].rect.top ==
1331 infoPtr->items[iIndexStart].rect.top) ;
1333 /* intentionally blank */;
1336 * we need to justify these tabs so they fill the whole given
1340 /* find the amount of space remaining on this row */
1341 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1342 infoPtr->items[iIndexEnd - 1].rect.right;
1344 /* iCount is the number of tab items on this row */
1345 iCount = iIndexEnd - iIndexStart;
1349 remainder = widthDiff % iCount;
1350 widthDiff = widthDiff / iCount;
1351 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1352 for (iIndex=iIndexStart, iCount=0; iIndex < iIndexEnd; iIndex++, iCount++)
1354 infoPtr->items[iIndex].rect.left += iCount * widthDiff;
1355 infoPtr->items[iIndex].rect.right += (iCount + 1) * widthDiff;
1357 TRACE("adjusting 1 <%s>, l,r=%ld,%ld\n",
1358 debugstr_w(infoPtr->items[iIndex].pszText),
1359 infoPtr->items[iIndex].rect.left,
1360 infoPtr->items[iIndex].rect.right);
1363 infoPtr->items[iIndex - 1].rect.right += remainder;
1365 else /* we have only one item on this row, make it take up the entire row */
1367 infoPtr->items[iIndexStart].rect.left = clientRect.left;
1368 infoPtr->items[iIndexStart].rect.right = clientRect.right - 4;
1370 TRACE("adjusting 2 <%s>, l,r=%ld,%ld\n",
1371 debugstr_w(infoPtr->items[iIndexStart].pszText),
1372 infoPtr->items[iIndexStart].rect.left,
1373 infoPtr->items[iIndexStart].rect.right);
1378 iIndexStart = iIndexEnd;
1383 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1384 if(lStyle & TCS_VERTICAL)
1387 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1389 rcItem = &(infoPtr->items[iIndex].rect);
1391 rcOriginal = *rcItem;
1393 /* this is rotating the items by 90 degrees clockwise around the center of the control */
1394 rcItem->top = (rcOriginal.left - clientRect.left);
1395 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1396 rcItem->left = rcOriginal.top;
1397 rcItem->right = rcOriginal.bottom;
1401 TAB_EnsureSelectionVisible(hwnd,infoPtr);
1402 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1405 SelectObject (hdc, hOldFont);
1406 ReleaseDC (hwnd, hdc);
1411 TAB_EraseTabInterior
1419 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1420 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1421 HBRUSH hbr = CreateSolidBrush (comctl32_color.clrBtnFace);
1422 BOOL deleteBrush = TRUE;
1423 RECT rTemp = *drawRect;
1425 InflateRect(&rTemp, -2, -2);
1426 if (lStyle & TCS_BUTTONS)
1428 if (iItem == infoPtr->iSelected)
1430 /* Background color */
1431 if (!(lStyle & TCS_OWNERDRAWFIXED))
1434 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1436 SetTextColor(hdc, comctl32_color.clr3dFace);
1437 SetBkColor(hdc, comctl32_color.clr3dHilight);
1439 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1440 * we better use 0x55aa bitmap brush to make scrollbar's background
1441 * look different from the window background.
1443 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1444 hbr = COMCTL32_hPattern55AABrush;
1446 deleteBrush = FALSE;
1448 FillRect(hdc, &rTemp, hbr);
1450 else /* ! selected */
1452 if (lStyle & TCS_FLATBUTTONS)
1454 FillRect(hdc, drawRect, hbr);
1455 if (iItem == infoPtr->iHotTracked)
1456 DrawEdge(hdc, drawRect, EDGE_RAISED, BF_SOFT|BF_RECT);
1459 FillRect(hdc, &rTemp, hbr);
1463 else /* !TCS_BUTTONS */
1465 FillRect(hdc, &rTemp, hbr);
1469 if (deleteBrush) DeleteObject(hbr);
1472 /******************************************************************************
1473 * TAB_DrawItemInterior
1475 * This method is used to draw the interior (text and icon) of a single tab
1476 * into the tab control.
1479 TAB_DrawItemInterior
1487 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1488 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1497 /* if (drawRect == NULL) */
1504 * Get the rectangle for the item.
1506 isVisible = TAB_InternalGetItemRect(hwnd, infoPtr, iItem, &itemRect, &selectedRect);
1511 * Make sure drawRect points to something valid; simplifies code.
1513 drawRect = &localRect;
1516 * This logic copied from the part of TAB_DrawItem which draws
1517 * the tab background. It's important to keep it in sync. I
1518 * would have liked to avoid code duplication, but couldn't figure
1519 * out how without making spaghetti of TAB_DrawItem.
1521 if (iItem == infoPtr->iSelected)
1522 *drawRect = selectedRect;
1524 *drawRect = itemRect;
1526 if (lStyle & TCS_BUTTONS)
1528 if (iItem == infoPtr->iSelected)
1530 drawRect->left += 4;
1532 drawRect->right -= 4;
1533 drawRect->bottom -= 1;
1537 drawRect->left += 2;
1539 drawRect->right -= 2;
1540 drawRect->bottom -= 2;
1545 if ((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1547 if (iItem != infoPtr->iSelected)
1549 drawRect->left += 2;
1551 drawRect->bottom -= 2;
1554 else if (lStyle & TCS_VERTICAL)
1556 if (iItem == infoPtr->iSelected)
1558 drawRect->right += 1;
1563 drawRect->right -= 2;
1564 drawRect->bottom -= 2;
1567 else if (lStyle & TCS_BOTTOM)
1569 if (iItem == infoPtr->iSelected)
1575 InflateRect(drawRect, -2, -2);
1576 drawRect->bottom += 2;
1581 if (iItem == infoPtr->iSelected)
1583 drawRect->bottom += 3;
1587 drawRect->bottom -= (infoPtr->items[iItem].rect.top != infoPtr->uNumRows-1)? 2:2;
1588 InflateRect(drawRect, -2, 0);
1593 TRACE("drawRect=(%ld,%ld)-(%ld,%ld)\n",
1594 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom);
1596 /* Clear interior */
1597 TAB_EraseTabInterior (hwnd, hdc, iItem, drawRect);
1599 /* Draw the focus rectangle */
1600 if (!(lStyle & TCS_FOCUSNEVER) &&
1601 (GetFocus() == hwnd) &&
1602 (iItem == infoPtr->uFocus) )
1604 RECT rFocus = *drawRect;
1605 InflateRect(&rFocus, -3, -3);
1606 if (lStyle & TCS_BOTTOM && !(lStyle & TCS_VERTICAL))
1608 if (lStyle & TCS_BUTTONS)
1614 DrawFocusRect(hdc, &rFocus);
1620 htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1621 holdPen = SelectObject(hdc, htextPen);
1622 hOldFont = SelectObject(hdc, infoPtr->hFont);
1625 * Setup for text output
1627 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1628 SetTextColor(hdc, (((iItem == infoPtr->iHotTracked) && !(lStyle & TCS_FLATBUTTONS)) |
1629 (infoPtr->items[iItem].dwState & TCIS_HIGHLIGHTED)) ?
1630 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1633 * if owner draw, tell the owner to draw
1635 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd))
1641 * get the control id
1643 id = GetWindowLongA( hwnd, GWL_ID );
1646 * put together the DRAWITEMSTRUCT
1648 dis.CtlType = ODT_TAB;
1651 dis.itemAction = ODA_DRAWENTIRE;
1653 if ( iItem == infoPtr->iSelected )
1654 dis.itemState |= ODS_SELECTED;
1655 if (infoPtr->uFocus == iItem)
1656 dis.itemState |= ODS_FOCUS;
1657 dis.hwndItem = hwnd; /* */
1659 CopyRect(&dis.rcItem,drawRect);
1661 memcpy( &dis.itemData, infoPtr->items[iItem].extra, min(sizeof(dis.itemData),infoPtr->cbInfo) );
1664 * send the draw message
1666 SendMessageA( infoPtr->hwndNotify, WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1673 /* used to center the icon and text in the tab */
1675 INT center_offset_h, center_offset_v;
1677 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1678 rcImage = *drawRect;
1682 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1684 /* get the rectangle that the text fits in */
1685 if (infoPtr->items[iItem].pszText)
1687 DrawTextW(hdc, infoPtr->items[iItem].pszText, -1,
1688 &rcText, DT_CALCRECT);
1691 * If not owner draw, then do the drawing ourselves.
1695 if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE))
1700 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1702 if(lStyle & TCS_VERTICAL)
1704 center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1705 center_offset_v = ((drawRect->right - drawRect->left) - (cx + infoPtr->uVItemPadding)) / 2;
1709 center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1710 center_offset_v = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uVItemPadding)) / 2;
1713 if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1714 center_offset_h = infoPtr->uHItemPadding;
1716 if (center_offset_h < 2)
1717 center_offset_h = 2;
1719 if (center_offset_v < 0)
1720 center_offset_v = 0;
1722 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1723 debugstr_w(infoPtr->items[iItem].pszText), center_offset_h, center_offset_v,
1724 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1725 (rcText.right-rcText.left));
1727 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1729 rcImage.top = drawRect->top + center_offset_h;
1730 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1731 /* right side of the tab, but the image still uses the left as its x position */
1732 /* this keeps the image always drawn off of the same side of the tab */
1733 rcImage.left = drawRect->right - cx - center_offset_v;
1734 drawRect->top += cy + infoPtr->uHItemPadding;
1736 else if(lStyle & TCS_VERTICAL)
1738 rcImage.top = drawRect->bottom - cy - center_offset_h;
1739 rcImage.left = drawRect->left + center_offset_v;
1740 drawRect->bottom -= cy + infoPtr->uHItemPadding;
1742 else /* normal style, whether TCS_BOTTOM or not */
1744 rcImage.left = drawRect->left + center_offset_h;
1745 rcImage.top = drawRect->top + center_offset_v;
1746 drawRect->left += cx + infoPtr->uHItemPadding;
1749 TRACE("drawing image=%d, left=%ld, top=%ld\n",
1750 infoPtr->items[iItem].iImage, rcImage.left, rcImage.top-1);
1754 infoPtr->items[iItem].iImage,
1762 /* Now position text */
1763 if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT)
1764 center_offset_h = infoPtr->uHItemPadding;
1766 if(lStyle & TCS_VERTICAL)
1767 center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1769 center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1771 if(lStyle & TCS_VERTICAL)
1773 if(lStyle & TCS_BOTTOM)
1774 drawRect->top+=center_offset_h;
1776 drawRect->bottom-=center_offset_h;
1778 center_offset_v = ((drawRect->right - drawRect->left) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1782 drawRect->left += center_offset_h;
1783 center_offset_v = ((drawRect->bottom - drawRect->top) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1786 if (center_offset_v < 0)
1787 center_offset_v = 0;
1789 if(lStyle & TCS_VERTICAL)
1790 drawRect->left += center_offset_v;
1792 drawRect->top += center_offset_v;
1795 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1799 INT nEscapement = 900;
1800 INT nOrientation = 900;
1802 if(lStyle & TCS_BOTTOM)
1805 nOrientation = -900;
1808 /* to get a font with the escapement and orientation we are looking for, we need to */
1809 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1810 if (!GetObjectA((infoPtr->hFont) ?
1811 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1812 sizeof(LOGFONTA),&logfont))
1816 lstrcpyA(logfont.lfFaceName, "Arial");
1817 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1819 logfont.lfWeight = FW_NORMAL;
1820 logfont.lfItalic = 0;
1821 logfont.lfUnderline = 0;
1822 logfont.lfStrikeOut = 0;
1825 logfont.lfEscapement = nEscapement;
1826 logfont.lfOrientation = nOrientation;
1827 hFont = CreateFontIndirectA(&logfont);
1828 SelectObject(hdc, hFont);
1830 if (infoPtr->items[iItem].pszText)
1833 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1834 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1837 infoPtr->items[iItem].pszText,
1838 lstrlenW(infoPtr->items[iItem].pszText),
1842 DeleteObject(hFont);
1846 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1847 debugstr_w(infoPtr->items[iItem].pszText), center_offset_h, center_offset_v,
1848 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1849 (rcText.right-rcText.left));
1850 if (infoPtr->items[iItem].pszText)
1855 infoPtr->items[iItem].pszText,
1856 lstrlenW(infoPtr->items[iItem].pszText),
1858 DT_LEFT | DT_SINGLELINE
1863 *drawRect = rcTemp; /* restore drawRect */
1869 SelectObject(hdc, hOldFont);
1870 SetBkMode(hdc, oldBkMode);
1871 SelectObject(hdc, holdPen);
1872 DeleteObject( htextPen );
1875 /******************************************************************************
1878 * This method is used to draw a single tab into the tab control.
1880 static void TAB_DrawItem(
1885 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1886 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1890 RECT r, fillRect, r1;
1893 COLORREF bkgnd, corner;
1896 * Get the rectangle for the item.
1898 isVisible = TAB_InternalGetItemRect(hwnd,
1908 /* Clip UpDown control to not draw over it */
1909 if (infoPtr->needsScrolling)
1911 GetWindowRect(hwnd, &rC);
1912 GetWindowRect(infoPtr->hwndUpDown, &rUD);
1913 ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
1916 /* If you need to see what the control is doing,
1917 * then override these variables. They will change what
1918 * fill colors are used for filling the tabs, and the
1919 * corners when drawing the edge.
1921 bkgnd = comctl32_color.clrBtnFace;
1922 corner = comctl32_color.clrBtnFace;
1924 if (lStyle & TCS_BUTTONS)
1926 /* Get item rectangle */
1929 /* Separators between flat buttons */
1930 if (lStyle & TCS_FLATBUTTONS)
1933 r1.right += (FLAT_BTN_SPACINGX -2);
1934 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1937 if (iItem == infoPtr->iSelected)
1939 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1941 OffsetRect(&r, 1, 1);
1943 else /* ! selected */
1945 if (!(lStyle & TCS_FLATBUTTONS))
1946 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1949 else /* !TCS_BUTTONS */
1951 /* We draw a rectangle of different sizes depending on the selection
1953 if (iItem == infoPtr->iSelected) {
1955 GetClientRect (hwnd, &rect);
1956 clRight = rect.right;
1957 clBottom = rect.bottom;
1964 * Erase the background. (Delay it but setup rectangle.)
1965 * This is necessary when drawing the selected item since it is larger
1966 * than the others, it might overlap with stuff already drawn by the
1971 if(lStyle & TCS_VERTICAL)
1973 /* These are for adjusting the drawing of a Selected tab */
1974 /* The initial values are for the normal case of non-Selected */
1975 int ZZ = 1; /* Do not strech if selected */
1976 if (iItem == infoPtr->iSelected) {
1979 /* if leftmost draw the line longer */
1980 if(selectedRect.top == 0)
1981 fillRect.top += CONTROL_BORDER_SIZEY;
1982 /* if rightmost draw the line longer */
1983 if(selectedRect.bottom == clBottom)
1984 fillRect.bottom -= CONTROL_BORDER_SIZEY;
1987 if (lStyle & TCS_BOTTOM)
1989 /* Adjust both rectangles to match native */
1992 TRACE("<right> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1994 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1995 r.left,r.top,r.right,r.bottom);
1997 /* Clear interior */
1998 SetBkColor(hdc, bkgnd);
1999 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2001 /* Draw rectangular edge around tab */
2002 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
2004 /* Now erase the top corner and draw diagonal edge */
2005 SetBkColor(hdc, corner);
2006 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2009 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2010 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2012 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2014 /* Now erase the bottom corner and draw diagonal edge */
2015 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2016 r1.bottom = r.bottom;
2018 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2019 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2021 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2023 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
2027 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
2033 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2035 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2036 r.left,r.top,r.right,r.bottom);
2038 /* Clear interior */
2039 SetBkColor(hdc, bkgnd);
2040 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2042 /* Draw rectangular edge around tab */
2043 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2045 /* Now erase the top corner and draw diagonal edge */
2046 SetBkColor(hdc, corner);
2049 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2050 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2051 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2053 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2055 /* Now erase the bottom corner and draw diagonal edge */
2057 r1.bottom = r.bottom;
2058 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2059 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2060 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2062 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2065 else /* ! TCS_VERTICAL */
2067 /* These are for adjusting the drawing of a Selected tab */
2068 /* The initial values are for the normal case of non-Selected */
2069 if (iItem == infoPtr->iSelected) {
2070 /* if leftmost draw the line longer */
2071 if(selectedRect.left == 0)
2072 fillRect.left += CONTROL_BORDER_SIZEX;
2073 /* if rightmost draw the line longer */
2074 if(selectedRect.right == clRight)
2075 fillRect.right -= CONTROL_BORDER_SIZEX;
2078 if (lStyle & TCS_BOTTOM)
2080 /* Adjust both rectangles for topmost row */
2081 if (infoPtr->items[iItem].rect.top == infoPtr->uNumRows-1)
2087 TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2089 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2090 r.left,r.top,r.right,r.bottom);
2092 /* Clear interior */
2093 SetBkColor(hdc, bkgnd);
2094 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2096 /* Draw rectangular edge around tab */
2097 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2099 /* Now erase the righthand corner and draw diagonal edge */
2100 SetBkColor(hdc, corner);
2101 r1.left = r.right - ROUND_CORNER_SIZE;
2102 r1.bottom = r.bottom;
2104 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2105 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2107 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2109 /* Now erase the lefthand corner and draw diagonal edge */
2111 r1.bottom = r.bottom;
2112 r1.right = r1.left + ROUND_CORNER_SIZE;
2113 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2114 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2116 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2118 if (iItem == infoPtr->iSelected)
2122 if (selectedRect.left == 0)
2127 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2134 /* Adjust both rectangles for bottommost row */
2135 if (infoPtr->items[iItem].rect.top == infoPtr->uNumRows-1)
2137 fillRect.bottom += 3;
2141 TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2143 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2144 r.left,r.top,r.right,r.bottom);
2146 /* Clear interior */
2147 SetBkColor(hdc, bkgnd);
2148 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2150 /* Draw rectangular edge around tab */
2151 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2153 /* Now erase the righthand corner and draw diagonal edge */
2154 SetBkColor(hdc, corner);
2155 r1.left = r.right - ROUND_CORNER_SIZE;
2158 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2159 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2161 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2163 /* Now erase the lefthand corner and draw diagonal edge */
2166 r1.right = r1.left + ROUND_CORNER_SIZE;
2167 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2168 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2170 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2175 TAB_DumpItemInternal(infoPtr, iItem);
2177 /* This modifies r to be the text rectangle. */
2178 TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
2182 /******************************************************************************
2185 * This method is used to draw the raised border around the tab control
2188 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
2190 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2192 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2194 GetClientRect (hwnd, &rect);
2197 * Adjust for the style
2200 if (infoPtr->uNumItem)
2202 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2203 rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2204 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2205 rect.right -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2206 else if(lStyle & TCS_VERTICAL)
2207 rect.left += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2208 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2209 rect.top += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2212 TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2213 rect.left, rect.top, rect.right, rect.bottom);
2215 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2218 /******************************************************************************
2221 * This method repaints the tab control..
2223 static void TAB_Refresh (HWND hwnd, HDC hdc)
2225 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2229 if (!infoPtr->DoRedraw)
2232 hOldFont = SelectObject (hdc, infoPtr->hFont);
2234 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
2236 for (i = 0; i < infoPtr->uNumItem; i++)
2237 TAB_DrawItem (hwnd, hdc, i);
2241 /* Draw all the non selected item first */
2242 for (i = 0; i < infoPtr->uNumItem; i++)
2244 if (i != infoPtr->iSelected)
2245 TAB_DrawItem (hwnd, hdc, i);
2248 /* Now, draw the border, draw it before the selected item
2249 * since the selected item overwrites part of the border. */
2250 TAB_DrawBorder (hwnd, hdc);
2252 /* Then, draw the selected item */
2253 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
2255 /* If we haven't set the current focus yet, set it now.
2256 * Only happens when we first paint the tab controls */
2257 if (infoPtr->uFocus == -1)
2258 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
2261 SelectObject (hdc, hOldFont);
2265 TAB_GetRowCount (HWND hwnd )
2267 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2269 return infoPtr->uNumRows;
2273 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
2275 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2277 infoPtr->DoRedraw=(BOOL) wParam;
2281 static LRESULT TAB_EraseBackground(
2288 HBRUSH brush = CreateSolidBrush(comctl32_color.clrBtnFace);
2290 hdc = givenDC ? givenDC : GetDC(hwnd);
2292 GetClientRect(hwnd, &clientRect);
2294 FillRect(hdc, &clientRect, brush);
2297 ReleaseDC(hwnd, hdc);
2299 DeleteObject(brush);
2304 /******************************************************************************
2305 * TAB_EnsureSelectionVisible
2307 * This method will make sure that the current selection is completely
2308 * visible by scrolling until it is.
2310 static void TAB_EnsureSelectionVisible(
2314 INT iSelected = infoPtr->iSelected;
2315 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2316 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2318 /* set the items row to the bottommost row or topmost row depending on
2320 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2325 if(lStyle & TCS_VERTICAL)
2326 newselected = infoPtr->items[iSelected].rect.left;
2328 newselected = infoPtr->items[iSelected].rect.top;
2330 /* the target row is always (number of rows - 1)
2331 as row 0 is furthest from the clientRect */
2332 iTargetRow = infoPtr->uNumRows - 1;
2334 if (newselected != iTargetRow)
2337 if(lStyle & TCS_VERTICAL)
2339 for (i=0; i < infoPtr->uNumItem; i++)
2341 /* move everything in the row of the selected item to the iTargetRow */
2342 if (infoPtr->items[i].rect.left == newselected )
2343 infoPtr->items[i].rect.left = iTargetRow;
2346 if (infoPtr->items[i].rect.left > newselected)
2347 infoPtr->items[i].rect.left-=1;
2353 for (i=0; i < infoPtr->uNumItem; i++)
2355 if (infoPtr->items[i].rect.top == newselected )
2356 infoPtr->items[i].rect.top = iTargetRow;
2359 if (infoPtr->items[i].rect.top > newselected)
2360 infoPtr->items[i].rect.top-=1;
2364 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2369 * Do the trivial cases first.
2371 if ( (!infoPtr->needsScrolling) ||
2372 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2375 if (infoPtr->leftmostVisible >= iSelected)
2377 infoPtr->leftmostVisible = iSelected;
2384 /* Calculate the part of the client area that is visible */
2385 GetClientRect(hwnd, &r);
2388 GetClientRect(infoPtr->hwndUpDown, &r);
2391 if ((infoPtr->items[iSelected].rect.right -
2392 infoPtr->items[iSelected].rect.left) >= width )
2394 /* Special case: width of selected item is greater than visible
2397 infoPtr->leftmostVisible = iSelected;
2401 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2403 if ((infoPtr->items[iSelected].rect.right -
2404 infoPtr->items[i].rect.left) < width)
2407 infoPtr->leftmostVisible = i;
2411 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2412 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2414 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2415 MAKELONG(infoPtr->leftmostVisible, 0));
2418 /******************************************************************************
2419 * TAB_InvalidateTabArea
2421 * This method will invalidate the portion of the control that contains the
2422 * tabs. It is called when the state of the control changes and needs
2425 static void TAB_InvalidateTabArea(
2429 RECT clientRect, rInvalidate, rAdjClient;
2430 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2431 INT lastRow = infoPtr->uNumRows - 1;
2434 if (lastRow < 0) return;
2436 GetClientRect(hwnd, &clientRect);
2437 rInvalidate = clientRect;
2438 rAdjClient = clientRect;
2440 TAB_AdjustRect(hwnd, 0, &rAdjClient);
2442 TAB_InternalGetItemRect(hwnd, infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2443 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2445 rInvalidate.left = rAdjClient.right;
2446 if (infoPtr->uNumRows == 1)
2447 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2449 else if(lStyle & TCS_VERTICAL)
2451 rInvalidate.right = rAdjClient.left;
2452 if (infoPtr->uNumRows == 1)
2453 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2455 else if (lStyle & TCS_BOTTOM)
2457 rInvalidate.top = rAdjClient.bottom;
2458 if (infoPtr->uNumRows == 1)
2459 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2463 rInvalidate.bottom = rAdjClient.top;
2464 if (infoPtr->uNumRows == 1)
2465 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2468 /* Punch out the updown control */
2469 if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2471 GetClientRect(infoPtr->hwndUpDown, &r);
2472 if (rInvalidate.right > clientRect.right - r.left)
2473 rInvalidate.right = rInvalidate.right - (r.right - r.left);
2475 rInvalidate.right = clientRect.right - r.left;
2478 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2479 rInvalidate.left, rInvalidate.top,
2480 rInvalidate.right, rInvalidate.bottom);
2482 InvalidateRect(hwnd, &rInvalidate, TRUE);
2486 TAB_Paint (HWND hwnd, WPARAM wParam)
2493 hdc = BeginPaint (hwnd, &ps);
2494 TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2496 ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2499 TAB_EraseBackground (hwnd, hdc);
2505 TAB_Refresh (hwnd, hdc);
2508 EndPaint (hwnd, &ps);
2514 TAB_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2516 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2521 GetClientRect (hwnd, &rect);
2522 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2523 rect.top, rect.left, rect.bottom, rect.right);
2525 pti = (TCITEMA *)lParam;
2526 iItem = (INT)wParam;
2528 if (iItem < 0) return -1;
2529 if (iItem > infoPtr->uNumItem)
2530 iItem = infoPtr->uNumItem;
2532 TAB_DumpItemExternalA(pti, iItem);
2535 if (infoPtr->uNumItem == 0) {
2536 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2537 infoPtr->uNumItem++;
2538 infoPtr->iSelected = 0;
2541 TAB_ITEM *oldItems = infoPtr->items;
2543 infoPtr->uNumItem++;
2544 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2546 /* pre insert copy */
2548 memcpy (&infoPtr->items[0], &oldItems[0],
2549 iItem * TAB_ITEM_SIZE(infoPtr));
2552 /* post insert copy */
2553 if (iItem < infoPtr->uNumItem - 1) {
2554 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2555 (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2559 if (iItem <= infoPtr->iSelected)
2560 infoPtr->iSelected++;
2565 infoPtr->items[iItem].mask = pti->mask;
2566 if (pti->mask & TCIF_TEXT)
2567 Str_SetPtrAtoW (&infoPtr->items[iItem].pszText, pti->pszText);
2569 if (pti->mask & TCIF_IMAGE)
2570 infoPtr->items[iItem].iImage = pti->iImage;
2572 if (pti->mask & TCIF_PARAM)
2573 memcpy(infoPtr->items[iItem].extra, &pti->lParam, infoPtr->cbInfo);
2575 memset(infoPtr->items[iItem].extra, 0, infoPtr->cbInfo);
2577 TAB_SetItemBounds(hwnd);
2578 if (infoPtr->uNumItem > 1)
2579 TAB_InvalidateTabArea(hwnd, infoPtr);
2581 InvalidateRect(hwnd, NULL, TRUE);
2583 TRACE("[%p]: added item %d %s\n",
2584 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2591 TAB_InsertItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2593 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2598 GetClientRect (hwnd, &rect);
2599 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2600 rect.top, rect.left, rect.bottom, rect.right);
2602 pti = (TCITEMW *)lParam;
2603 iItem = (INT)wParam;
2605 if (iItem < 0) return -1;
2606 if (iItem > infoPtr->uNumItem)
2607 iItem = infoPtr->uNumItem;
2609 TAB_DumpItemExternalW(pti, iItem);
2611 if (infoPtr->uNumItem == 0) {
2612 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2613 infoPtr->uNumItem++;
2614 infoPtr->iSelected = 0;
2617 TAB_ITEM *oldItems = infoPtr->items;
2619 infoPtr->uNumItem++;
2620 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2622 /* pre insert copy */
2624 memcpy (&infoPtr->items[0], &oldItems[0],
2625 iItem * TAB_ITEM_SIZE(infoPtr));
2628 /* post insert copy */
2629 if (iItem < infoPtr->uNumItem - 1) {
2630 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2631 (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2635 if (iItem <= infoPtr->iSelected)
2636 infoPtr->iSelected++;
2641 infoPtr->items[iItem].mask = pti->mask;
2642 if (pti->mask & TCIF_TEXT)
2643 Str_SetPtrW (&infoPtr->items[iItem].pszText, pti->pszText);
2645 if (pti->mask & TCIF_IMAGE)
2646 infoPtr->items[iItem].iImage = pti->iImage;
2648 if (pti->mask & TCIF_PARAM)
2649 memcpy(infoPtr->items[iItem].extra, &pti->lParam, infoPtr->cbInfo);
2651 memset(infoPtr->items[iItem].extra, 0, infoPtr->cbInfo);
2653 TAB_SetItemBounds(hwnd);
2654 if (infoPtr->uNumItem > 1)
2655 TAB_InvalidateTabArea(hwnd, infoPtr);
2657 InvalidateRect(hwnd, NULL, TRUE);
2659 TRACE("[%p]: added item %d %s\n",
2660 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2667 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2669 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2670 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2672 BOOL bNeedPaint = FALSE;
2674 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2676 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2677 if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2679 infoPtr->tabWidth = max((INT)LOWORD(lParam), infoPtr->tabMinWidth);
2683 if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2685 if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2686 infoPtr->tabHeight = (INT)HIWORD(lParam);
2690 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2691 HIWORD(lResult), LOWORD(lResult),
2692 infoPtr->tabHeight, infoPtr->tabWidth);
2696 TAB_SetItemBounds(hwnd);
2697 RedrawWindow(hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2704 TAB_SetMinTabWidth (HWND hwnd, LPARAM lParam)
2706 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2707 INT cx = (INT)lParam;
2711 oldcx = infoPtr->tabMinWidth;
2712 infoPtr->tabMinWidth = (cx==-1)?DEFAULT_TAB_WIDTH:cx;
2720 TAB_HighlightItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2722 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2723 INT iItem = (INT)wParam;
2724 BOOL fHighlight = (BOOL)LOWORD(lParam);
2726 if ((infoPtr) && (iItem>=0) && (iItem<infoPtr->uNumItem)) {
2728 infoPtr->items[iItem].dwState |= TCIS_HIGHLIGHTED;
2730 infoPtr->items[iItem].dwState &= ~TCIS_HIGHLIGHTED;
2738 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2740 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2745 iItem = (INT)wParam;
2746 tabItem = (LPTCITEMA)lParam;
2748 TRACE("%d %p\n", iItem, tabItem);
2749 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2751 TAB_DumpItemExternalA(tabItem, iItem);
2753 wineItem = &infoPtr->items[iItem];
2755 if (tabItem->mask & TCIF_IMAGE)
2756 wineItem->iImage = tabItem->iImage;
2758 if (tabItem->mask & TCIF_PARAM)
2759 memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2761 if (tabItem->mask & TCIF_RTLREADING)
2762 FIXME("TCIF_RTLREADING\n");
2764 if (tabItem->mask & TCIF_STATE)
2765 wineItem->dwState = tabItem->dwState;
2767 if (tabItem->mask & TCIF_TEXT)
2768 Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2770 /* Update and repaint tabs */
2771 TAB_SetItemBounds(hwnd);
2772 TAB_InvalidateTabArea(hwnd,infoPtr);
2779 TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2781 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2786 iItem = (INT)wParam;
2787 tabItem = (LPTCITEMW)lParam;
2789 TRACE("%d %p\n", iItem, tabItem);
2790 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2792 TAB_DumpItemExternalW(tabItem, iItem);
2794 wineItem = &infoPtr->items[iItem];
2796 if (tabItem->mask & TCIF_IMAGE)
2797 wineItem->iImage = tabItem->iImage;
2799 if (tabItem->mask & TCIF_PARAM)
2800 memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2802 if (tabItem->mask & TCIF_RTLREADING)
2803 FIXME("TCIF_RTLREADING\n");
2805 if (tabItem->mask & TCIF_STATE)
2806 wineItem->dwState = tabItem->dwState;
2808 if (tabItem->mask & TCIF_TEXT)
2809 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2811 /* Update and repaint tabs */
2812 TAB_SetItemBounds(hwnd);
2813 TAB_InvalidateTabArea(hwnd,infoPtr);
2820 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2822 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2824 return infoPtr->uNumItem;
2829 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2831 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2836 iItem = (INT)wParam;
2837 tabItem = (LPTCITEMA)lParam;
2839 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2842 wineItem = &infoPtr->items[iItem];
2844 if (tabItem->mask & TCIF_IMAGE)
2845 tabItem->iImage = wineItem->iImage;
2847 if (tabItem->mask & TCIF_PARAM)
2848 memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2850 if (tabItem->mask & TCIF_RTLREADING)
2851 FIXME("TCIF_RTLREADING\n");
2853 if (tabItem->mask & TCIF_STATE)
2854 tabItem->dwState = wineItem->dwState;
2856 if (tabItem->mask & TCIF_TEXT)
2857 Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2859 TAB_DumpItemExternalA(tabItem, iItem);
2866 TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2868 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2873 iItem = (INT)wParam;
2874 tabItem = (LPTCITEMW)lParam;
2876 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2879 wineItem=& infoPtr->items[iItem];
2881 if (tabItem->mask & TCIF_IMAGE)
2882 tabItem->iImage = wineItem->iImage;
2884 if (tabItem->mask & TCIF_PARAM)
2885 memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2887 if (tabItem->mask & TCIF_RTLREADING)
2888 FIXME("TCIF_RTLREADING\n");
2890 if (tabItem->mask & TCIF_STATE)
2891 tabItem->dwState = wineItem->dwState;
2893 if (tabItem->mask & TCIF_TEXT)
2894 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2896 TAB_DumpItemExternalW(tabItem, iItem);
2903 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2905 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2906 INT iItem = (INT) wParam;
2907 BOOL bResult = FALSE;
2909 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2911 TAB_ITEM *oldItems = infoPtr->items;
2913 TAB_InvalidateTabArea(hwnd, infoPtr);
2915 infoPtr->uNumItem--;
2916 infoPtr->items = Alloc(TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2919 memcpy(&infoPtr->items[0], &oldItems[0], iItem * TAB_ITEM_SIZE(infoPtr));
2921 if (iItem < infoPtr->uNumItem)
2922 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
2923 (infoPtr->uNumItem - iItem) * TAB_ITEM_SIZE(infoPtr));
2927 /* Readjust the selected index */
2928 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2929 infoPtr->iSelected--;
2931 if (iItem < infoPtr->iSelected)
2932 infoPtr->iSelected--;
2934 if (infoPtr->uNumItem == 0)
2935 infoPtr->iSelected = -1;
2937 /* Reposition and repaint tabs */
2938 TAB_SetItemBounds(hwnd);
2947 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2949 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2951 TAB_InvalidateTabArea(hwnd,infoPtr);
2953 Free (infoPtr->items);
2954 infoPtr->uNumItem = 0;
2955 infoPtr->iSelected = -1;
2956 if (infoPtr->iHotTracked >= 0)
2957 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2958 infoPtr->iHotTracked = -1;
2960 TAB_SetItemBounds(hwnd);
2966 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2968 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2971 return (LRESULT)infoPtr->hFont;
2975 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2978 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2980 TRACE("%x %lx\n",wParam, lParam);
2982 infoPtr->hFont = (HFONT)wParam;
2984 TAB_SetItemBounds(hwnd);
2986 TAB_InvalidateTabArea(hwnd, infoPtr);
2993 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2995 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2998 return (LRESULT)infoPtr->himl;
3002 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
3004 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3005 HIMAGELIST himlPrev;
3008 himlPrev = infoPtr->himl;
3009 infoPtr->himl= (HIMAGELIST)lParam;
3010 return (LRESULT)himlPrev;
3014 TAB_GetUnicodeFormat (HWND hwnd)
3016 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
3017 return infoPtr->bUnicode;
3021 TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
3023 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
3024 BOOL bTemp = infoPtr->bUnicode;
3026 infoPtr->bUnicode = (BOOL)wParam;
3032 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
3035 /* I'm not really sure what the following code was meant to do.
3036 This is what it is doing:
3037 When WM_SIZE is sent with SIZE_RESTORED, the control
3038 gets positioned in the top left corner.
3042 UINT uPosFlags,cx,cy;
3046 parent = GetParent (hwnd);
3047 GetClientRect(parent, &parent_rect);
3050 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
3051 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
3053 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
3054 cx, cy, uPosFlags | SWP_NOZORDER);
3056 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
3059 /* Recompute the size/position of the tabs. */
3060 TAB_SetItemBounds (hwnd);
3062 /* Force a repaint of the control. */
3063 InvalidateRect(hwnd, NULL, TRUE);
3070 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
3073 TEXTMETRICA fontMetrics;
3078 infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
3080 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
3082 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
3083 infoPtr->uNumItem = 0;
3084 infoPtr->uNumRows = 0;
3085 infoPtr->uHItemPadding = 6;
3086 infoPtr->uVItemPadding = 3;
3087 infoPtr->uHItemPadding_s = 6;
3088 infoPtr->uVItemPadding_s = 3;
3091 infoPtr->hcurArrow = LoadCursorA (0, (LPSTR)IDC_ARROW);
3092 infoPtr->iSelected = -1;
3093 infoPtr->iHotTracked = -1;
3094 infoPtr->uFocus = -1;
3095 infoPtr->hwndToolTip = 0;
3096 infoPtr->DoRedraw = TRUE;
3097 infoPtr->needsScrolling = FALSE;
3098 infoPtr->hwndUpDown = 0;
3099 infoPtr->leftmostVisible = 0;
3100 infoPtr->fHeightSet = FALSE;
3101 infoPtr->bUnicode = IsWindowUnicode (hwnd);
3102 infoPtr->cbInfo = sizeof(LPARAM);
3104 TRACE("Created tab control, hwnd [%p]\n", hwnd);
3106 /* The tab control always has the WS_CLIPSIBLINGS style. Even
3107 if you don't specify it in CreateWindow. This is necessary in
3108 order for paint to work correctly. This follows windows behaviour. */
3109 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
3110 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
3112 if (dwStyle & TCS_TOOLTIPS) {
3113 /* Create tooltip control */
3114 infoPtr->hwndToolTip =
3115 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
3116 CW_USEDEFAULT, CW_USEDEFAULT,
3117 CW_USEDEFAULT, CW_USEDEFAULT,
3120 /* Send NM_TOOLTIPSCREATED notification */
3121 if (infoPtr->hwndToolTip) {
3122 NMTOOLTIPSCREATED nmttc;
3124 nmttc.hdr.hwndFrom = hwnd;
3125 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
3126 nmttc.hdr.code = NM_TOOLTIPSCREATED;
3127 nmttc.hwndToolTips = infoPtr->hwndToolTip;
3129 SendMessageA (infoPtr->hwndNotify, WM_NOTIFY,
3130 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
3135 * We need to get text information so we need a DC and we need to select
3139 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3141 /* Use the system font to determine the initial height of a tab. */
3142 GetTextMetricsA(hdc, &fontMetrics);
3145 * Make sure there is enough space for the letters + growing the
3146 * selected item + extra space for the selected item.
3148 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3149 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
3150 infoPtr->uVItemPadding;
3152 /* Initialize the width of a tab. */
3153 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
3154 infoPtr->tabMinWidth = 0;
3156 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3158 SelectObject (hdc, hOldFont);
3159 ReleaseDC(hwnd, hdc);
3165 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
3167 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3173 if (infoPtr->items) {
3174 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3175 if (infoPtr->items[iItem].pszText)
3176 Free (infoPtr->items[iItem].pszText);
3178 Free (infoPtr->items);
3181 if (infoPtr->hwndToolTip)
3182 DestroyWindow (infoPtr->hwndToolTip);
3184 if (infoPtr->hwndUpDown)
3185 DestroyWindow(infoPtr->hwndUpDown);
3187 if (infoPtr->iHotTracked >= 0)
3188 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
3191 SetWindowLongA(hwnd, 0, 0);
3196 TAB_SetItemExtra (HWND hwnd, WPARAM wParam, LPARAM lParam)
3198 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3199 INT cbInfo = wParam;
3201 if (!infoPtr || cbInfo <= 0)
3204 if (infoPtr->uNumItem)
3206 /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
3210 infoPtr->cbInfo = cbInfo;
3214 static LRESULT WINAPI
3215 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3217 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3219 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3220 if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
3221 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3225 case TCM_GETIMAGELIST:
3226 return TAB_GetImageList (hwnd, wParam, lParam);
3228 case TCM_SETIMAGELIST:
3229 return TAB_SetImageList (hwnd, wParam, lParam);
3231 case TCM_GETITEMCOUNT:
3232 return TAB_GetItemCount (hwnd, wParam, lParam);
3235 return TAB_GetItemA (hwnd, wParam, lParam);
3238 return TAB_GetItemW (hwnd, wParam, lParam);
3241 return TAB_SetItemA (hwnd, wParam, lParam);
3244 return TAB_SetItemW (hwnd, wParam, lParam);
3246 case TCM_DELETEITEM:
3247 return TAB_DeleteItem (hwnd, wParam, lParam);
3249 case TCM_DELETEALLITEMS:
3250 return TAB_DeleteAllItems (hwnd, wParam, lParam);
3252 case TCM_GETITEMRECT:
3253 return TAB_GetItemRect (hwnd, wParam, lParam);
3256 return TAB_GetCurSel (hwnd);
3259 return TAB_HitTest (hwnd, wParam, lParam);
3262 return TAB_SetCurSel (hwnd, wParam);
3264 case TCM_INSERTITEMA:
3265 return TAB_InsertItemA (hwnd, wParam, lParam);
3267 case TCM_INSERTITEMW:
3268 return TAB_InsertItemW (hwnd, wParam, lParam);
3270 case TCM_SETITEMEXTRA:
3271 return TAB_SetItemExtra (hwnd, wParam, lParam);
3273 case TCM_ADJUSTRECT:
3274 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
3276 case TCM_SETITEMSIZE:
3277 return TAB_SetItemSize (hwnd, wParam, lParam);
3279 case TCM_REMOVEIMAGE:
3280 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3283 case TCM_SETPADDING:
3284 return TAB_SetPadding (hwnd, wParam, lParam);
3286 case TCM_GETROWCOUNT:
3287 return TAB_GetRowCount(hwnd);
3289 case TCM_GETUNICODEFORMAT:
3290 return TAB_GetUnicodeFormat (hwnd);
3292 case TCM_SETUNICODEFORMAT:
3293 return TAB_SetUnicodeFormat (hwnd, wParam);
3295 case TCM_HIGHLIGHTITEM:
3296 return TAB_HighlightItem (hwnd, wParam, lParam);
3298 case TCM_GETTOOLTIPS:
3299 return TAB_GetToolTips (hwnd, wParam, lParam);
3301 case TCM_SETTOOLTIPS:
3302 return TAB_SetToolTips (hwnd, wParam, lParam);
3304 case TCM_GETCURFOCUS:
3305 return TAB_GetCurFocus (hwnd);
3307 case TCM_SETCURFOCUS:
3308 return TAB_SetCurFocus (hwnd, wParam);
3310 case TCM_SETMINTABWIDTH:
3311 return TAB_SetMinTabWidth(hwnd, lParam);
3313 case TCM_DESELECTALL:
3314 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3317 case TCM_GETEXTENDEDSTYLE:
3318 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3321 case TCM_SETEXTENDEDSTYLE:
3322 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3326 return TAB_GetFont (hwnd, wParam, lParam);
3329 return TAB_SetFont (hwnd, wParam, lParam);
3332 return TAB_Create (hwnd, wParam, lParam);
3335 return TAB_Destroy (hwnd, wParam, lParam);
3338 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3340 case WM_LBUTTONDOWN:
3341 return TAB_LButtonDown (hwnd, wParam, lParam);
3344 return TAB_LButtonUp (hwnd, wParam, lParam);
3347 return SendMessageA(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3349 case WM_RBUTTONDOWN:
3350 return TAB_RButtonDown (hwnd, wParam, lParam);
3353 return TAB_MouseMove (hwnd, wParam, lParam);
3356 return TAB_EraseBackground (hwnd, (HDC)wParam);
3359 return TAB_Paint (hwnd, wParam);
3362 return TAB_Size (hwnd, wParam, lParam);
3365 return TAB_SetRedraw (hwnd, wParam);
3368 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3370 case WM_STYLECHANGED:
3371 TAB_SetItemBounds (hwnd);
3372 InvalidateRect(hwnd, NULL, TRUE);
3375 case WM_SYSCOLORCHANGE:
3376 COMCTL32_RefreshSysColors();
3381 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
3384 return TAB_KeyUp(hwnd, wParam);
3386 return TAB_NCHitTest(hwnd, lParam);
3389 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
3390 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3391 uMsg, wParam, lParam);
3392 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
3404 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3405 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3406 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
3407 wndClass.cbClsExtra = 0;
3408 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3409 wndClass.hCursor = LoadCursorA (0, (LPSTR)IDC_ARROW);
3410 wndClass.hbrBackground = NULL;
3411 wndClass.lpszClassName = WC_TABCONTROLA;
3413 RegisterClassA (&wndClass);
3418 TAB_Unregister (void)
3420 UnregisterClassA (WC_TABCONTROLA, NULL);