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)
33 * TCM_GETEXTENDEDSTYLE
34 * TCM_SETEXTENDEDSTYLE
48 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(tab);
60 RECT rect; /* bounding rectangle of the item relative to the
61 * leftmost item (the leftmost item, 0, would have a
62 * "left" member of 0 in this rectangle)
64 * additionally the top member hold the row number
65 * and bottom is unused and should be 0 */
70 HWND hwndNotify; /* notification window (parent) */
71 UINT uNumItem; /* number of tab items */
72 UINT uNumRows; /* number of tab rows */
73 INT tabHeight; /* height of the tab row */
74 INT tabWidth; /* width of tabs */
75 INT tabMinWidth; /* minimum width of items */
76 USHORT uHItemPadding; /* amount of horizontal padding, in pixels */
77 USHORT uVItemPadding; /* amount of vertical padding, in pixels */
78 USHORT uHItemPadding_s; /* Set amount of horizontal padding, in pixels */
79 USHORT uVItemPadding_s; /* Set amount of vertical padding, in pixels */
80 HFONT hFont; /* handle to the current font */
81 HCURSOR hcurArrow; /* handle to the current cursor */
82 HIMAGELIST himl; /* handle to a image list (may be 0) */
83 HWND hwndToolTip; /* handle to tab's tooltip */
84 INT leftmostVisible; /* Used for scrolling, this member contains
85 * the index of the first visible item */
86 INT iSelected; /* the currently selected item */
87 INT iHotTracked; /* the highlighted item under the mouse */
88 INT uFocus; /* item which has the focus */
89 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
90 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
91 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
92 * the size of the control */
93 BOOL fHeightSet; /* was the height of the tabs explicitly set? */
94 BOOL bUnicode; /* Unicode control? */
95 HWND hwndUpDown; /* Updown control used for scrolling */
98 /******************************************************************************
99 * Positioning constants
101 #define SELECTED_TAB_OFFSET 2
102 #define ROUND_CORNER_SIZE 2
103 #define DISPLAY_AREA_PADDINGX 2
104 #define DISPLAY_AREA_PADDINGY 2
105 #define CONTROL_BORDER_SIZEX 2
106 #define CONTROL_BORDER_SIZEY 2
107 #define BUTTON_SPACINGX 3
108 #define BUTTON_SPACINGY 3
109 #define FLAT_BTN_SPACINGX 8
110 #define DEFAULT_TAB_WIDTH 96
112 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
114 /******************************************************************************
115 * Hot-tracking timer constants
117 #define TAB_HOTTRACK_TIMER 1
118 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
120 /******************************************************************************
123 static void TAB_Refresh (HWND hwnd, HDC hdc);
124 static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
125 static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
126 static void TAB_DrawItem(HWND hwnd, HDC hdc, INT iItem);
127 static void TAB_DrawItemInterior(HWND hwnd, HDC hdc, INT iItem, RECT* drawRect);
130 TAB_SendSimpleNotify (HWND hwnd, UINT code)
132 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
135 nmhdr.hwndFrom = hwnd;
136 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
139 return (BOOL) SendMessageA (infoPtr->hwndNotify, WM_NOTIFY,
140 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
144 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
145 WPARAM wParam, LPARAM lParam)
153 msg.time = GetMessageTime ();
154 msg.pt.x = LOWORD(GetMessagePos ());
155 msg.pt.y = HIWORD(GetMessagePos ());
157 SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
161 TAB_DumpItemExternalA(TCITEMA *pti, UINT iItem)
164 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
165 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
166 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextA=%s\n",
167 iItem, pti->iImage, pti->lParam, debugstr_a(pti->pszText));
173 TAB_DumpItemExternalW(TCITEMW *pti, UINT iItem)
176 TRACE("external tab %d, mask=0x%08x, dwState=0x%08lx, dwStateMask=0x%08lx, cchTextMax=0x%08x\n",
177 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
178 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
179 iItem, pti->iImage, pti->lParam, debugstr_w(pti->pszText));
184 TAB_DumpItemInternal(TAB_INFO *infoPtr, UINT iItem)
189 ti = &infoPtr->items[iItem];
190 TRACE("tab %d, mask=0x%08x, dwState=0x%08lx, pszText=%s, iImage=%d\n",
191 iItem, ti->mask, ti->dwState, debugstr_w(ti->pszText),
193 TRACE("tab %d, lParam=0x%08lx, rect.left=%ld, rect.top(row)=%ld\n",
194 iItem, ti->lParam, ti->rect.left, ti->rect.top);
199 TAB_GetCurSel (HWND hwnd)
201 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
203 return infoPtr->iSelected;
207 TAB_GetCurFocus (HWND hwnd)
209 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
211 return infoPtr->uFocus;
215 TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
217 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
219 if (infoPtr == NULL) return 0;
220 return (LRESULT)infoPtr->hwndToolTip;
224 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
226 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
227 INT iItem = (INT)wParam;
231 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
232 prevItem=infoPtr->iSelected;
233 infoPtr->iSelected=iItem;
234 TAB_EnsureSelectionVisible(hwnd, infoPtr);
235 TAB_InvalidateTabArea(hwnd, infoPtr);
241 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
243 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
244 INT iItem=(INT) wParam;
246 if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
248 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
249 FIXME("Should set input focus\n");
251 int oldFocus = infoPtr->uFocus;
252 if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) {
253 infoPtr->uFocus = iItem;
254 if (oldFocus != -1) {
255 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
256 infoPtr->iSelected = iItem;
257 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
260 infoPtr->iSelected = iItem;
261 TAB_EnsureSelectionVisible(hwnd, infoPtr);
262 TAB_InvalidateTabArea(hwnd, infoPtr);
270 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
272 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
274 if (infoPtr == NULL) return 0;
275 infoPtr->hwndToolTip = (HWND)wParam;
280 TAB_SetPadding (HWND hwnd, WPARAM wParam, LPARAM lParam)
282 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
284 if (infoPtr == NULL) return 0;
285 infoPtr->uHItemPadding_s=LOWORD(lParam);
286 infoPtr->uVItemPadding_s=HIWORD(lParam);
290 /******************************************************************************
291 * TAB_InternalGetItemRect
293 * This method will calculate the rectangle representing a given tab item in
294 * client coordinates. This method takes scrolling into account.
296 * This method returns TRUE if the item is visible in the window and FALSE
297 * if it is completely outside the client area.
299 static BOOL TAB_InternalGetItemRect(
306 RECT tmpItemRect,clientRect;
307 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
309 /* Perform a sanity check and a trivial visibility check. */
310 if ( (infoPtr->uNumItem <= 0) ||
311 (itemIndex >= infoPtr->uNumItem) ||
312 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
316 * Avoid special cases in this procedure by assigning the "out"
317 * parameters if the caller didn't supply them
319 if (itemRect == NULL)
320 itemRect = &tmpItemRect;
322 /* Retrieve the unmodified item rect. */
323 *itemRect = infoPtr->items[itemIndex].rect;
325 /* calculate the times bottom and top based on the row */
326 GetClientRect(hwnd, &clientRect);
328 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
330 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * infoPtr->tabHeight -
331 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
332 itemRect->left = itemRect->right - infoPtr->tabHeight;
334 else if (lStyle & TCS_VERTICAL)
336 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * infoPtr->tabHeight +
337 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
338 itemRect->right = itemRect->left + infoPtr->tabHeight;
340 else if (lStyle & TCS_BOTTOM)
342 itemRect->bottom = clientRect.bottom - itemRect->top * infoPtr->tabHeight -
343 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
344 itemRect->top = itemRect->bottom - infoPtr->tabHeight;
346 else /* not TCS_BOTTOM and not TCS_VERTICAL */
348 itemRect->top = clientRect.top + itemRect->top * infoPtr->tabHeight +
349 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
350 itemRect->bottom = itemRect->top + infoPtr->tabHeight;
354 * "scroll" it to make sure the item at the very left of the
355 * tab control is the leftmost visible tab.
357 if(lStyle & TCS_VERTICAL)
361 -infoPtr->items[infoPtr->leftmostVisible].rect.top);
364 * Move the rectangle so the first item is slightly offset from
365 * the bottom of the tab control.
369 SELECTED_TAB_OFFSET);
374 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
378 * Move the rectangle so the first item is slightly offset from
379 * the left of the tab control.
385 TRACE("item %d tab h=%d, rect=(%ld,%ld)-(%ld,%ld)\n",
386 itemIndex, infoPtr->tabHeight,
387 itemRect->left, itemRect->top, itemRect->right, itemRect->bottom);
389 /* Now, calculate the position of the item as if it were selected. */
390 if (selectedRect!=NULL)
392 CopyRect(selectedRect, itemRect);
394 /* The rectangle of a selected item is a bit wider. */
395 if(lStyle & TCS_VERTICAL)
396 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
398 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
400 /* If it also a bit higher. */
401 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
403 selectedRect->left -= 2; /* the border is thicker on the right */
404 selectedRect->right += SELECTED_TAB_OFFSET;
406 else if (lStyle & TCS_VERTICAL)
408 selectedRect->left -= SELECTED_TAB_OFFSET;
409 selectedRect->right += 1;
411 else if (lStyle & TCS_BOTTOM)
413 selectedRect->bottom += SELECTED_TAB_OFFSET;
415 else /* not TCS_BOTTOM and not TCS_VERTICAL */
417 selectedRect->top -= SELECTED_TAB_OFFSET;
418 selectedRect->bottom -= 1;
425 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
427 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
428 (LPRECT)lParam, (LPRECT)NULL);
431 /******************************************************************************
434 * This method is called to handle keyboard input
436 static LRESULT TAB_KeyUp(
440 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
446 newItem = infoPtr->uFocus - 1;
449 newItem = infoPtr->uFocus + 1;
454 * If we changed to a valid item, change the selection
456 if ((newItem >= 0) &&
457 (newItem < infoPtr->uNumItem) &&
458 (infoPtr->uFocus != newItem))
460 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
462 infoPtr->iSelected = newItem;
463 infoPtr->uFocus = newItem;
464 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
466 TAB_EnsureSelectionVisible(hwnd, infoPtr);
467 TAB_InvalidateTabArea(hwnd, infoPtr);
474 /******************************************************************************
477 * This method is called whenever the focus goes in or out of this control
478 * it is used to update the visual state of the control.
480 static LRESULT TAB_FocusChanging(
486 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
491 * Get the rectangle for the item.
493 isVisible = TAB_InternalGetItemRect(hwnd,
500 * If the rectangle is not completely invisible, invalidate that
501 * portion of the window.
505 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
506 selectedRect.left,selectedRect.top,
507 selectedRect.right,selectedRect.bottom);
508 InvalidateRect(hwnd, &selectedRect, TRUE);
512 * Don't otherwise disturb normal behavior.
514 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
517 static INT TAB_InternalHitTest (
527 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
529 TAB_InternalGetItemRect(hwnd, infoPtr, iCount, &rect, NULL);
531 if (PtInRect(&rect, pt))
533 *flags = TCHT_ONITEM;
538 *flags = TCHT_NOWHERE;
543 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
545 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
546 LPTCHITTESTINFO lptest = (LPTCHITTESTINFO) lParam;
548 return TAB_InternalHitTest (hwnd, infoPtr, lptest->pt, &lptest->flags);
551 /******************************************************************************
554 * Napster v2b5 has a tab control for its main navigation which has a client
555 * area that covers the whole area of the dialog pages.
556 * That's why it receives all msgs for that area and the underlying dialog ctrls
558 * So I decided that we should handle WM_NCHITTEST here and return
559 * HTTRANSPARENT if we don't hit the tab control buttons.
560 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
561 * doesn't do it that way. Maybe depends on tab control styles ?
564 TAB_NCHitTest (HWND hwnd, LPARAM lParam)
566 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
570 pt.x = LOWORD(lParam);
571 pt.y = HIWORD(lParam);
572 ScreenToClient(hwnd, &pt);
574 if (TAB_InternalHitTest(hwnd, infoPtr, pt, &dummyflag) == -1)
575 return HTTRANSPARENT;
581 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
583 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
587 if (infoPtr->hwndToolTip)
588 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
589 WM_LBUTTONDOWN, wParam, lParam);
591 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
595 if (infoPtr->hwndToolTip)
596 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
597 WM_LBUTTONDOWN, wParam, lParam);
599 pt.x = (INT)LOWORD(lParam);
600 pt.y = (INT)HIWORD(lParam);
602 newItem = TAB_InternalHitTest (hwnd, infoPtr, pt, &dummy);
604 TRACE("On Tab, item %d\n", newItem);
606 if ((newItem != -1) && (infoPtr->iSelected != newItem))
608 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING) != TRUE)
610 infoPtr->iSelected = newItem;
611 infoPtr->uFocus = newItem;
612 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
614 TAB_EnsureSelectionVisible(hwnd, infoPtr);
616 TAB_InvalidateTabArea(hwnd, infoPtr);
623 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
625 TAB_SendSimpleNotify(hwnd, NM_CLICK);
631 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
633 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
637 /******************************************************************************
638 * TAB_DrawLoneItemInterior
640 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
641 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
642 * up the device context and font. This routine does the same setup but
643 * only calls TAB_DrawItemInterior for the single specified item.
646 TAB_DrawLoneItemInterior(HWND hwnd, TAB_INFO* infoPtr, int iItem)
648 HDC hdc = GetDC(hwnd);
651 /* Clip UpDown control to not draw over it */
652 if (infoPtr->needsScrolling)
654 GetWindowRect(hwnd, &rC);
655 GetWindowRect(infoPtr->hwndUpDown, &r);
656 ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top);
658 TAB_DrawItemInterior(hwnd, hdc, iItem, NULL);
659 ReleaseDC(hwnd, hdc);
662 /******************************************************************************
663 * TAB_HotTrackTimerProc
665 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
666 * timer is setup so we can check if the mouse is moved out of our window.
667 * (We don't get an event when the mouse leaves, the mouse-move events just
668 * stop being delivered to our window and just start being delivered to
669 * another window.) This function is called when the timer triggers so
670 * we can check if the mouse has left our window. If so, we un-highlight
671 * the hot-tracked tab.
674 TAB_HotTrackTimerProc
676 HWND hwnd, /* handle of window for timer messages */
677 UINT uMsg, /* WM_TIMER message */
678 UINT idEvent, /* timer identifier */
679 DWORD dwTime /* current system time */
682 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
684 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
689 ** If we can't get the cursor position, or if the cursor is outside our
690 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
691 ** "outside" even if it is within our bounding rect if another window
692 ** overlaps. Note also that the case where the cursor stayed within our
693 ** window but has moved off the hot-tracked tab will be handled by the
694 ** WM_MOUSEMOVE event.
696 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
698 /* Redraw iHotTracked to look normal */
699 INT iRedraw = infoPtr->iHotTracked;
700 infoPtr->iHotTracked = -1;
701 TAB_DrawLoneItemInterior(hwnd, infoPtr, iRedraw);
703 /* Kill this timer */
704 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
709 /******************************************************************************
712 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
713 * should be highlighted. This function determines which tab in a tab control,
714 * if any, is under the mouse and records that information. The caller may
715 * supply output parameters to receive the item number of the tab item which
716 * was highlighted but isn't any longer and of the tab item which is now
717 * highlighted but wasn't previously. The caller can use this information to
718 * selectively redraw those tab items.
720 * If the caller has a mouse position, it can supply it through the pos
721 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
722 * supplies NULL and this function determines the current mouse position
730 int* out_redrawLeave,
734 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
739 if (out_redrawLeave != NULL)
740 *out_redrawLeave = -1;
741 if (out_redrawEnter != NULL)
742 *out_redrawEnter = -1;
744 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_HOTTRACK)
752 ScreenToClient(hwnd, &pt);
760 item = TAB_InternalHitTest(hwnd, infoPtr, pt, &flags);
763 if (item != infoPtr->iHotTracked)
765 if (infoPtr->iHotTracked >= 0)
767 /* Mark currently hot-tracked to be redrawn to look normal */
768 if (out_redrawLeave != NULL)
769 *out_redrawLeave = infoPtr->iHotTracked;
773 /* Kill timer which forces recheck of mouse pos */
774 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
779 /* Start timer so we recheck mouse pos */
780 UINT timerID = SetTimer
784 TAB_HOTTRACK_TIMER_INTERVAL,
785 TAB_HotTrackTimerProc
789 return; /* Hot tracking not available */
792 infoPtr->iHotTracked = item;
796 /* Mark new hot-tracked to be redrawn to look highlighted */
797 if (out_redrawEnter != NULL)
798 *out_redrawEnter = item;
803 /******************************************************************************
806 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
809 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
814 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
816 if (infoPtr->hwndToolTip)
817 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
818 WM_LBUTTONDOWN, wParam, lParam);
820 /* Determine which tab to highlight. Redraw tabs which change highlight
822 TAB_RecalcHotTrack(hwnd, &lParam, &redrawLeave, &redrawEnter);
824 if (redrawLeave != -1)
825 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawLeave);
826 if (redrawEnter != -1)
827 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawEnter);
832 /******************************************************************************
835 * Calculates the tab control's display area given the window rectangle or
836 * the window rectangle given the requested display rectangle.
838 static LRESULT TAB_AdjustRect(
843 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
844 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
845 LONG *iRightBottom, *iLeftTop;
847 TRACE ("hwnd=%p fLarger=%d (%ld,%ld)-(%ld,%ld)\n", hwnd, fLarger, prc->left, prc->top, prc->right, prc->bottom);
849 if(lStyle & TCS_VERTICAL)
851 iRightBottom = &(prc->right);
852 iLeftTop = &(prc->left);
856 iRightBottom = &(prc->bottom);
857 iLeftTop = &(prc->top);
860 if (fLarger) /* Go from display rectangle */
862 /* Add the height of the tabs. */
863 if (lStyle & TCS_BOTTOM)
864 *iRightBottom += infoPtr->tabHeight * infoPtr->uNumRows;
866 *iLeftTop -= infoPtr->tabHeight * infoPtr->uNumRows +
867 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
869 /* Inflate the rectangle for the padding */
870 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
872 /* Inflate for the border */
873 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEY);
875 else /* Go from window rectangle. */
877 /* Deflate the rectangle for the border */
878 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEY);
880 /* Deflate the rectangle for the padding */
881 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
883 /* Remove the height of the tabs. */
884 if (lStyle & TCS_BOTTOM)
885 *iRightBottom -= infoPtr->tabHeight * infoPtr->uNumRows;
887 *iLeftTop += (infoPtr->tabHeight) * infoPtr->uNumRows +
888 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
894 /******************************************************************************
897 * This method will handle the notification from the scroll control and
898 * perform the scrolling operation on the tab control.
900 static LRESULT TAB_OnHScroll(
906 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
908 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
910 if(nPos < infoPtr->leftmostVisible)
911 infoPtr->leftmostVisible--;
913 infoPtr->leftmostVisible++;
915 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
916 TAB_InvalidateTabArea(hwnd, infoPtr);
917 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
918 MAKELONG(infoPtr->leftmostVisible, 0));
924 /******************************************************************************
927 * This method will check the current scrolling state and make sure the
928 * scrolling control is displayed (or not).
930 static void TAB_SetupScrolling(
933 const RECT* clientRect)
936 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
938 if (infoPtr->needsScrolling)
944 * Calculate the position of the scroll control.
946 if(lStyle & TCS_VERTICAL)
948 controlPos.right = clientRect->right;
949 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
951 if (lStyle & TCS_BOTTOM)
953 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
954 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
958 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
959 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
964 controlPos.right = clientRect->right;
965 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
967 if (lStyle & TCS_BOTTOM)
969 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
970 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
974 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
975 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
980 * If we don't have a scroll control yet, we want to create one.
981 * If we have one, we want to make sure it's positioned properly.
983 if (infoPtr->hwndUpDown==0)
985 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
987 WS_VISIBLE | WS_CHILD | UDS_HORZ,
988 controlPos.left, controlPos.top,
989 controlPos.right - controlPos.left,
990 controlPos.bottom - controlPos.top,
998 SetWindowPos(infoPtr->hwndUpDown,
1000 controlPos.left, controlPos.top,
1001 controlPos.right - controlPos.left,
1002 controlPos.bottom - controlPos.top,
1003 SWP_SHOWWINDOW | SWP_NOZORDER);
1006 /* Now calculate upper limit of the updown control range.
1007 * We do this by calculating how many tabs will be offscreen when the
1008 * last tab is visible.
1010 if(infoPtr->uNumItem)
1012 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
1013 maxRange = infoPtr->uNumItem;
1014 tabwidth = infoPtr->items[maxRange - 1].rect.right;
1016 for(; maxRange > 0; maxRange--)
1018 if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
1022 if(maxRange == infoPtr->uNumItem)
1028 /* If we once had a scroll control... hide it */
1029 if (infoPtr->hwndUpDown!=0)
1030 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1032 if (infoPtr->hwndUpDown)
1033 SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1036 /******************************************************************************
1039 * This method will calculate the position rectangles of all the items in the
1040 * control. The rectangle calculated starts at 0 for the first item in the
1041 * list and ignores scrolling and selection.
1042 * It also uses the current font to determine the height of the tab row and
1043 * it checks if all the tabs fit in the client area of the window. If they
1044 * don't, a scrolling control is added.
1046 static void TAB_SetItemBounds (HWND hwnd)
1048 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1049 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1050 TEXTMETRICA fontMetrics;
1053 INT curItemRowCount;
1054 HFONT hFont, hOldFont;
1064 * We need to get text information so we need a DC and we need to select
1069 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1070 hOldFont = SelectObject (hdc, hFont);
1073 * We will base the rectangle calculations on the client rectangle
1076 GetClientRect(hwnd, &clientRect);
1078 /* if TCS_VERTICAL then swap the height and width so this code places the
1079 tabs along the top of the rectangle and we can just rotate them after
1080 rather than duplicate all of the below code */
1081 if(lStyle & TCS_VERTICAL)
1083 iTemp = clientRect.bottom;
1084 clientRect.bottom = clientRect.right;
1085 clientRect.right = iTemp;
1088 /* Now use hPadding and vPadding */
1089 infoPtr->uHItemPadding = infoPtr->uHItemPadding_s;
1090 infoPtr->uVItemPadding = infoPtr->uVItemPadding_s;
1092 /* The leftmost item will be "0" aligned */
1094 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1096 if (!(infoPtr->fHeightSet))
1099 int icon_height = 0;
1101 /* Use the current font to determine the height of a tab. */
1102 GetTextMetricsA(hdc, &fontMetrics);
1104 /* Get the icon height */
1106 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1108 /* Take the highest between font or icon */
1109 if (fontMetrics.tmHeight > icon_height)
1110 item_height = fontMetrics.tmHeight + 2;
1112 item_height = icon_height;
1115 * Make sure there is enough space for the letters + icon + growing the
1116 * selected item + extra space for the selected item.
1118 infoPtr->tabHeight = item_height +
1119 ((lStyle & TCS_BUTTONS) ? 2 : 1) *
1120 infoPtr->uVItemPadding;
1122 TRACE("tabH=%d, tmH=%ld, iconh=%d\n",
1123 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1126 TRACE("client right=%ld\n", clientRect.right);
1128 /* Get the icon width */
1131 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1133 if (lStyle & TCS_FIXEDWIDTH)
1136 /* Add padding if icon is present */
1137 icon_width += infoPtr->uHItemPadding;
1140 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1142 /* Set the leftmost position of the tab. */
1143 infoPtr->items[curItem].rect.left = curItemLeftPos;
1145 if (lStyle & TCS_FIXEDWIDTH)
1147 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1148 max(infoPtr->tabWidth, icon_width);
1154 /* Calculate how wide the tab is depending on the text it contains */
1155 GetTextExtentPoint32W(hdc, infoPtr->items[curItem].pszText,
1156 lstrlenW(infoPtr->items[curItem].pszText), &size);
1158 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1159 size.cx + icon_width +
1160 num * infoPtr->uHItemPadding;
1161 TRACE("for <%s>, l,r=%ld,%ld, num=%d\n",
1162 debugstr_w(infoPtr->items[curItem].pszText),
1163 infoPtr->items[curItem].rect.left,
1164 infoPtr->items[curItem].rect.right,
1169 * Check if this is a multiline tab control and if so
1170 * check to see if we should wrap the tabs
1172 * Wrap all these tabs. We will arrange them evenly later.
1176 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1177 (infoPtr->items[curItem].rect.right >
1178 (clientRect.right - CONTROL_BORDER_SIZEX - DISPLAY_AREA_PADDINGX)))
1180 infoPtr->items[curItem].rect.right -=
1181 infoPtr->items[curItem].rect.left;
1183 infoPtr->items[curItem].rect.left = 0;
1185 TRACE("wrapping <%s>, l,r=%ld,%ld\n",
1186 debugstr_w(infoPtr->items[curItem].pszText),
1187 infoPtr->items[curItem].rect.left,
1188 infoPtr->items[curItem].rect.right);
1191 infoPtr->items[curItem].rect.bottom = 0;
1192 infoPtr->items[curItem].rect.top = curItemRowCount - 1;
1194 TRACE("TextSize: %li\n", size.cx);
1195 TRACE("Rect: T %li, L %li, B %li, R %li\n",
1196 infoPtr->items[curItem].rect.top,
1197 infoPtr->items[curItem].rect.left,
1198 infoPtr->items[curItem].rect.bottom,
1199 infoPtr->items[curItem].rect.right);
1202 * The leftmost position of the next item is the rightmost position
1205 if (lStyle & TCS_BUTTONS)
1207 curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX;
1208 if (lStyle & TCS_FLATBUTTONS)
1209 curItemLeftPos += FLAT_BTN_SPACINGX;
1212 curItemLeftPos = infoPtr->items[curItem].rect.right;
1215 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1218 * Check if we need a scrolling control.
1220 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1223 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1224 if(!infoPtr->needsScrolling)
1225 infoPtr->leftmostVisible = 0;
1230 * No scrolling in Multiline or Vertical styles.
1232 infoPtr->needsScrolling = FALSE;
1233 infoPtr->leftmostVisible = 0;
1235 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
1237 /* Set the number of rows */
1238 infoPtr->uNumRows = curItemRowCount;
1240 /* Arrange all tabs evenly if style says so */
1241 if (!(lStyle & TCS_RAGGEDRIGHT) && ((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->uNumItem > 0))
1243 INT tabPerRow,remTab;
1248 * Ok windows tries to even out the rows. place the same
1249 * number of tabs in each row. So lets give that a shot
1252 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1253 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1255 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1256 iItm<infoPtr->uNumItem;
1259 /* normalize the current rect */
1261 /* shift the item to the left side of the clientRect */
1262 infoPtr->items[iItm].rect.right -=
1263 infoPtr->items[iItm].rect.left;
1264 infoPtr->items[iItm].rect.left = 0;
1266 TRACE("r=%ld, cl=%d, cl.r=%ld, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1267 infoPtr->items[iItm].rect.right,
1268 curItemLeftPos, clientRect.right,
1269 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1271 /* if we have reached the maximum number of tabs on this row */
1272 /* move to the next row, reset our current item left position and */
1273 /* the count of items on this row */
1275 if (lStyle & TCS_VERTICAL) {
1276 /* Vert: Add the remaining tabs in the *last* remainder rows */
1277 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1283 /* Horz: Add the remaining tabs in the *first* remainder rows */
1284 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1291 /* shift the item to the right to place it as the next item in this row */
1292 infoPtr->items[iItm].rect.left += curItemLeftPos;
1293 infoPtr->items[iItm].rect.right += curItemLeftPos;
1294 infoPtr->items[iItm].rect.top = iRow;
1295 if (lStyle & TCS_BUTTONS)
1297 curItemLeftPos = infoPtr->items[iItm].rect.right + 1;
1298 if (lStyle & TCS_FLATBUTTONS)
1299 curItemLeftPos += FLAT_BTN_SPACINGX;
1302 curItemLeftPos = infoPtr->items[iItm].rect.right;
1304 TRACE("arranging <%s>, l,r=%ld,%ld, row=%ld\n",
1305 debugstr_w(infoPtr->items[iItm].pszText),
1306 infoPtr->items[iItm].rect.left,
1307 infoPtr->items[iItm].rect.right,
1308 infoPtr->items[iItm].rect.top);
1315 INT widthDiff, iIndexStart=0, iIndexEnd=0;
1319 while(iIndexStart < infoPtr->uNumItem)
1322 * find the indexs of the row
1324 /* find the first item on the next row */
1325 for (iIndexEnd=iIndexStart;
1326 (iIndexEnd < infoPtr->uNumItem) &&
1327 (infoPtr->items[iIndexEnd].rect.top ==
1328 infoPtr->items[iIndexStart].rect.top) ;
1330 /* intentionally blank */;
1333 * we need to justify these tabs so they fill the whole given
1337 /* find the amount of space remaining on this row */
1338 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1339 infoPtr->items[iIndexEnd - 1].rect.right;
1341 /* iCount is the number of tab items on this row */
1342 iCount = iIndexEnd - iIndexStart;
1346 remainder = widthDiff % iCount;
1347 widthDiff = widthDiff / iCount;
1348 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1349 for (iIndex=iIndexStart, iCount=0; iIndex < iIndexEnd; iIndex++, iCount++)
1351 infoPtr->items[iIndex].rect.left += iCount * widthDiff;
1352 infoPtr->items[iIndex].rect.right += (iCount + 1) * widthDiff;
1354 TRACE("adjusting 1 <%s>, l,r=%ld,%ld\n",
1355 debugstr_w(infoPtr->items[iIndex].pszText),
1356 infoPtr->items[iIndex].rect.left,
1357 infoPtr->items[iIndex].rect.right);
1360 infoPtr->items[iIndex - 1].rect.right += remainder;
1362 else /* we have only one item on this row, make it take up the entire row */
1364 infoPtr->items[iIndexStart].rect.left = clientRect.left;
1365 infoPtr->items[iIndexStart].rect.right = clientRect.right - 4;
1367 TRACE("adjusting 2 <%s>, l,r=%ld,%ld\n",
1368 debugstr_w(infoPtr->items[iIndexStart].pszText),
1369 infoPtr->items[iIndexStart].rect.left,
1370 infoPtr->items[iIndexStart].rect.right);
1375 iIndexStart = iIndexEnd;
1380 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1381 if(lStyle & TCS_VERTICAL)
1384 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1386 rcItem = &(infoPtr->items[iIndex].rect);
1388 rcOriginal = *rcItem;
1390 /* this is rotating the items by 90 degrees clockwise around the center of the control */
1391 rcItem->top = (rcOriginal.left - clientRect.left);
1392 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1393 rcItem->left = rcOriginal.top;
1394 rcItem->right = rcOriginal.bottom;
1398 TAB_EnsureSelectionVisible(hwnd,infoPtr);
1399 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1402 SelectObject (hdc, hOldFont);
1403 ReleaseDC (hwnd, hdc);
1408 TAB_EraseTabInterior
1416 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1417 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1418 HBRUSH hbr = CreateSolidBrush (comctl32_color.clrBtnFace);
1419 BOOL deleteBrush = TRUE;
1420 RECT rTemp = *drawRect;
1422 InflateRect(&rTemp, -2, -2);
1423 if (lStyle & TCS_BUTTONS)
1425 if (iItem == infoPtr->iSelected)
1427 /* Background color */
1428 if (!(lStyle & TCS_OWNERDRAWFIXED))
1431 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1433 SetTextColor(hdc, comctl32_color.clr3dFace);
1434 SetBkColor(hdc, comctl32_color.clr3dHilight);
1436 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1437 * we better use 0x55aa bitmap brush to make scrollbar's background
1438 * look different from the window background.
1440 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1441 hbr = COMCTL32_hPattern55AABrush;
1443 deleteBrush = FALSE;
1445 FillRect(hdc, &rTemp, hbr);
1447 else /* ! selected */
1449 if (lStyle & TCS_FLATBUTTONS)
1451 FillRect(hdc, drawRect, hbr);
1452 if (iItem == infoPtr->iHotTracked)
1453 DrawEdge(hdc, drawRect, EDGE_RAISED, BF_SOFT|BF_RECT);
1456 FillRect(hdc, &rTemp, hbr);
1460 else /* !TCS_BUTTONS */
1462 FillRect(hdc, &rTemp, hbr);
1466 if (deleteBrush) DeleteObject(hbr);
1469 /******************************************************************************
1470 * TAB_DrawItemInterior
1472 * This method is used to draw the interior (text and icon) of a single tab
1473 * into the tab control.
1476 TAB_DrawItemInterior
1484 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1485 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1494 /* if (drawRect == NULL) */
1501 * Get the rectangle for the item.
1503 isVisible = TAB_InternalGetItemRect(hwnd, infoPtr, iItem, &itemRect, &selectedRect);
1508 * Make sure drawRect points to something valid; simplifies code.
1510 drawRect = &localRect;
1513 * This logic copied from the part of TAB_DrawItem which draws
1514 * the tab background. It's important to keep it in sync. I
1515 * would have liked to avoid code duplication, but couldn't figure
1516 * out how without making spaghetti of TAB_DrawItem.
1518 if (iItem == infoPtr->iSelected)
1519 *drawRect = selectedRect;
1521 *drawRect = itemRect;
1523 if (lStyle & TCS_BUTTONS)
1525 if (iItem == infoPtr->iSelected)
1527 drawRect->left += 4;
1529 drawRect->right -= 4;
1530 drawRect->bottom -= 1;
1534 drawRect->left += 2;
1536 drawRect->right -= 2;
1537 drawRect->bottom -= 2;
1542 if ((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1544 if (iItem != infoPtr->iSelected)
1546 drawRect->left += 2;
1548 drawRect->bottom -= 2;
1551 else if (lStyle & TCS_VERTICAL)
1553 if (iItem == infoPtr->iSelected)
1555 drawRect->right += 1;
1560 drawRect->right -= 2;
1561 drawRect->bottom -= 2;
1564 else if (lStyle & TCS_BOTTOM)
1566 if (iItem == infoPtr->iSelected)
1572 InflateRect(drawRect, -2, -2);
1573 drawRect->bottom += 2;
1578 if (iItem == infoPtr->iSelected)
1580 drawRect->bottom += 3;
1584 drawRect->bottom -= (infoPtr->items[iItem].rect.top != infoPtr->uNumRows-1)? 2:2;
1585 InflateRect(drawRect, -2, 0);
1590 TRACE("drawRect=(%ld,%ld)-(%ld,%ld)\n",
1591 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom);
1593 /* Clear interior */
1594 TAB_EraseTabInterior (hwnd, hdc, iItem, drawRect);
1596 /* Draw the focus rectangle */
1597 if (!(lStyle & TCS_FOCUSNEVER) &&
1598 (GetFocus() == hwnd) &&
1599 (iItem == infoPtr->uFocus) )
1601 RECT rFocus = *drawRect;
1602 InflateRect(&rFocus, -3, -3);
1603 if (lStyle & TCS_BOTTOM && !(lStyle & TCS_VERTICAL))
1605 if (lStyle & TCS_BUTTONS)
1611 DrawFocusRect(hdc, &rFocus);
1617 htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1618 holdPen = SelectObject(hdc, htextPen);
1619 hOldFont = SelectObject(hdc, infoPtr->hFont);
1622 * Setup for text output
1624 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1625 SetTextColor(hdc, (((iItem == infoPtr->iHotTracked) && !(lStyle & TCS_FLATBUTTONS)) |
1626 (infoPtr->items[iItem].dwState & TCIS_HIGHLIGHTED)) ?
1627 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1630 * if owner draw, tell the owner to draw
1632 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd))
1638 * get the control id
1640 id = GetWindowLongA( hwnd, GWL_ID );
1643 * put together the DRAWITEMSTRUCT
1645 dis.CtlType = ODT_TAB;
1648 dis.itemAction = ODA_DRAWENTIRE;
1650 if ( iItem == infoPtr->iSelected )
1651 dis.itemState |= ODS_SELECTED;
1652 if (infoPtr->uFocus == iItem)
1653 dis.itemState |= ODS_FOCUS;
1654 dis.hwndItem = hwnd; /* */
1656 CopyRect(&dis.rcItem,drawRect);
1657 dis.itemData = infoPtr->items[iItem].lParam;
1660 * send the draw message
1662 SendMessageA( infoPtr->hwndNotify, WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1669 /* used to center the icon and text in the tab */
1671 INT center_offset_h, center_offset_v;
1673 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1674 rcImage = *drawRect;
1678 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1680 /* get the rectangle that the text fits in */
1681 DrawTextW(hdc, infoPtr->items[iItem].pszText, -1,
1682 &rcText, DT_CALCRECT);
1684 * If not owner draw, then do the drawing ourselves.
1688 if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE))
1693 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1695 if(lStyle & TCS_VERTICAL)
1697 center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1698 center_offset_v = ((drawRect->right - drawRect->left) - (cx + infoPtr->uVItemPadding)) / 2;
1702 center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1703 center_offset_v = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uVItemPadding)) / 2;
1706 if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1707 center_offset_h = infoPtr->uHItemPadding;
1709 if (center_offset_h < 2)
1710 center_offset_h = 2;
1712 if (center_offset_v < 0)
1713 center_offset_v = 0;
1715 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1716 debugstr_w(infoPtr->items[iItem].pszText), center_offset_h, center_offset_v,
1717 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1718 (rcText.right-rcText.left));
1720 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1722 rcImage.top = drawRect->top + center_offset_h;
1723 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1724 /* right side of the tab, but the image still uses the left as its x position */
1725 /* this keeps the image always drawn off of the same side of the tab */
1726 rcImage.left = drawRect->right - cx - center_offset_v;
1727 drawRect->top += cy + infoPtr->uHItemPadding;
1729 else if(lStyle & TCS_VERTICAL)
1731 rcImage.top = drawRect->bottom - cy - center_offset_h;
1732 rcImage.left = drawRect->left + center_offset_v;
1733 drawRect->bottom -= cy + infoPtr->uHItemPadding;
1735 else /* normal style, whether TCS_BOTTOM or not */
1737 rcImage.left = drawRect->left + center_offset_h;
1738 rcImage.top = drawRect->top + center_offset_v;
1739 drawRect->left += cx + infoPtr->uHItemPadding;
1742 TRACE("drawing image=%d, left=%ld, top=%ld\n",
1743 infoPtr->items[iItem].iImage, rcImage.left, rcImage.top-1);
1747 infoPtr->items[iItem].iImage,
1755 /* Now position text */
1756 if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT)
1757 center_offset_h = infoPtr->uHItemPadding;
1759 if(lStyle & TCS_VERTICAL)
1760 center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1762 center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1764 if(lStyle & TCS_VERTICAL)
1766 if(lStyle & TCS_BOTTOM)
1767 drawRect->top+=center_offset_h;
1769 drawRect->bottom-=center_offset_h;
1771 center_offset_v = ((drawRect->right - drawRect->left) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1775 drawRect->left += center_offset_h;
1776 center_offset_v = ((drawRect->bottom - drawRect->top) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1779 if (center_offset_v < 0)
1780 center_offset_v = 0;
1782 if(lStyle & TCS_VERTICAL)
1783 drawRect->left += center_offset_v;
1785 drawRect->top += center_offset_v;
1788 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1792 INT nEscapement = 900;
1793 INT nOrientation = 900;
1795 if(lStyle & TCS_BOTTOM)
1798 nOrientation = -900;
1801 /* to get a font with the escapement and orientation we are looking for, we need to */
1802 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1803 if (!GetObjectA((infoPtr->hFont) ?
1804 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1805 sizeof(LOGFONTA),&logfont))
1809 lstrcpyA(logfont.lfFaceName, "Arial");
1810 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1812 logfont.lfWeight = FW_NORMAL;
1813 logfont.lfItalic = 0;
1814 logfont.lfUnderline = 0;
1815 logfont.lfStrikeOut = 0;
1818 logfont.lfEscapement = nEscapement;
1819 logfont.lfOrientation = nOrientation;
1820 hFont = CreateFontIndirectA(&logfont);
1821 SelectObject(hdc, hFont);
1824 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1825 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1828 infoPtr->items[iItem].pszText,
1829 lstrlenW(infoPtr->items[iItem].pszText),
1832 DeleteObject(hFont);
1836 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1837 debugstr_w(infoPtr->items[iItem].pszText), center_offset_h, center_offset_v,
1838 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1839 (rcText.right-rcText.left));
1843 infoPtr->items[iItem].pszText,
1844 lstrlenW(infoPtr->items[iItem].pszText),
1846 DT_LEFT | DT_SINGLELINE
1850 *drawRect = rcTemp; /* restore drawRect */
1856 SelectObject(hdc, hOldFont);
1857 SetBkMode(hdc, oldBkMode);
1858 SelectObject(hdc, holdPen);
1859 DeleteObject( htextPen );
1862 /******************************************************************************
1865 * This method is used to draw a single tab into the tab control.
1867 static void TAB_DrawItem(
1872 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1873 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1877 RECT r, fillRect, r1;
1880 COLORREF bkgnd, corner;
1883 * Get the rectangle for the item.
1885 isVisible = TAB_InternalGetItemRect(hwnd,
1895 /* Clip UpDown control to not draw over it */
1896 if (infoPtr->needsScrolling)
1898 GetWindowRect(hwnd, &rC);
1899 GetWindowRect(infoPtr->hwndUpDown, &rUD);
1900 ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
1903 /* If you need to see what the control is doing,
1904 * then override these variables. They will change what
1905 * fill colors are used for filling the tabs, and the
1906 * corners when drawing the edge.
1908 bkgnd = comctl32_color.clrBtnFace;
1909 corner = comctl32_color.clrBtnFace;
1911 if (lStyle & TCS_BUTTONS)
1913 /* Get item rectangle */
1916 /* Separators between flat buttons */
1917 if (lStyle & TCS_FLATBUTTONS)
1920 r1.right += (FLAT_BTN_SPACINGX -2);
1921 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1924 if (iItem == infoPtr->iSelected)
1926 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1928 OffsetRect(&r, 1, 1);
1930 else /* ! selected */
1932 if (!(lStyle & TCS_FLATBUTTONS))
1933 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1936 else /* !TCS_BUTTONS */
1938 /* We draw a rectangle of different sizes depending on the selection
1940 if (iItem == infoPtr->iSelected) {
1942 GetClientRect (hwnd, &rect);
1943 clRight = rect.right;
1944 clBottom = rect.bottom;
1951 * Erase the background. (Delay it but setup rectangle.)
1952 * This is necessary when drawing the selected item since it is larger
1953 * than the others, it might overlap with stuff already drawn by the
1958 if(lStyle & TCS_VERTICAL)
1960 /* These are for adjusting the drawing of a Selected tab */
1961 /* The initial values are for the normal case of non-Selected */
1962 int ZZ = 1; /* Do not strech if selected */
1963 if (iItem == infoPtr->iSelected) {
1966 /* if leftmost draw the line longer */
1967 if(selectedRect.top == 0)
1968 fillRect.top += CONTROL_BORDER_SIZEY;
1969 /* if rightmost draw the line longer */
1970 if(selectedRect.bottom == clBottom)
1971 fillRect.bottom -= CONTROL_BORDER_SIZEY;
1974 if (lStyle & TCS_BOTTOM)
1976 /* Adjust both rectangles to match native */
1979 TRACE("<right> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1981 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1982 r.left,r.top,r.right,r.bottom);
1984 /* Clear interior */
1985 SetBkColor(hdc, bkgnd);
1986 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1988 /* Draw rectangular edge around tab */
1989 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
1991 /* Now erase the top corner and draw diagonal edge */
1992 SetBkColor(hdc, corner);
1993 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1996 r1.bottom = r1.top + ROUND_CORNER_SIZE;
1997 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1999 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2001 /* Now erase the bottom corner and draw diagonal edge */
2002 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2003 r1.bottom = r.bottom;
2005 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2006 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2008 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2010 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
2014 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
2020 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2022 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2023 r.left,r.top,r.right,r.bottom);
2025 /* Clear interior */
2026 SetBkColor(hdc, bkgnd);
2027 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2029 /* Draw rectangular edge around tab */
2030 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2032 /* Now erase the top corner and draw diagonal edge */
2033 SetBkColor(hdc, corner);
2036 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2037 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2038 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2040 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2042 /* Now erase the bottom corner and draw diagonal edge */
2044 r1.bottom = r.bottom;
2045 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2046 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2047 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2049 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2052 else /* ! TCS_VERTICAL */
2054 /* These are for adjusting the drawing of a Selected tab */
2055 /* The initial values are for the normal case of non-Selected */
2056 if (iItem == infoPtr->iSelected) {
2057 /* if leftmost draw the line longer */
2058 if(selectedRect.left == 0)
2059 fillRect.left += CONTROL_BORDER_SIZEX;
2060 /* if rightmost draw the line longer */
2061 if(selectedRect.right == clRight)
2062 fillRect.right -= CONTROL_BORDER_SIZEX;
2065 if (lStyle & TCS_BOTTOM)
2067 /* Adjust both rectangles for topmost row */
2068 if (infoPtr->items[iItem].rect.top == infoPtr->uNumRows-1)
2074 TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2076 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2077 r.left,r.top,r.right,r.bottom);
2079 /* Clear interior */
2080 SetBkColor(hdc, bkgnd);
2081 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2083 /* Draw rectangular edge around tab */
2084 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2086 /* Now erase the righthand corner and draw diagonal edge */
2087 SetBkColor(hdc, corner);
2088 r1.left = r.right - ROUND_CORNER_SIZE;
2089 r1.bottom = r.bottom;
2091 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2092 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2094 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2096 /* Now erase the lefthand corner and draw diagonal edge */
2098 r1.bottom = r.bottom;
2099 r1.right = r1.left + ROUND_CORNER_SIZE;
2100 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2101 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2103 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2105 if (iItem == infoPtr->iSelected)
2109 if (selectedRect.left == 0)
2114 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2121 /* Adjust both rectangles for bottommost row */
2122 if (infoPtr->items[iItem].rect.top == infoPtr->uNumRows-1)
2124 fillRect.bottom += 3;
2128 TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2130 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2131 r.left,r.top,r.right,r.bottom);
2133 /* Clear interior */
2134 SetBkColor(hdc, bkgnd);
2135 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2137 /* Draw rectangular edge around tab */
2138 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2140 /* Now erase the righthand corner and draw diagonal edge */
2141 SetBkColor(hdc, corner);
2142 r1.left = r.right - ROUND_CORNER_SIZE;
2145 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2146 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2148 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2150 /* Now erase the lefthand corner and draw diagonal edge */
2153 r1.right = r1.left + ROUND_CORNER_SIZE;
2154 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2155 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2157 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2162 TAB_DumpItemInternal(infoPtr, iItem);
2164 /* This modifies r to be the text rectangle. */
2165 TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
2169 /******************************************************************************
2172 * This method is used to draw the raised border around the tab control
2175 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
2177 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2179 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2181 GetClientRect (hwnd, &rect);
2184 * Adjust for the style
2187 if (infoPtr->uNumItem)
2189 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2190 rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2191 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2192 rect.right -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2193 else if(lStyle & TCS_VERTICAL)
2194 rect.left += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2195 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2196 rect.top += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2199 TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2200 rect.left, rect.top, rect.right, rect.bottom);
2202 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2205 /******************************************************************************
2208 * This method repaints the tab control..
2210 static void TAB_Refresh (HWND hwnd, HDC hdc)
2212 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2216 if (!infoPtr->DoRedraw)
2219 hOldFont = SelectObject (hdc, infoPtr->hFont);
2221 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
2223 for (i = 0; i < infoPtr->uNumItem; i++)
2224 TAB_DrawItem (hwnd, hdc, i);
2228 /* Draw all the non selected item first */
2229 for (i = 0; i < infoPtr->uNumItem; i++)
2231 if (i != infoPtr->iSelected)
2232 TAB_DrawItem (hwnd, hdc, i);
2235 /* Now, draw the border, draw it before the selected item
2236 * since the selected item overwrites part of the border. */
2237 TAB_DrawBorder (hwnd, hdc);
2239 /* Then, draw the selected item */
2240 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
2242 /* If we haven't set the current focus yet, set it now.
2243 * Only happens when we first paint the tab controls */
2244 if (infoPtr->uFocus == -1)
2245 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
2248 SelectObject (hdc, hOldFont);
2252 TAB_GetRowCount (HWND hwnd )
2254 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2256 return infoPtr->uNumRows;
2260 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
2262 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2264 infoPtr->DoRedraw=(BOOL) wParam;
2268 static LRESULT TAB_EraseBackground(
2275 HBRUSH brush = CreateSolidBrush(comctl32_color.clrBtnFace);
2277 hdc = givenDC ? givenDC : GetDC(hwnd);
2279 GetClientRect(hwnd, &clientRect);
2281 FillRect(hdc, &clientRect, brush);
2284 ReleaseDC(hwnd, hdc);
2286 DeleteObject(brush);
2291 /******************************************************************************
2292 * TAB_EnsureSelectionVisible
2294 * This method will make sure that the current selection is completely
2295 * visible by scrolling until it is.
2297 static void TAB_EnsureSelectionVisible(
2301 INT iSelected = infoPtr->iSelected;
2302 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2303 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2305 /* set the items row to the bottommost row or topmost row depending on
2307 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2312 if(lStyle & TCS_VERTICAL)
2313 newselected = infoPtr->items[iSelected].rect.left;
2315 newselected = infoPtr->items[iSelected].rect.top;
2317 /* the target row is always (number of rows - 1)
2318 as row 0 is furthest from the clientRect */
2319 iTargetRow = infoPtr->uNumRows - 1;
2321 if (newselected != iTargetRow)
2324 if(lStyle & TCS_VERTICAL)
2326 for (i=0; i < infoPtr->uNumItem; i++)
2328 /* move everything in the row of the selected item to the iTargetRow */
2329 if (infoPtr->items[i].rect.left == newselected )
2330 infoPtr->items[i].rect.left = iTargetRow;
2333 if (infoPtr->items[i].rect.left > newselected)
2334 infoPtr->items[i].rect.left-=1;
2340 for (i=0; i < infoPtr->uNumItem; i++)
2342 if (infoPtr->items[i].rect.top == newselected )
2343 infoPtr->items[i].rect.top = iTargetRow;
2346 if (infoPtr->items[i].rect.top > newselected)
2347 infoPtr->items[i].rect.top-=1;
2351 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2356 * Do the trivial cases first.
2358 if ( (!infoPtr->needsScrolling) ||
2359 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2362 if (infoPtr->leftmostVisible >= iSelected)
2364 infoPtr->leftmostVisible = iSelected;
2371 /* Calculate the part of the client area that is visible */
2372 GetClientRect(hwnd, &r);
2375 GetClientRect(infoPtr->hwndUpDown, &r);
2378 if ((infoPtr->items[iSelected].rect.right -
2379 infoPtr->items[iSelected].rect.left) >= width )
2381 /* Special case: width of selected item is greater than visible
2384 infoPtr->leftmostVisible = iSelected;
2388 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2390 if ((infoPtr->items[iSelected].rect.right -
2391 infoPtr->items[i].rect.left) < width)
2394 infoPtr->leftmostVisible = i;
2398 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2399 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2401 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2402 MAKELONG(infoPtr->leftmostVisible, 0));
2405 /******************************************************************************
2406 * TAB_InvalidateTabArea
2408 * This method will invalidate the portion of the control that contains the
2409 * tabs. It is called when the state of the control changes and needs
2412 static void TAB_InvalidateTabArea(
2416 RECT clientRect, rInvalidate, rAdjClient;
2417 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2418 INT lastRow = infoPtr->uNumRows - 1;
2421 if (lastRow < 0) return;
2423 GetClientRect(hwnd, &clientRect);
2424 rInvalidate = clientRect;
2425 rAdjClient = clientRect;
2427 TAB_AdjustRect(hwnd, 0, &rAdjClient);
2429 TAB_InternalGetItemRect(hwnd, infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2430 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2432 rInvalidate.left = rAdjClient.right;
2433 if (infoPtr->uNumRows == 1)
2434 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2436 else if(lStyle & TCS_VERTICAL)
2438 rInvalidate.right = rAdjClient.left;
2439 if (infoPtr->uNumRows == 1)
2440 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2442 else if (lStyle & TCS_BOTTOM)
2444 rInvalidate.top = rAdjClient.bottom;
2445 if (infoPtr->uNumRows == 1)
2446 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2450 rInvalidate.bottom = rAdjClient.top;
2451 if (infoPtr->uNumRows == 1)
2452 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2455 /* Punch out the updown control */
2456 if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2458 GetClientRect(infoPtr->hwndUpDown, &r);
2459 if (rInvalidate.right > clientRect.right - r.left)
2460 rInvalidate.right = rInvalidate.right - (r.right - r.left);
2462 rInvalidate.right = clientRect.right - r.left;
2465 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2466 rInvalidate.left, rInvalidate.top,
2467 rInvalidate.right, rInvalidate.bottom);
2469 InvalidateRect(hwnd, &rInvalidate, TRUE);
2473 TAB_Paint (HWND hwnd, WPARAM wParam)
2480 hdc = BeginPaint (hwnd, &ps);
2481 TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2483 ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2486 TAB_EraseBackground (hwnd, hdc);
2492 TAB_Refresh (hwnd, hdc);
2495 EndPaint (hwnd, &ps);
2501 TAB_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2503 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2508 GetClientRect (hwnd, &rect);
2509 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2510 rect.top, rect.left, rect.bottom, rect.right);
2512 pti = (TCITEMA *)lParam;
2513 iItem = (INT)wParam;
2515 if (iItem < 0) return -1;
2516 if (iItem > infoPtr->uNumItem)
2517 iItem = infoPtr->uNumItem;
2519 TAB_DumpItemExternalA(pti, iItem);
2522 if (infoPtr->uNumItem == 0) {
2523 infoPtr->items = Alloc (sizeof (TAB_ITEM));
2524 infoPtr->uNumItem++;
2525 infoPtr->iSelected = 0;
2528 TAB_ITEM *oldItems = infoPtr->items;
2530 infoPtr->uNumItem++;
2531 infoPtr->items = Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2533 /* pre insert copy */
2535 memcpy (&infoPtr->items[0], &oldItems[0],
2536 iItem * sizeof(TAB_ITEM));
2539 /* post insert copy */
2540 if (iItem < infoPtr->uNumItem - 1) {
2541 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2542 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2546 if (iItem <= infoPtr->iSelected)
2547 infoPtr->iSelected++;
2552 infoPtr->items[iItem].mask = pti->mask;
2553 if (pti->mask & TCIF_TEXT)
2554 Str_SetPtrAtoW (&infoPtr->items[iItem].pszText, pti->pszText);
2556 if (pti->mask & TCIF_IMAGE)
2557 infoPtr->items[iItem].iImage = pti->iImage;
2559 if (pti->mask & TCIF_PARAM)
2560 infoPtr->items[iItem].lParam = pti->lParam;
2562 TAB_SetItemBounds(hwnd);
2563 if (infoPtr->uNumItem > 1)
2564 TAB_InvalidateTabArea(hwnd, infoPtr);
2566 InvalidateRect(hwnd, NULL, TRUE);
2568 TRACE("[%p]: added item %d %s\n",
2569 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2576 TAB_InsertItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2578 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2583 GetClientRect (hwnd, &rect);
2584 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2585 rect.top, rect.left, rect.bottom, rect.right);
2587 pti = (TCITEMW *)lParam;
2588 iItem = (INT)wParam;
2590 if (iItem < 0) return -1;
2591 if (iItem > infoPtr->uNumItem)
2592 iItem = infoPtr->uNumItem;
2594 TAB_DumpItemExternalW(pti, iItem);
2596 if (infoPtr->uNumItem == 0) {
2597 infoPtr->items = Alloc (sizeof (TAB_ITEM));
2598 infoPtr->uNumItem++;
2599 infoPtr->iSelected = 0;
2602 TAB_ITEM *oldItems = infoPtr->items;
2604 infoPtr->uNumItem++;
2605 infoPtr->items = Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2607 /* pre insert copy */
2609 memcpy (&infoPtr->items[0], &oldItems[0],
2610 iItem * sizeof(TAB_ITEM));
2613 /* post insert copy */
2614 if (iItem < infoPtr->uNumItem - 1) {
2615 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2616 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2620 if (iItem <= infoPtr->iSelected)
2621 infoPtr->iSelected++;
2626 infoPtr->items[iItem].mask = pti->mask;
2627 if (pti->mask & TCIF_TEXT)
2628 Str_SetPtrW (&infoPtr->items[iItem].pszText, pti->pszText);
2630 if (pti->mask & TCIF_IMAGE)
2631 infoPtr->items[iItem].iImage = pti->iImage;
2633 if (pti->mask & TCIF_PARAM)
2634 infoPtr->items[iItem].lParam = pti->lParam;
2636 TAB_SetItemBounds(hwnd);
2637 if (infoPtr->uNumItem > 1)
2638 TAB_InvalidateTabArea(hwnd, infoPtr);
2640 InvalidateRect(hwnd, NULL, TRUE);
2642 TRACE("[%p]: added item %d %s\n",
2643 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2650 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2652 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2653 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2655 BOOL bNeedPaint = FALSE;
2657 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2659 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2660 if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2662 infoPtr->tabWidth = max((INT)LOWORD(lParam), infoPtr->tabMinWidth);
2666 if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2668 if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2669 infoPtr->tabHeight = (INT)HIWORD(lParam);
2673 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2674 HIWORD(lResult), LOWORD(lResult),
2675 infoPtr->tabHeight, infoPtr->tabWidth);
2679 TAB_SetItemBounds(hwnd);
2680 RedrawWindow(hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2687 TAB_SetMinTabWidth (HWND hwnd, LPARAM lParam)
2689 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2690 INT cx = (INT)lParam;
2694 oldcx = infoPtr->tabMinWidth;
2695 infoPtr->tabMinWidth = (cx==-1)?DEFAULT_TAB_WIDTH:cx;
2703 TAB_HighlightItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2705 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2706 INT iItem = (INT)wParam;
2707 BOOL fHighlight = (BOOL)LOWORD(lParam);
2709 if ((infoPtr) && (iItem>=0) && (iItem<infoPtr->uNumItem)) {
2711 infoPtr->items[iItem].dwState |= TCIS_HIGHLIGHTED;
2713 infoPtr->items[iItem].dwState &= ~TCIS_HIGHLIGHTED;
2721 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2723 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2728 iItem = (INT)wParam;
2729 tabItem = (LPTCITEMA)lParam;
2731 TRACE("%d %p\n", iItem, tabItem);
2732 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2734 TAB_DumpItemExternalA(tabItem, iItem);
2736 wineItem = &infoPtr->items[iItem];
2738 if (tabItem->mask & TCIF_IMAGE)
2739 wineItem->iImage = tabItem->iImage;
2741 if (tabItem->mask & TCIF_PARAM)
2742 wineItem->lParam = tabItem->lParam;
2744 if (tabItem->mask & TCIF_RTLREADING)
2745 FIXME("TCIF_RTLREADING\n");
2747 if (tabItem->mask & TCIF_STATE)
2748 wineItem->dwState = tabItem->dwState;
2750 if (tabItem->mask & TCIF_TEXT)
2751 Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2753 /* Update and repaint tabs */
2754 TAB_SetItemBounds(hwnd);
2755 TAB_InvalidateTabArea(hwnd,infoPtr);
2762 TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2764 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2769 iItem = (INT)wParam;
2770 tabItem = (LPTCITEMW)lParam;
2772 TRACE("%d %p\n", iItem, tabItem);
2773 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2775 TAB_DumpItemExternalW(tabItem, iItem);
2777 wineItem = &infoPtr->items[iItem];
2779 if (tabItem->mask & TCIF_IMAGE)
2780 wineItem->iImage = tabItem->iImage;
2782 if (tabItem->mask & TCIF_PARAM)
2783 wineItem->lParam = tabItem->lParam;
2785 if (tabItem->mask & TCIF_RTLREADING)
2786 FIXME("TCIF_RTLREADING\n");
2788 if (tabItem->mask & TCIF_STATE)
2789 wineItem->dwState = tabItem->dwState;
2791 if (tabItem->mask & TCIF_TEXT)
2792 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2794 /* Update and repaint tabs */
2795 TAB_SetItemBounds(hwnd);
2796 TAB_InvalidateTabArea(hwnd,infoPtr);
2803 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2805 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2807 return infoPtr->uNumItem;
2812 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2814 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2819 iItem = (INT)wParam;
2820 tabItem = (LPTCITEMA)lParam;
2822 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2825 wineItem = &infoPtr->items[iItem];
2827 if (tabItem->mask & TCIF_IMAGE)
2828 tabItem->iImage = wineItem->iImage;
2830 if (tabItem->mask & TCIF_PARAM)
2831 tabItem->lParam = wineItem->lParam;
2833 if (tabItem->mask & TCIF_RTLREADING)
2834 FIXME("TCIF_RTLREADING\n");
2836 if (tabItem->mask & TCIF_STATE)
2837 tabItem->dwState = wineItem->dwState;
2839 if (tabItem->mask & TCIF_TEXT)
2840 Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2842 TAB_DumpItemExternalA(tabItem, iItem);
2849 TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2851 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2856 iItem = (INT)wParam;
2857 tabItem = (LPTCITEMW)lParam;
2859 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2862 wineItem=& infoPtr->items[iItem];
2864 if (tabItem->mask & TCIF_IMAGE)
2865 tabItem->iImage = wineItem->iImage;
2867 if (tabItem->mask & TCIF_PARAM)
2868 tabItem->lParam = wineItem->lParam;
2870 if (tabItem->mask & TCIF_RTLREADING)
2871 FIXME("TCIF_RTLREADING\n");
2873 if (tabItem->mask & TCIF_STATE)
2874 tabItem->dwState = wineItem->dwState;
2876 if (tabItem->mask & TCIF_TEXT)
2877 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2879 TAB_DumpItemExternalW(tabItem, iItem);
2886 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2888 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2889 INT iItem = (INT) wParam;
2890 BOOL bResult = FALSE;
2892 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2894 TAB_ITEM *oldItems = infoPtr->items;
2896 TAB_InvalidateTabArea(hwnd, infoPtr);
2898 infoPtr->uNumItem--;
2899 infoPtr->items = Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
2902 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
2904 if (iItem < infoPtr->uNumItem)
2905 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
2906 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
2910 /* Readjust the selected index */
2911 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2912 infoPtr->iSelected--;
2914 if (iItem < infoPtr->iSelected)
2915 infoPtr->iSelected--;
2917 if (infoPtr->uNumItem == 0)
2918 infoPtr->iSelected = -1;
2920 /* Reposition and repaint tabs */
2921 TAB_SetItemBounds(hwnd);
2930 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2932 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2934 TAB_InvalidateTabArea(hwnd,infoPtr);
2936 Free (infoPtr->items);
2937 infoPtr->uNumItem = 0;
2938 infoPtr->iSelected = -1;
2939 if (infoPtr->iHotTracked >= 0)
2940 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2941 infoPtr->iHotTracked = -1;
2943 TAB_SetItemBounds(hwnd);
2949 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2951 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2954 return (LRESULT)infoPtr->hFont;
2958 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2961 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2963 TRACE("%x %lx\n",wParam, lParam);
2965 infoPtr->hFont = (HFONT)wParam;
2967 TAB_SetItemBounds(hwnd);
2969 TAB_InvalidateTabArea(hwnd, infoPtr);
2976 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2978 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2981 return (LRESULT)infoPtr->himl;
2985 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2987 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2988 HIMAGELIST himlPrev;
2991 himlPrev = infoPtr->himl;
2992 infoPtr->himl= (HIMAGELIST)lParam;
2993 return (LRESULT)himlPrev;
2997 TAB_GetUnicodeFormat (HWND hwnd)
2999 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
3000 return infoPtr->bUnicode;
3004 TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
3006 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
3007 BOOL bTemp = infoPtr->bUnicode;
3009 infoPtr->bUnicode = (BOOL)wParam;
3015 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
3018 /* I'm not really sure what the following code was meant to do.
3019 This is what it is doing:
3020 When WM_SIZE is sent with SIZE_RESTORED, the control
3021 gets positioned in the top left corner.
3025 UINT uPosFlags,cx,cy;
3029 parent = GetParent (hwnd);
3030 GetClientRect(parent, &parent_rect);
3033 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
3034 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
3036 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
3037 cx, cy, uPosFlags | SWP_NOZORDER);
3039 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
3042 /* Recompute the size/position of the tabs. */
3043 TAB_SetItemBounds (hwnd);
3045 /* Force a repaint of the control. */
3046 InvalidateRect(hwnd, NULL, TRUE);
3053 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
3056 TEXTMETRICA fontMetrics;
3061 infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
3063 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
3065 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
3066 infoPtr->uNumItem = 0;
3067 infoPtr->uNumRows = 0;
3068 infoPtr->uHItemPadding = 6;
3069 infoPtr->uVItemPadding = 3;
3070 infoPtr->uHItemPadding_s = 6;
3071 infoPtr->uVItemPadding_s = 3;
3074 infoPtr->hcurArrow = LoadCursorA (0, (LPSTR)IDC_ARROW);
3075 infoPtr->iSelected = -1;
3076 infoPtr->iHotTracked = -1;
3077 infoPtr->uFocus = -1;
3078 infoPtr->hwndToolTip = 0;
3079 infoPtr->DoRedraw = TRUE;
3080 infoPtr->needsScrolling = FALSE;
3081 infoPtr->hwndUpDown = 0;
3082 infoPtr->leftmostVisible = 0;
3083 infoPtr->fHeightSet = FALSE;
3084 infoPtr->bUnicode = IsWindowUnicode (hwnd);
3086 TRACE("Created tab control, hwnd [%p]\n", hwnd);
3088 /* The tab control always has the WS_CLIPSIBLINGS style. Even
3089 if you don't specify it in CreateWindow. This is necessary in
3090 order for paint to work correctly. This follows windows behaviour. */
3091 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
3092 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
3094 if (dwStyle & TCS_TOOLTIPS) {
3095 /* Create tooltip control */
3096 infoPtr->hwndToolTip =
3097 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
3098 CW_USEDEFAULT, CW_USEDEFAULT,
3099 CW_USEDEFAULT, CW_USEDEFAULT,
3102 /* Send NM_TOOLTIPSCREATED notification */
3103 if (infoPtr->hwndToolTip) {
3104 NMTOOLTIPSCREATED nmttc;
3106 nmttc.hdr.hwndFrom = hwnd;
3107 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
3108 nmttc.hdr.code = NM_TOOLTIPSCREATED;
3109 nmttc.hwndToolTips = infoPtr->hwndToolTip;
3111 SendMessageA (infoPtr->hwndNotify, WM_NOTIFY,
3112 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
3117 * We need to get text information so we need a DC and we need to select
3121 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3123 /* Use the system font to determine the initial height of a tab. */
3124 GetTextMetricsA(hdc, &fontMetrics);
3127 * Make sure there is enough space for the letters + growing the
3128 * selected item + extra space for the selected item.
3130 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3131 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
3132 infoPtr->uVItemPadding;
3134 /* Initialize the width of a tab. */
3135 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
3136 infoPtr->tabMinWidth = 0;
3138 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3140 SelectObject (hdc, hOldFont);
3141 ReleaseDC(hwnd, hdc);
3147 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
3149 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3155 if (infoPtr->items) {
3156 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3157 if (infoPtr->items[iItem].pszText)
3158 Free (infoPtr->items[iItem].pszText);
3160 Free (infoPtr->items);
3163 if (infoPtr->hwndToolTip)
3164 DestroyWindow (infoPtr->hwndToolTip);
3166 if (infoPtr->hwndUpDown)
3167 DestroyWindow(infoPtr->hwndUpDown);
3169 if (infoPtr->iHotTracked >= 0)
3170 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
3173 SetWindowLongA(hwnd, 0, 0);
3177 static LRESULT WINAPI
3178 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3180 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3182 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3183 if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
3184 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3188 case TCM_GETIMAGELIST:
3189 return TAB_GetImageList (hwnd, wParam, lParam);
3191 case TCM_SETIMAGELIST:
3192 return TAB_SetImageList (hwnd, wParam, lParam);
3194 case TCM_GETITEMCOUNT:
3195 return TAB_GetItemCount (hwnd, wParam, lParam);
3198 return TAB_GetItemA (hwnd, wParam, lParam);
3201 return TAB_GetItemW (hwnd, wParam, lParam);
3204 return TAB_SetItemA (hwnd, wParam, lParam);
3207 return TAB_SetItemW (hwnd, wParam, lParam);
3209 case TCM_DELETEITEM:
3210 return TAB_DeleteItem (hwnd, wParam, lParam);
3212 case TCM_DELETEALLITEMS:
3213 return TAB_DeleteAllItems (hwnd, wParam, lParam);
3215 case TCM_GETITEMRECT:
3216 return TAB_GetItemRect (hwnd, wParam, lParam);
3219 return TAB_GetCurSel (hwnd);
3222 return TAB_HitTest (hwnd, wParam, lParam);
3225 return TAB_SetCurSel (hwnd, wParam);
3227 case TCM_INSERTITEMA:
3228 return TAB_InsertItemA (hwnd, wParam, lParam);
3230 case TCM_INSERTITEMW:
3231 return TAB_InsertItemW (hwnd, wParam, lParam);
3233 case TCM_SETITEMEXTRA:
3234 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
3237 case TCM_ADJUSTRECT:
3238 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
3240 case TCM_SETITEMSIZE:
3241 return TAB_SetItemSize (hwnd, wParam, lParam);
3243 case TCM_REMOVEIMAGE:
3244 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3247 case TCM_SETPADDING:
3248 return TAB_SetPadding (hwnd, wParam, lParam);
3250 case TCM_GETROWCOUNT:
3251 return TAB_GetRowCount(hwnd);
3253 case TCM_GETUNICODEFORMAT:
3254 return TAB_GetUnicodeFormat (hwnd);
3256 case TCM_SETUNICODEFORMAT:
3257 return TAB_SetUnicodeFormat (hwnd, wParam);
3259 case TCM_HIGHLIGHTITEM:
3260 return TAB_HighlightItem (hwnd, wParam, lParam);
3262 case TCM_GETTOOLTIPS:
3263 return TAB_GetToolTips (hwnd, wParam, lParam);
3265 case TCM_SETTOOLTIPS:
3266 return TAB_SetToolTips (hwnd, wParam, lParam);
3268 case TCM_GETCURFOCUS:
3269 return TAB_GetCurFocus (hwnd);
3271 case TCM_SETCURFOCUS:
3272 return TAB_SetCurFocus (hwnd, wParam);
3274 case TCM_SETMINTABWIDTH:
3275 return TAB_SetMinTabWidth(hwnd, lParam);
3277 case TCM_DESELECTALL:
3278 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3281 case TCM_GETEXTENDEDSTYLE:
3282 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3285 case TCM_SETEXTENDEDSTYLE:
3286 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3290 return TAB_GetFont (hwnd, wParam, lParam);
3293 return TAB_SetFont (hwnd, wParam, lParam);
3296 return TAB_Create (hwnd, wParam, lParam);
3299 return TAB_Destroy (hwnd, wParam, lParam);
3302 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3304 case WM_LBUTTONDOWN:
3305 return TAB_LButtonDown (hwnd, wParam, lParam);
3308 return TAB_LButtonUp (hwnd, wParam, lParam);
3311 return SendMessageA(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3313 case WM_RBUTTONDOWN:
3314 return TAB_RButtonDown (hwnd, wParam, lParam);
3317 return TAB_MouseMove (hwnd, wParam, lParam);
3320 return TAB_EraseBackground (hwnd, (HDC)wParam);
3323 return TAB_Paint (hwnd, wParam);
3326 return TAB_Size (hwnd, wParam, lParam);
3329 return TAB_SetRedraw (hwnd, wParam);
3332 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3334 case WM_STYLECHANGED:
3335 TAB_SetItemBounds (hwnd);
3336 InvalidateRect(hwnd, NULL, TRUE);
3339 case WM_SYSCOLORCHANGE:
3340 COMCTL32_RefreshSysColors();
3345 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
3348 return TAB_KeyUp(hwnd, wParam);
3350 return TAB_NCHitTest(hwnd, lParam);
3353 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
3354 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3355 uMsg, wParam, lParam);
3356 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
3368 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3369 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3370 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
3371 wndClass.cbClsExtra = 0;
3372 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3373 wndClass.hCursor = LoadCursorA (0, (LPSTR)IDC_ARROW);
3374 wndClass.hbrBackground = NULL;
3375 wndClass.lpszClassName = WC_TABCONTROLA;
3377 RegisterClassA (&wndClass);
3382 TAB_Unregister (void)
3384 UnregisterClassA (WC_TABCONTROLA, NULL);