4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Unicode support (under construction)
27 * UpDown control not displayed until after a tab is clicked on
35 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(tab);
47 RECT rect; /* bounding rectangle of the item relative to the
48 * leftmost item (the leftmost item, 0, would have a
49 * "left" member of 0 in this rectangle)
51 * additionally the top member hold the row number
52 * and bottom is unused and should be 0 */
57 UINT uNumItem; /* number of tab items */
58 UINT uNumRows; /* number of tab rows */
59 INT tabHeight; /* height of the tab row */
60 INT tabWidth; /* width of tabs */
61 USHORT uHItemPadding; /* amount of horizontal padding, in pixels */
62 USHORT uVItemPadding; /* amount of vertical padding, in pixels */
63 HFONT hFont; /* handle to the current font */
64 HCURSOR hcurArrow; /* handle to the current cursor */
65 HIMAGELIST himl; /* handle to a image list (may be 0) */
66 HWND hwndToolTip; /* handle to tab's tooltip */
67 INT leftmostVisible; /* Used for scrolling, this member contains
68 * the index of the first visible item */
69 INT iSelected; /* the currently selected item */
70 INT iHotTracked; /* the highlighted item under the mouse */
71 INT uFocus; /* item which has the focus */
72 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
73 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
74 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
75 * the size of the control */
76 BOOL fSizeSet; /* was the size of the tabs explicitly set? */
77 BOOL bUnicode; /* Unicode control? */
78 HWND hwndUpDown; /* Updown control used for scrolling */
81 /******************************************************************************
82 * Positioning constants
84 #define SELECTED_TAB_OFFSET 2
85 #define ROUND_CORNER_SIZE 2
86 #define DISPLAY_AREA_PADDINGX 2
87 #define DISPLAY_AREA_PADDINGY 2
88 #define CONTROL_BORDER_SIZEX 2
89 #define CONTROL_BORDER_SIZEY 2
90 #define BUTTON_SPACINGX 4
91 #define BUTTON_SPACINGY 4
92 #define FLAT_BTN_SPACINGX 8
93 #define DEFAULT_TAB_WIDTH 96
95 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
97 /******************************************************************************
98 * Hot-tracking timer constants
100 #define TAB_HOTTRACK_TIMER 1
101 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
103 /******************************************************************************
106 static void TAB_Refresh (HWND hwnd, HDC hdc);
107 static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
108 static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
109 static void TAB_DrawItem(HWND hwnd, HDC hdc, INT iItem);
110 static void TAB_DrawItemInterior(HWND hwnd, HDC hdc, INT iItem, RECT* drawRect);
113 TAB_SendSimpleNotify (HWND hwnd, UINT code)
117 nmhdr.hwndFrom = hwnd;
118 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
121 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
122 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
126 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
127 WPARAM wParam, LPARAM lParam)
135 msg.time = GetMessageTime ();
136 msg.pt.x = LOWORD(GetMessagePos ());
137 msg.pt.y = HIWORD(GetMessagePos ());
139 SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
143 TAB_DumpItemExternalA(TCITEMA *pti, UINT iItem)
146 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
147 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
148 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextA=%s\n",
149 iItem, pti->iImage, pti->lParam, debugstr_a(pti->pszText));
155 TAB_DumpItemExternalW(TCITEMW *pti, UINT iItem)
158 TRACE("external tab %d, mask=0x%08x, dwState=0x%08lx, dwStateMask=0x%08lx, cchTextMax=0x%08x\n",
159 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
160 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
161 iItem, pti->iImage, pti->lParam, debugstr_w(pti->pszText));
166 TAB_DumpItemInternal(TAB_INFO *infoPtr, UINT iItem)
171 ti = &infoPtr->items[iItem];
172 TRACE("tab %d, mask=0x%08x, dwState=0x%08lx, pszText=%s, iImage=%d\n",
173 iItem, ti->mask, ti->dwState, debugstr_w(ti->pszText),
175 TRACE("tab %d, lParam=0x%08lx, rect.left=%ld, rect.top(row)=%ld\n",
176 iItem, ti->lParam, ti->rect.left, ti->rect.top);
181 TAB_GetCurSel (HWND hwnd)
183 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
185 return infoPtr->iSelected;
189 TAB_GetCurFocus (HWND hwnd)
191 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
193 return infoPtr->uFocus;
197 TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
199 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
201 if (infoPtr == NULL) return 0;
202 return (LRESULT)infoPtr->hwndToolTip;
206 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
208 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
209 INT iItem = (INT)wParam;
213 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
214 prevItem=infoPtr->iSelected;
215 infoPtr->iSelected=iItem;
216 TAB_EnsureSelectionVisible(hwnd, infoPtr);
217 TAB_InvalidateTabArea(hwnd, infoPtr);
223 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
225 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
226 INT iItem=(INT) wParam;
228 if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
230 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
231 FIXME("Should set input focus\n");
233 int oldFocus = infoPtr->uFocus;
234 if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) {
235 infoPtr->uFocus = iItem;
236 if (oldFocus != -1) {
237 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
238 infoPtr->iSelected = iItem;
239 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
242 infoPtr->iSelected = iItem;
243 TAB_EnsureSelectionVisible(hwnd, infoPtr);
244 TAB_InvalidateTabArea(hwnd, infoPtr);
252 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
254 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
256 if (infoPtr == NULL) return 0;
257 infoPtr->hwndToolTip = (HWND)wParam;
262 TAB_SetPadding (HWND hwnd, WPARAM wParam, LPARAM lParam)
264 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
266 if (infoPtr == NULL) return 0;
267 infoPtr->uHItemPadding=LOWORD(lParam);
268 infoPtr->uVItemPadding=HIWORD(lParam);
272 /******************************************************************************
273 * TAB_InternalGetItemRect
275 * This method will calculate the rectangle representing a given tab item in
276 * client coordinates. This method takes scrolling into account.
278 * This method returns TRUE if the item is visible in the window and FALSE
279 * if it is completely outside the client area.
281 static BOOL TAB_InternalGetItemRect(
288 RECT tmpItemRect,clientRect;
289 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
291 /* Perform a sanity check and a trivial visibility check. */
292 if ( (infoPtr->uNumItem <= 0) ||
293 (itemIndex >= infoPtr->uNumItem) ||
294 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
298 * Avoid special cases in this procedure by assigning the "out"
299 * parameters if the caller didn't supply them
301 if (itemRect == NULL)
302 itemRect = &tmpItemRect;
304 /* Retrieve the unmodified item rect. */
305 *itemRect = infoPtr->items[itemIndex].rect;
307 /* calculate the times bottom and top based on the row */
308 GetClientRect(hwnd, &clientRect);
310 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
312 itemRect->bottom = clientRect.bottom -
313 SELECTED_TAB_OFFSET -
314 itemRect->top * (infoPtr->tabHeight - 2) -
315 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
317 itemRect->top = clientRect.bottom -
319 itemRect->top * (infoPtr->tabHeight - 2) -
320 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
322 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
324 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * (infoPtr->tabHeight - 2) -
325 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
326 itemRect->left = clientRect.right - infoPtr->tabHeight - itemRect->left * (infoPtr->tabHeight - 2) -
327 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
329 else if((lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM))
331 itemRect->right = clientRect.left + infoPtr->tabHeight + itemRect->left * (infoPtr->tabHeight - 2) +
332 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
333 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * (infoPtr->tabHeight - 2) +
334 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
336 else if(!(lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM)) /* not TCS_BOTTOM and not TCS_VERTICAL */
338 itemRect->bottom = clientRect.top +
340 itemRect->top * (infoPtr->tabHeight - 2) +
341 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
342 itemRect->top = clientRect.top +
343 SELECTED_TAB_OFFSET +
344 itemRect->top * (infoPtr->tabHeight - 2) +
345 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
349 * "scroll" it to make sure the item at the very left of the
350 * tab control is the leftmost visible tab.
352 if(lStyle & TCS_VERTICAL)
356 -(clientRect.bottom - infoPtr->items[infoPtr->leftmostVisible].rect.bottom));
359 * Move the rectangle so the first item is slightly offset from
360 * the bottom of the tab control.
364 -SELECTED_TAB_OFFSET);
369 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
373 * Move the rectangle so the first item is slightly offset from
374 * the left of the tab control.
380 TRACE("item %d tab h=%d, rect=(%ld,%ld)-(%ld,%ld)\n",
381 itemIndex, infoPtr->tabHeight,
382 itemRect->left, itemRect->top, itemRect->right, itemRect->bottom);
384 /* Now, calculate the position of the item as if it were selected. */
385 if (selectedRect!=NULL)
387 CopyRect(selectedRect, itemRect);
389 /* The rectangle of a selected item is a bit wider. */
390 if(lStyle & TCS_VERTICAL)
391 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
393 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
395 /* If it also a bit higher. */
396 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
398 selectedRect->top -= 2; /* the border is thicker on the bottom */
399 selectedRect->bottom += SELECTED_TAB_OFFSET;
401 else 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;
413 selectedRect->top -= SELECTED_TAB_OFFSET;
414 selectedRect->bottom += 1;
421 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
423 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
424 (LPRECT)lParam, (LPRECT)NULL);
427 /******************************************************************************
430 * This method is called to handle keyboard input
432 static LRESULT TAB_KeyUp(
436 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
442 newItem = infoPtr->uFocus - 1;
445 newItem = infoPtr->uFocus + 1;
450 * If we changed to a valid item, change the selection
452 if ((newItem >= 0) &&
453 (newItem < infoPtr->uNumItem) &&
454 (infoPtr->uFocus != newItem))
456 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
458 infoPtr->iSelected = newItem;
459 infoPtr->uFocus = newItem;
460 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
462 TAB_EnsureSelectionVisible(hwnd, infoPtr);
463 TAB_InvalidateTabArea(hwnd, infoPtr);
470 /******************************************************************************
473 * This method is called whenever the focus goes in or out of this control
474 * it is used to update the visual state of the control.
476 static LRESULT TAB_FocusChanging(
482 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
487 * Get the rectangle for the item.
489 isVisible = TAB_InternalGetItemRect(hwnd,
496 * If the rectangle is not completely invisible, invalidate that
497 * portion of the window.
501 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
502 selectedRect.left,selectedRect.top,
503 selectedRect.right,selectedRect.bottom);
504 InvalidateRect(hwnd, &selectedRect, TRUE);
508 * Don't otherwise disturb normal behavior.
510 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
513 static INT TAB_InternalHitTest (
523 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
525 TAB_InternalGetItemRect(hwnd, infoPtr, iCount, &rect, NULL);
527 if (PtInRect(&rect, pt))
529 *flags = TCHT_ONITEM;
534 *flags = TCHT_NOWHERE;
539 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
541 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
542 LPTCHITTESTINFO lptest = (LPTCHITTESTINFO) lParam;
544 return TAB_InternalHitTest (hwnd, infoPtr, lptest->pt, &lptest->flags);
547 /******************************************************************************
550 * Napster v2b5 has a tab control for its main navigation which has a client
551 * area that covers the whole area of the dialog pages.
552 * That's why it receives all msgs for that area and the underlying dialog ctrls
554 * So I decided that we should handle WM_NCHITTEST here and return
555 * HTTRANSPARENT if we don't hit the tab control buttons.
556 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
557 * doesn't do it that way. Maybe depends on tab control styles ?
560 TAB_NCHitTest (HWND hwnd, LPARAM lParam)
562 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
566 pt.x = LOWORD(lParam);
567 pt.y = HIWORD(lParam);
568 ScreenToClient(hwnd, &pt);
570 if (TAB_InternalHitTest(hwnd, infoPtr, pt, &dummyflag) == -1)
571 return HTTRANSPARENT;
577 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
579 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
583 if (infoPtr->hwndToolTip)
584 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
585 WM_LBUTTONDOWN, wParam, lParam);
587 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
591 if (infoPtr->hwndToolTip)
592 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
593 WM_LBUTTONDOWN, wParam, lParam);
595 pt.x = (INT)LOWORD(lParam);
596 pt.y = (INT)HIWORD(lParam);
598 newItem = TAB_InternalHitTest (hwnd, infoPtr, pt, &dummy);
600 TRACE("On Tab, item %d\n", newItem);
602 if ((newItem != -1) && (infoPtr->iSelected != newItem))
604 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING) != TRUE)
606 infoPtr->iSelected = newItem;
607 infoPtr->uFocus = newItem;
608 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
610 TAB_EnsureSelectionVisible(hwnd, infoPtr);
612 TAB_InvalidateTabArea(hwnd, infoPtr);
619 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
621 TAB_SendSimpleNotify(hwnd, NM_CLICK);
627 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
629 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
633 /******************************************************************************
634 * TAB_DrawLoneItemInterior
636 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
637 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
638 * up the device context and font. This routine does the same setup but
639 * only calls TAB_DrawItemInterior for the single specified item.
642 TAB_DrawLoneItemInterior(HWND hwnd, TAB_INFO* infoPtr, int iItem)
644 HDC hdc = GetDC(hwnd);
645 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
646 TAB_DrawItemInterior(hwnd, hdc, iItem, NULL);
647 SelectObject(hdc, hOldFont);
648 ReleaseDC(hwnd, hdc);
651 /******************************************************************************
652 * TAB_HotTrackTimerProc
654 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
655 * timer is setup so we can check if the mouse is moved out of our window.
656 * (We don't get an event when the mouse leaves, the mouse-move events just
657 * stop being delivered to our window and just start being delivered to
658 * another window.) This function is called when the timer triggers so
659 * we can check if the mouse has left our window. If so, we un-highlight
660 * the hot-tracked tab.
663 TAB_HotTrackTimerProc
665 HWND hwnd, /* handle of window for timer messages */
666 UINT uMsg, /* WM_TIMER message */
667 UINT idEvent, /* timer identifier */
668 DWORD dwTime /* current system time */
671 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
673 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
678 ** If we can't get the cursor position, or if the cursor is outside our
679 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
680 ** "outside" even if it is within our bounding rect if another window
681 ** overlaps. Note also that the case where the cursor stayed within our
682 ** window but has moved off the hot-tracked tab will be handled by the
683 ** WM_MOUSEMOVE event.
685 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
687 /* Redraw iHotTracked to look normal */
688 INT iRedraw = infoPtr->iHotTracked;
689 infoPtr->iHotTracked = -1;
690 TAB_DrawLoneItemInterior(hwnd, infoPtr, iRedraw);
692 /* Kill this timer */
693 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
698 /******************************************************************************
701 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
702 * should be highlighted. This function determines which tab in a tab control,
703 * if any, is under the mouse and records that information. The caller may
704 * supply output parameters to receive the item number of the tab item which
705 * was highlighted but isn't any longer and of the tab item which is now
706 * highlighted but wasn't previously. The caller can use this information to
707 * selectively redraw those tab items.
709 * If the caller has a mouse position, it can supply it through the pos
710 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
711 * supplies NULL and this function determines the current mouse position
719 int* out_redrawLeave,
723 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
728 if (out_redrawLeave != NULL)
729 *out_redrawLeave = -1;
730 if (out_redrawEnter != NULL)
731 *out_redrawEnter = -1;
733 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_HOTTRACK)
741 ScreenToClient(hwnd, &pt);
749 item = TAB_InternalHitTest(hwnd, infoPtr, pt, &flags);
752 if (item != infoPtr->iHotTracked)
754 if (infoPtr->iHotTracked >= 0)
756 /* Mark currently hot-tracked to be redrawn to look normal */
757 if (out_redrawLeave != NULL)
758 *out_redrawLeave = infoPtr->iHotTracked;
762 /* Kill timer which forces recheck of mouse pos */
763 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
768 /* Start timer so we recheck mouse pos */
769 UINT timerID = SetTimer
773 TAB_HOTTRACK_TIMER_INTERVAL,
774 TAB_HotTrackTimerProc
778 return; /* Hot tracking not available */
781 infoPtr->iHotTracked = item;
785 /* Mark new hot-tracked to be redrawn to look highlighted */
786 if (out_redrawEnter != NULL)
787 *out_redrawEnter = item;
792 /******************************************************************************
795 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
798 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
803 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
805 if (infoPtr->hwndToolTip)
806 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
807 WM_LBUTTONDOWN, wParam, lParam);
809 /* Determine which tab to highlight. Redraw tabs which change highlight
811 TAB_RecalcHotTrack(hwnd, &lParam, &redrawLeave, &redrawEnter);
813 if (redrawLeave != -1)
814 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawLeave);
815 if (redrawEnter != -1)
816 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawEnter);
821 /******************************************************************************
824 * Calculates the tab control's display area given the window rectangle or
825 * the window rectangle given the requested display rectangle.
827 static LRESULT TAB_AdjustRect(
832 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
833 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
835 if(lStyle & TCS_VERTICAL)
837 if (fLarger) /* Go from display rectangle */
839 /* Add the height of the tabs. */
840 if (lStyle & TCS_BOTTOM)
841 prc->right += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
843 prc->left -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
845 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
846 /* Inflate the rectangle for the padding */
847 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
849 /* Inflate for the border */
850 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
852 else /* Go from window rectangle. */
854 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
855 /* Deflate the rectangle for the border */
856 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
858 /* Deflate the rectangle for the padding */
859 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
861 /* Remove the height of the tabs. */
862 if (lStyle & TCS_BOTTOM)
863 prc->right -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
865 prc->left += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
869 if (fLarger) /* Go from display rectangle */
871 /* Add the height of the tabs. */
872 if (lStyle & TCS_BOTTOM)
873 prc->bottom += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
875 prc->top -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
877 /* Inflate the rectangle for the padding */
878 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
880 /* Inflate for the border */
881 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
883 else /* Go from window rectangle. */
885 /* Deflate the rectangle for the border */
886 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
888 /* Deflate the rectangle for the padding */
889 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
891 /* Remove the height of the tabs. */
892 if (lStyle & TCS_BOTTOM)
893 prc->bottom -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
895 prc->top += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
902 /******************************************************************************
905 * This method will handle the notification from the scroll control and
906 * perform the scrolling operation on the tab control.
908 static LRESULT TAB_OnHScroll(
914 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
916 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
918 if(nPos < infoPtr->leftmostVisible)
919 infoPtr->leftmostVisible--;
921 infoPtr->leftmostVisible++;
923 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
924 TAB_InvalidateTabArea(hwnd, infoPtr);
925 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
926 MAKELONG(infoPtr->leftmostVisible, 0));
932 /******************************************************************************
935 * This method will check the current scrolling state and make sure the
936 * scrolling control is displayed (or not).
938 static void TAB_SetupScrolling(
941 const RECT* clientRect)
944 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
946 if (infoPtr->needsScrolling)
952 * Calculate the position of the scroll control.
954 if(lStyle & TCS_VERTICAL)
956 controlPos.right = clientRect->right;
957 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
959 if (lStyle & TCS_BOTTOM)
961 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
962 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
966 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
967 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
972 controlPos.right = clientRect->right;
973 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
975 if (lStyle & TCS_BOTTOM)
977 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
978 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
982 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
983 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
988 * If we don't have a scroll control yet, we want to create one.
989 * If we have one, we want to make sure it's positioned properly.
991 if (infoPtr->hwndUpDown==0)
993 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
995 WS_VISIBLE | WS_CHILD | UDS_HORZ,
996 controlPos.left, controlPos.top,
997 controlPos.right - controlPos.left,
998 controlPos.bottom - controlPos.top,
1006 SetWindowPos(infoPtr->hwndUpDown,
1008 controlPos.left, controlPos.top,
1009 controlPos.right - controlPos.left,
1010 controlPos.bottom - controlPos.top,
1011 SWP_SHOWWINDOW | SWP_NOZORDER);
1014 /* Now calculate upper limit of the updown control range.
1015 * We do this by calculating how many tabs will be offscreen when the
1016 * last tab is visible.
1018 if(infoPtr->uNumItem)
1020 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
1021 maxRange = infoPtr->uNumItem;
1022 tabwidth = infoPtr->items[maxRange - 1].rect.right;
1024 for(; maxRange > 0; maxRange--)
1026 if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
1030 if(maxRange == infoPtr->uNumItem)
1036 /* If we once had a scroll control... hide it */
1037 if (infoPtr->hwndUpDown!=0)
1038 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1040 if (infoPtr->hwndUpDown)
1041 SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1044 /******************************************************************************
1047 * This method will calculate the position rectangles of all the items in the
1048 * control. The rectangle calculated starts at 0 for the first item in the
1049 * list and ignores scrolling and selection.
1050 * It also uses the current font to determine the height of the tab row and
1051 * it checks if all the tabs fit in the client area of the window. If they
1052 * dont, a scrolling control is added.
1054 static void TAB_SetItemBounds (HWND hwnd)
1056 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1057 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1058 TEXTMETRICA fontMetrics;
1061 INT curItemRowCount;
1062 HFONT hFont, hOldFont;
1071 * We need to get text information so we need a DC and we need to select
1076 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1077 hOldFont = SelectObject (hdc, hFont);
1080 * We will base the rectangle calculations on the client rectangle
1083 GetClientRect(hwnd, &clientRect);
1085 /* if TCS_VERTICAL then swap the height and width so this code places the
1086 tabs along the top of the rectangle and we can just rotate them after
1087 rather than duplicate all of the below code */
1088 if(lStyle & TCS_VERTICAL)
1090 iTemp = clientRect.bottom;
1091 clientRect.bottom = clientRect.right;
1092 clientRect.right = iTemp;
1095 /* The leftmost item will be "0" aligned */
1097 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1099 if (!(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet) )
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 + SELECTED_TAB_OFFSET +
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 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1133 /* Set the leftmost position of the tab. */
1134 infoPtr->items[curItem].rect.left = curItemLeftPos;
1136 if ( (lStyle & TCS_FIXEDWIDTH) || ((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
1138 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1140 2 * infoPtr->uHItemPadding;
1147 /* Calculate how wide the tab is depending on the text it contains */
1148 GetTextExtentPoint32W(hdc, infoPtr->items[curItem].pszText,
1149 lstrlenW(infoPtr->items[curItem].pszText), &size);
1151 /* under Windows, there seems to be a minimum width of 2x the height
1152 * for button style tabs */
1153 if (lStyle & TCS_BUTTONS)
1154 size.cx = max(size.cx, 2 * (infoPtr->tabHeight - 2));
1156 /* Add the icon width */
1159 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1163 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1164 size.cx + icon_width +
1165 num * infoPtr->uHItemPadding;
1166 TRACE("for <%s>, l,r=%ld,%ld, num=%d\n",
1167 debugstr_w(infoPtr->items[curItem].pszText),
1168 infoPtr->items[curItem].rect.left,
1169 infoPtr->items[curItem].rect.right,
1174 * Check if this is a multiline tab control and if so
1175 * check to see if we should wrap the tabs
1177 * Because we are going to arange all these tabs evenly
1178 * really we are basically just counting rows at this point
1182 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1183 (infoPtr->items[curItem].rect.right > clientRect.right))
1185 infoPtr->items[curItem].rect.right -=
1186 infoPtr->items[curItem].rect.left;
1188 infoPtr->items[curItem].rect.left = 0;
1190 TRACE("wrapping <%s>, l,r=%ld,%ld\n",
1191 debugstr_w(infoPtr->items[curItem].pszText),
1192 infoPtr->items[curItem].rect.left,
1193 infoPtr->items[curItem].rect.right);
1196 infoPtr->items[curItem].rect.bottom = 0;
1197 infoPtr->items[curItem].rect.top = curItemRowCount - 1;
1199 TRACE("TextSize: %li\n", size.cx);
1200 TRACE("Rect: T %li, L %li, B %li, R %li\n",
1201 infoPtr->items[curItem].rect.top,
1202 infoPtr->items[curItem].rect.left,
1203 infoPtr->items[curItem].rect.bottom,
1204 infoPtr->items[curItem].rect.right);
1207 * The leftmost position of the next item is the rightmost position
1210 if (lStyle & TCS_BUTTONS)
1212 curItemLeftPos = infoPtr->items[curItem].rect.right + 1;
1213 if (lStyle & TCS_FLATBUTTONS)
1214 curItemLeftPos += FLAT_BTN_SPACINGX;
1217 curItemLeftPos = infoPtr->items[curItem].rect.right;
1220 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1223 * Check if we need a scrolling control.
1225 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1228 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1229 if(!infoPtr->needsScrolling)
1230 infoPtr->leftmostVisible = 0;
1232 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
1235 /* Set the number of rows */
1236 infoPtr->uNumRows = curItemRowCount;
1238 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->uNumItem > 0))
1240 INT widthDiff, remainder;
1241 INT tabPerRow,remTab;
1243 INT iIndexStart=0,iIndexEnd=0, iCount=0;
1246 * Ok windows tries to even out the rows. place the same
1247 * number of tabs in each row. So lets give that a shot
1250 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1251 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1253 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1254 iItm<infoPtr->uNumItem;
1257 /* normalize the current rect */
1259 /* shift the item to the left side of the clientRect */
1260 infoPtr->items[iItm].rect.right -=
1261 infoPtr->items[iItm].rect.left;
1262 infoPtr->items[iItm].rect.left = 0;
1264 TRACE("r=%ld, cl=%d, cl.r=%ld, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1265 infoPtr->items[iItm].rect.right,
1266 curItemLeftPos, clientRect.right,
1267 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1269 /* if we have reached the maximum number of tabs on this row */
1270 /* move to the next row, reset our current item left position and */
1271 /* the count of items on this row */
1273 /* ************ FIXME FIXME FIXME *************** */
1277 /* if item n and n+1 are in the same row, */
1278 /* then the display has n+1 lower (toward the */
1279 /* bottom) than n. We do it just the */
1282 /* ************ FIXME FIXME FIXME *************** */
1284 if (lStyle & TCS_VERTICAL) {
1285 /* Vert: Add the remaining tabs in the *last* remainder rows */
1286 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1292 /* Horz: Add the remaining tabs in the *first* remainder rows */
1293 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1300 /* shift the item to the right to place it as the next item in this row */
1301 infoPtr->items[iItm].rect.left += curItemLeftPos;
1302 infoPtr->items[iItm].rect.right += curItemLeftPos;
1303 infoPtr->items[iItm].rect.top = iRow;
1304 if (lStyle & TCS_BUTTONS)
1306 curItemLeftPos = infoPtr->items[iItm].rect.right + 1;
1307 if (lStyle & TCS_FLATBUTTONS)
1308 curItemLeftPos += FLAT_BTN_SPACINGX;
1311 curItemLeftPos = infoPtr->items[iItm].rect.right;
1313 TRACE("arranging <%s>, l,r=%ld,%ld, row=%ld\n",
1314 debugstr_w(infoPtr->items[iItm].pszText),
1315 infoPtr->items[iItm].rect.left,
1316 infoPtr->items[iItm].rect.right,
1317 infoPtr->items[iItm].rect.top);
1324 while(iIndexStart < infoPtr->uNumItem)
1327 * find the indexs of the row
1329 /* find the first item on the next row */
1330 for (iIndexEnd=iIndexStart;
1331 (iIndexEnd < infoPtr->uNumItem) &&
1332 (infoPtr->items[iIndexEnd].rect.top ==
1333 infoPtr->items[iIndexStart].rect.top) ;
1335 /* intentionally blank */;
1338 * we need to justify these tabs so they fill the whole given
1342 /* find the amount of space remaining on this row */
1343 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1344 infoPtr->items[iIndexEnd - 1].rect.right;
1346 /* iCount is the number of tab items on this row */
1347 iCount = iIndexEnd - iIndexStart;
1352 remainder = widthDiff % iCount;
1353 widthDiff = widthDiff / iCount;
1354 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1355 for (iIndex=iIndexStart,iCount=0; iIndex < iIndexEnd;
1358 infoPtr->items[iIndex].rect.left += iCount * widthDiff;
1359 infoPtr->items[iIndex].rect.right += (iCount + 1) * widthDiff;
1361 TRACE("adjusting 1 <%s>, l,r=%ld,%ld\n",
1362 debugstr_w(infoPtr->items[iIndex].pszText),
1363 infoPtr->items[iIndex].rect.left,
1364 infoPtr->items[iIndex].rect.right);
1367 infoPtr->items[iIndex - 1].rect.right += remainder;
1369 else /* we have only one item on this row, make it take up the entire row */
1371 infoPtr->items[iIndexStart].rect.left = clientRect.left;
1372 infoPtr->items[iIndexStart].rect.right = clientRect.right - 4;
1374 TRACE("adjusting 2 <%s>, l,r=%ld,%ld\n",
1375 debugstr_w(infoPtr->items[iIndexStart].pszText),
1376 infoPtr->items[iIndexStart].rect.left,
1377 infoPtr->items[iIndexStart].rect.right);
1382 iIndexStart = iIndexEnd;
1387 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1388 if(lStyle & TCS_VERTICAL)
1391 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1393 rcItem = &(infoPtr->items[iIndex].rect);
1395 rcOriginal = *rcItem;
1397 /* this is rotating the items by 90 degrees around the center of the control */
1398 rcItem->top = (clientRect.right - (rcOriginal.left - clientRect.left)) - (rcOriginal.right - rcOriginal.left);
1399 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1400 rcItem->left = rcOriginal.top;
1401 rcItem->right = rcOriginal.bottom;
1405 TAB_EnsureSelectionVisible(hwnd,infoPtr);
1406 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1409 SelectObject (hdc, hOldFont);
1410 ReleaseDC (hwnd, hdc);
1413 /******************************************************************************
1414 * TAB_DrawItemInterior
1416 * This method is used to draw the interior (text and icon) of a single tab
1417 * into the tab control.
1420 TAB_DrawItemInterior
1428 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1429 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1437 if (drawRect == NULL)
1444 * Get the rectangle for the item.
1446 isVisible = TAB_InternalGetItemRect(hwnd, infoPtr, iItem, &itemRect, &selectedRect);
1451 * Make sure drawRect points to something valid; simplifies code.
1453 drawRect = &localRect;
1456 * This logic copied from the part of TAB_DrawItem which draws
1457 * the tab background. It's important to keep it in sync. I
1458 * would have liked to avoid code duplication, but couldn't figure
1459 * out how without making spaghetti of TAB_DrawItem.
1461 if (lStyle & TCS_BUTTONS)
1463 *drawRect = itemRect;
1464 if (iItem == infoPtr->iSelected)
1472 if (iItem == infoPtr->iSelected)
1473 *drawRect = selectedRect;
1475 *drawRect = itemRect;
1484 htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1485 holdPen = SelectObject(hdc, htextPen);
1487 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1488 SetTextColor(hdc, (iItem == infoPtr->iHotTracked) ?
1489 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1493 * if owner draw, tell the owner to draw
1495 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd))
1501 * get the control id
1503 id = GetWindowLongA( hwnd, GWL_ID );
1506 * put together the DRAWITEMSTRUCT
1508 dis.CtlType = ODT_TAB;
1511 dis.itemAction = ODA_DRAWENTIRE;
1513 if ( iItem == infoPtr->iSelected )
1514 dis.itemState |= ODS_SELECTED;
1515 if (infoPtr->uFocus == iItem)
1516 dis.itemState |= ODS_FOCUS;
1517 dis.hwndItem = hwnd; /* */
1519 CopyRect(&dis.rcItem,drawRect);
1520 dis.itemData = infoPtr->items[iItem].lParam;
1523 * send the draw message
1525 SendMessageA( GetParent(hwnd), WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1536 HFONT hOldFont = 0; /* stop uninitialized warning */
1538 INT nEscapement = 0; /* stop uninitialized warning */
1539 INT nOrientation = 0; /* stop uninitialized warning */
1542 /* used to center the icon and text in the tab */
1547 * Deflate the rectangle to acount for the padding
1549 if(lStyle & TCS_VERTICAL)
1550 InflateRect(drawRect, -infoPtr->uVItemPadding, -infoPtr->uHItemPadding);
1552 InflateRect(drawRect, -infoPtr->uHItemPadding, -infoPtr->uVItemPadding);
1554 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1555 rcImage = *drawRect;
1559 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1562 * Setup for text output
1564 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1565 SetTextColor(hdc, (iItem == infoPtr->iHotTracked) ?
1566 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1568 /* get the rectangle that the text fits in */
1569 DrawTextW(hdc, infoPtr->items[iItem].pszText, -1,
1570 &rcText, DT_CALCRECT);
1573 * If not owner draw, then do the drawing ourselves.
1577 if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE))
1579 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1581 if(lStyle & TCS_VERTICAL)
1582 center_offset = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1584 center_offset = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1586 TRACE("for <%s>, c_o=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1587 debugstr_w(infoPtr->items[iItem].pszText), center_offset,
1588 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1589 (rcText.right-rcText.left));
1591 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1593 rcImage.top = drawRect->top + center_offset;
1594 rcImage.left = drawRect->right - cx; /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1595 /* right side of the tab, but the image still uses the left as its x position */
1596 /* this keeps the image always drawn off of the same side of the tab */
1597 drawRect->top = rcImage.top + (cx + infoPtr->uHItemPadding);
1599 else if(lStyle & TCS_VERTICAL)
1601 rcImage.top = drawRect->bottom - cy - center_offset;
1603 drawRect->bottom = rcImage.top - infoPtr->uHItemPadding;
1605 else /* normal style, whether TCS_BOTTOM or not */
1607 rcImage.left = drawRect->left + center_offset + 3;
1608 drawRect->left = rcImage.left + cx + infoPtr->uHItemPadding;
1609 rcImage.top -= (lStyle & TCS_BOTTOM) ? 2 : 1;
1612 TRACE("drawing image=%d, left=%ld, top=%ld\n",
1613 infoPtr->items[iItem].iImage, rcImage.left, rcImage.top-1);
1617 infoPtr->items[iItem].iImage,
1623 } else /* no image, so just shift the drawRect borders around */
1625 if(lStyle & TCS_VERTICAL)
1629 currently the rcText rect is flawed because the rotated font does not
1630 often match the horizontal font. So leave this as 0
1631 ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1633 if(lStyle & TCS_BOTTOM)
1634 drawRect->top+=center_offset;
1636 drawRect->bottom-=center_offset;
1640 center_offset = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1641 drawRect->left+=center_offset;
1646 if (lStyle & TCS_RIGHTJUSTIFY)
1647 uHorizAlign = DT_CENTER;
1649 uHorizAlign = DT_LEFT;
1651 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1653 if(lStyle & TCS_BOTTOM)
1656 nOrientation = -900;
1665 /* to get a font with the escapement and orientation we are looking for, we need to */
1666 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1667 if(lStyle & TCS_VERTICAL)
1669 if (!GetObjectA((infoPtr->hFont) ?
1670 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1671 sizeof(LOGFONTA),&logfont))
1675 lstrcpyA(logfont.lfFaceName, "Arial");
1676 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1678 logfont.lfWeight = FW_NORMAL;
1679 logfont.lfItalic = 0;
1680 logfont.lfUnderline = 0;
1681 logfont.lfStrikeOut = 0;
1684 logfont.lfEscapement = nEscapement;
1685 logfont.lfOrientation = nOrientation;
1686 hFont = CreateFontIndirectA(&logfont);
1687 hOldFont = SelectObject(hdc, hFont);
1690 if (lStyle & TCS_VERTICAL)
1693 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1694 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1697 infoPtr->items[iItem].pszText,
1698 lstrlenW(infoPtr->items[iItem].pszText),
1706 infoPtr->items[iItem].pszText,
1707 lstrlenW(infoPtr->items[iItem].pszText),
1709 uHorizAlign | DT_SINGLELINE
1713 /* clean things up */
1714 *drawRect = rcTemp; /* restore drawRect */
1716 if(lStyle & TCS_VERTICAL)
1718 SelectObject(hdc, hOldFont); /* restore the original font */
1720 DeleteObject(hFont);
1727 SetBkMode(hdc, oldBkMode);
1728 SelectObject(hdc, holdPen);
1729 DeleteObject( htextPen );
1732 /******************************************************************************
1735 * This method is used to draw a single tab into the tab control.
1737 static void TAB_DrawItem(
1742 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1743 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1747 RECT r, fillRect, r1;
1750 COLORREF bkgnd, corner;
1753 * Get the rectangle for the item.
1755 isVisible = TAB_InternalGetItemRect(hwnd,
1763 /* If you need to see what the control is doing,
1764 * then override these variables. They will change what
1765 * fill colors are used for filling the tabs, and the
1766 * corners when drawing the edge.
1768 bkgnd = comctl32_color.clrBtnFace;
1769 corner = comctl32_color.clrBtnFace;
1771 if (lStyle & TCS_BUTTONS)
1773 HBRUSH hbr = CreateSolidBrush (bkgnd);
1774 BOOL deleteBrush = TRUE;
1776 /* Get item rectangle */
1779 /* Separators between flat buttons */
1780 if (lStyle & TCS_FLATBUTTONS)
1783 r1.right += (FLAT_BTN_SPACINGX -2);
1784 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1787 if (iItem == infoPtr->iSelected)
1789 /* Background color */
1790 if (!((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
1793 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1795 SetTextColor(hdc, comctl32_color.clr3dFace);
1796 SetBkColor(hdc, comctl32_color.clr3dHilight);
1798 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1799 * we better use 0x55aa bitmap brush to make scrollbar's background
1800 * look different from the window background.
1802 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1803 hbr = COMCTL32_hPattern55AABrush;
1805 deleteBrush = FALSE;
1808 /* Clear interior */
1809 FillRect(hdc, &r, hbr);
1811 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1813 else /* ! selected */
1815 if (!(lStyle & TCS_FLATBUTTONS))
1817 /* Clear interior */
1818 FillRect(hdc, &r, hbr);
1820 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1825 if (deleteBrush) DeleteObject(hbr);
1827 else /* !TCS_BUTTONS */
1829 /* We draw a rectangle of different sizes depending on the selection
1831 if (iItem == infoPtr->iSelected) {
1833 GetClientRect (hwnd, &rect);
1834 clRight = rect.right;
1835 clBottom = rect.bottom;
1842 * Erase the background. (Delay it but setup rectangle.)
1843 * This is necessary when drawing the selected item since it is larger
1844 * than the others, it might overlap with stuff already drawn by the
1849 if(lStyle & TCS_VERTICAL)
1851 /* These are for adjusting the drawing of a Selected tab */
1852 /* The initial values are for the normal case of non-Selected */
1853 int ZZ = 1; /* Do not strech if selected */
1854 if (iItem == infoPtr->iSelected) {
1857 /* if leftmost draw the line longer */
1858 if(selectedRect.top == 0)
1860 /* if rightmost draw the line longer */
1861 if(selectedRect.bottom == clBottom)
1862 fillRect.bottom -= 2;
1865 if (lStyle & TCS_BOTTOM)
1867 /* Adjust both rectangles to match native */
1870 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1872 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1873 r.left,r.top,r.right,r.bottom);
1875 /* Clear interior */
1876 SetBkColor(hdc, bkgnd);
1877 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1879 /* Draw rectangular edge around tab */
1880 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
1882 /* Now erase the top corner and draw diagonal edge */
1883 SetBkColor(hdc, corner);
1884 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1887 r1.bottom = r1.top + ROUND_CORNER_SIZE;
1888 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1890 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
1892 /* Now erase the bottom corner and draw diagonal edge */
1893 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1894 r1.bottom = r.bottom;
1896 r1.top = r1.bottom - ROUND_CORNER_SIZE;
1897 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1899 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
1901 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
1905 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
1911 /* Adjust both rectangles to match native */
1912 fillRect.right += (1-ZZ);
1914 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1916 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1917 r.left,r.top,r.right,r.bottom);
1919 /* Clear interior */
1920 SetBkColor(hdc, bkgnd);
1921 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1923 /* Draw rectangular edge around tab */
1924 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
1926 /* Now erase the top corner and draw diagonal edge */
1927 SetBkColor(hdc, corner);
1930 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
1931 r1.bottom = r1.top + ROUND_CORNER_SIZE;
1932 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1934 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
1936 /* Now erase the bottom corner and draw diagonal edge */
1938 r1.bottom = r.bottom;
1939 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
1940 r1.top = r1.bottom - ROUND_CORNER_SIZE;
1941 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1943 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
1946 else /* ! TCS_VERTICAL */
1948 /* These are for adjusting the drawing of a Selected tab */
1949 /* The initial values are for the normal case of non-Selected */
1950 int ZZ = 1; /* Do not strech if selected */
1951 if (iItem == infoPtr->iSelected) {
1954 /* if leftmost draw the line longer */
1955 if(selectedRect.left == 0)
1957 /* if rightmost draw the line longer */
1958 if(selectedRect.right == clRight)
1959 fillRect.right -= 2;
1962 if (lStyle & TCS_BOTTOM)
1965 /* Adjust both rectangles to match native */
1971 TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1973 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1974 r.left,r.top,r.right,r.bottom);
1976 /* Clear interior */
1977 SetBkColor(hdc, bkgnd);
1978 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1980 /* Draw rectangular edge around tab */
1981 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
1983 /* Now erase the righthand corner and draw diagonal edge */
1984 SetBkColor(hdc, corner);
1985 r1.left = r.right - ROUND_CORNER_SIZE;
1986 r1.bottom = r.bottom;
1988 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
1989 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1991 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
1993 /* Now erase the lefthand corner and draw diagonal edge */
1995 r1.bottom = r.bottom;
1996 r1.right = r1.left + ROUND_CORNER_SIZE;
1997 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
1998 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2000 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2002 if ((iItem == infoPtr->iSelected) && (selectedRect.left == 0)) {
2006 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2013 /* Adjust both rectangles to match native */
2014 fillRect.bottom += (1-ZZ);
2016 TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2018 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2019 r.left,r.top,r.right,r.bottom);
2021 /* Clear interior */
2022 SetBkColor(hdc, bkgnd);
2023 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2025 /* Draw rectangular edge around tab */
2026 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2028 /* Now erase the righthand corner and draw diagonal edge */
2029 SetBkColor(hdc, corner);
2030 r1.left = r.right - ROUND_CORNER_SIZE;
2033 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2034 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2036 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2038 /* Now erase the lefthand corner and draw diagonal edge */
2041 r1.right = r1.left + ROUND_CORNER_SIZE;
2042 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2043 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2045 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2051 TAB_DumpItemInternal(infoPtr, iItem);
2053 /* This modifies r to be the text rectangle. */
2055 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
2056 TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
2057 SelectObject(hdc,hOldFont);
2060 /* Draw the focus rectangle */
2061 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
2062 (GetFocus() == hwnd) &&
2063 (iItem == infoPtr->uFocus) )
2066 InflateRect(&r, -1, -1);
2068 DrawFocusRect(hdc, &r);
2073 /******************************************************************************
2076 * This method is used to draw the raised border around the tab control
2079 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
2081 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2083 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2085 GetClientRect (hwnd, &rect);
2088 * Adjust for the style
2091 if (infoPtr->uNumItem)
2093 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2095 rect.bottom -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 3;
2097 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2099 rect.right -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2101 else if(lStyle & TCS_VERTICAL)
2103 rect.left += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2105 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2107 rect.top += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2111 TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2112 rect.left, rect.top, rect.right, rect.bottom);
2114 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2117 /******************************************************************************
2120 * This method repaints the tab control..
2122 static void TAB_Refresh (HWND hwnd, HDC hdc)
2124 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2128 if (!infoPtr->DoRedraw)
2131 hOldFont = SelectObject (hdc, infoPtr->hFont);
2133 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
2135 for (i = 0; i < infoPtr->uNumItem; i++)
2136 TAB_DrawItem (hwnd, hdc, i);
2140 /* Draw all the non selected item first */
2141 for (i = 0; i < infoPtr->uNumItem; i++)
2143 if (i != infoPtr->iSelected)
2144 TAB_DrawItem (hwnd, hdc, i);
2147 /* Now, draw the border, draw it before the selected item
2148 * since the selected item overwrites part of the border. */
2149 TAB_DrawBorder (hwnd, hdc);
2151 /* Then, draw the selected item */
2152 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
2154 /* If we haven't set the current focus yet, set it now.
2155 * Only happens when we first paint the tab controls */
2156 if (infoPtr->uFocus == -1)
2157 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
2160 SelectObject (hdc, hOldFont);
2164 TAB_GetRowCount (HWND hwnd )
2166 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2168 return infoPtr->uNumRows;
2172 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
2174 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2176 infoPtr->DoRedraw=(BOOL) wParam;
2180 static LRESULT TAB_EraseBackground(
2187 HBRUSH brush = CreateSolidBrush(comctl32_color.clrBtnFace);
2189 hdc = givenDC ? givenDC : GetDC(hwnd);
2191 GetClientRect(hwnd, &clientRect);
2193 FillRect(hdc, &clientRect, brush);
2196 ReleaseDC(hwnd, hdc);
2198 DeleteObject(brush);
2203 /******************************************************************************
2204 * TAB_EnsureSelectionVisible
2206 * This method will make sure that the current selection is completely
2207 * visible by scrolling until it is.
2209 static void TAB_EnsureSelectionVisible(
2213 INT iSelected = infoPtr->iSelected;
2214 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2215 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2217 /* set the items row to the bottommost row or topmost row depending on
2219 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2224 if(lStyle & TCS_VERTICAL)
2225 newselected = infoPtr->items[iSelected].rect.left;
2227 newselected = infoPtr->items[iSelected].rect.top;
2229 /* the target row is always (number of rows - 1)
2230 as row 0 is furthest from the clientRect */
2231 iTargetRow = infoPtr->uNumRows - 1;
2233 if (newselected != iTargetRow)
2236 if(lStyle & TCS_VERTICAL)
2238 for (i=0; i < infoPtr->uNumItem; i++)
2240 /* move everything in the row of the selected item to the iTargetRow */
2241 if (infoPtr->items[i].rect.left == newselected )
2242 infoPtr->items[i].rect.left = iTargetRow;
2245 if (infoPtr->items[i].rect.left > newselected)
2246 infoPtr->items[i].rect.left-=1;
2252 for (i=0; i < infoPtr->uNumItem; i++)
2254 if (infoPtr->items[i].rect.top == newselected )
2255 infoPtr->items[i].rect.top = iTargetRow;
2258 if (infoPtr->items[i].rect.top > newselected)
2259 infoPtr->items[i].rect.top-=1;
2263 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2268 * Do the trivial cases first.
2270 if ( (!infoPtr->needsScrolling) ||
2271 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2274 if (infoPtr->leftmostVisible >= iSelected)
2276 infoPtr->leftmostVisible = iSelected;
2283 /* Calculate the part of the client area that is visible */
2284 GetClientRect(hwnd, &r);
2287 GetClientRect(infoPtr->hwndUpDown, &r);
2290 if ((infoPtr->items[iSelected].rect.right -
2291 infoPtr->items[iSelected].rect.left) >= width )
2293 /* Special case: width of selected item is greater than visible
2296 infoPtr->leftmostVisible = iSelected;
2300 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2302 if ((infoPtr->items[iSelected].rect.right -
2303 infoPtr->items[i].rect.left) < width)
2306 infoPtr->leftmostVisible = i;
2310 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2311 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2313 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2314 MAKELONG(infoPtr->leftmostVisible, 0));
2317 /******************************************************************************
2318 * TAB_InvalidateTabArea
2320 * This method will invalidate the portion of the control that contains the
2321 * tabs. It is called when the state of the control changes and needs
2324 static void TAB_InvalidateTabArea(
2329 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2330 INT lastRow = infoPtr->uNumRows - 1;
2332 if (lastRow < 0) return;
2334 GetClientRect(hwnd, &clientRect);
2336 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2338 clientRect.top = clientRect.bottom -
2339 infoPtr->tabHeight -
2340 lastRow * (infoPtr->tabHeight - 2) -
2341 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 3;
2343 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2345 clientRect.left = clientRect.right - infoPtr->tabHeight -
2346 lastRow * (infoPtr->tabHeight - 2) -
2347 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 2;
2349 else if(lStyle & TCS_VERTICAL)
2351 clientRect.right = clientRect.left + infoPtr->tabHeight +
2352 lastRow * (infoPtr->tabHeight - 2) -
2353 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 2;
2358 clientRect.bottom = clientRect.top + infoPtr->tabHeight +
2359 lastRow * (infoPtr->tabHeight - 2) +
2360 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 2;
2363 /* Punch out the updown control */
2364 if (infoPtr->needsScrolling && (clientRect.right > 0)) {
2365 GetClientRect(infoPtr->hwndUpDown, &r);
2366 clientRect.right = clientRect.right - (r.right - r.left);
2369 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2370 clientRect.left,clientRect.top,
2371 clientRect.right,clientRect.bottom);
2373 InvalidateRect(hwnd, &clientRect, TRUE);
2377 TAB_Paint (HWND hwnd, WPARAM wParam)
2382 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2384 TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2386 ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2389 TAB_EraseBackground (hwnd, hdc);
2391 TAB_Refresh (hwnd, hdc);
2394 EndPaint (hwnd, &ps);
2400 TAB_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2402 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2407 GetClientRect (hwnd, &rect);
2408 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2409 rect.top, rect.left, rect.bottom, rect.right);
2411 pti = (TCITEMA *)lParam;
2412 iItem = (INT)wParam;
2414 if (iItem < 0) return -1;
2415 if (iItem > infoPtr->uNumItem)
2416 iItem = infoPtr->uNumItem;
2418 TAB_DumpItemExternalA(pti, iItem);
2421 if (infoPtr->uNumItem == 0) {
2422 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
2423 infoPtr->uNumItem++;
2424 infoPtr->iSelected = 0;
2427 TAB_ITEM *oldItems = infoPtr->items;
2429 infoPtr->uNumItem++;
2430 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2432 /* pre insert copy */
2434 memcpy (&infoPtr->items[0], &oldItems[0],
2435 iItem * sizeof(TAB_ITEM));
2438 /* post insert copy */
2439 if (iItem < infoPtr->uNumItem - 1) {
2440 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2441 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2445 if (iItem <= infoPtr->iSelected)
2446 infoPtr->iSelected++;
2448 COMCTL32_Free (oldItems);
2451 infoPtr->items[iItem].mask = pti->mask;
2452 if (pti->mask & TCIF_TEXT)
2453 Str_SetPtrAtoW (&infoPtr->items[iItem].pszText, pti->pszText);
2455 if (pti->mask & TCIF_IMAGE)
2456 infoPtr->items[iItem].iImage = pti->iImage;
2458 if (pti->mask & TCIF_PARAM)
2459 infoPtr->items[iItem].lParam = pti->lParam;
2461 TAB_SetItemBounds(hwnd);
2462 if (infoPtr->uNumItem > 1)
2463 TAB_InvalidateTabArea(hwnd, infoPtr);
2465 InvalidateRect(hwnd, NULL, TRUE);
2467 TRACE("[%p]: added item %d %s\n",
2468 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2475 TAB_InsertItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2477 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2482 GetClientRect (hwnd, &rect);
2483 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2484 rect.top, rect.left, rect.bottom, rect.right);
2486 pti = (TCITEMW *)lParam;
2487 iItem = (INT)wParam;
2489 if (iItem < 0) return -1;
2490 if (iItem > infoPtr->uNumItem)
2491 iItem = infoPtr->uNumItem;
2493 TAB_DumpItemExternalW(pti, iItem);
2495 if (infoPtr->uNumItem == 0) {
2496 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
2497 infoPtr->uNumItem++;
2498 infoPtr->iSelected = 0;
2501 TAB_ITEM *oldItems = infoPtr->items;
2503 infoPtr->uNumItem++;
2504 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2506 /* pre insert copy */
2508 memcpy (&infoPtr->items[0], &oldItems[0],
2509 iItem * sizeof(TAB_ITEM));
2512 /* post insert copy */
2513 if (iItem < infoPtr->uNumItem - 1) {
2514 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2515 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2519 if (iItem <= infoPtr->iSelected)
2520 infoPtr->iSelected++;
2522 COMCTL32_Free (oldItems);
2525 infoPtr->items[iItem].mask = pti->mask;
2526 if (pti->mask & TCIF_TEXT)
2527 Str_SetPtrW (&infoPtr->items[iItem].pszText, pti->pszText);
2529 if (pti->mask & TCIF_IMAGE)
2530 infoPtr->items[iItem].iImage = pti->iImage;
2532 if (pti->mask & TCIF_PARAM)
2533 infoPtr->items[iItem].lParam = pti->lParam;
2535 TAB_SetItemBounds(hwnd);
2536 if (infoPtr->uNumItem > 1)
2537 TAB_InvalidateTabArea(hwnd, infoPtr);
2539 InvalidateRect(hwnd, NULL, TRUE);
2541 TRACE("[%p]: added item %d %s\n",
2542 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2549 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2551 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2552 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2556 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
2558 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2559 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use default. */
2560 if (LOWORD(lParam)) infoPtr->tabWidth = (INT)LOWORD(lParam);
2561 if (HIWORD(lParam)) infoPtr->tabHeight = (INT)HIWORD(lParam);
2562 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2563 HIWORD(lResult), LOWORD(lResult),
2564 infoPtr->tabHeight, infoPtr->tabWidth);
2566 infoPtr->fSizeSet = TRUE;
2572 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2574 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2579 iItem = (INT)wParam;
2580 tabItem = (LPTCITEMA)lParam;
2582 TRACE("%d %p\n", iItem, tabItem);
2583 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2585 TAB_DumpItemExternalA(tabItem, iItem);
2587 wineItem = &infoPtr->items[iItem];
2589 if (tabItem->mask & TCIF_IMAGE)
2590 wineItem->iImage = tabItem->iImage;
2592 if (tabItem->mask & TCIF_PARAM)
2593 wineItem->lParam = tabItem->lParam;
2595 if (tabItem->mask & TCIF_RTLREADING)
2596 FIXME("TCIF_RTLREADING\n");
2598 if (tabItem->mask & TCIF_STATE)
2599 wineItem->dwState = tabItem->dwState;
2601 if (tabItem->mask & TCIF_TEXT)
2602 Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2604 /* Update and repaint tabs */
2605 TAB_SetItemBounds(hwnd);
2606 TAB_InvalidateTabArea(hwnd,infoPtr);
2613 TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2615 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2620 iItem = (INT)wParam;
2621 tabItem = (LPTCITEMW)lParam;
2623 TRACE("%d %p\n", iItem, tabItem);
2624 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2626 TAB_DumpItemExternalW(tabItem, iItem);
2628 wineItem = &infoPtr->items[iItem];
2630 if (tabItem->mask & TCIF_IMAGE)
2631 wineItem->iImage = tabItem->iImage;
2633 if (tabItem->mask & TCIF_PARAM)
2634 wineItem->lParam = tabItem->lParam;
2636 if (tabItem->mask & TCIF_RTLREADING)
2637 FIXME("TCIF_RTLREADING\n");
2639 if (tabItem->mask & TCIF_STATE)
2640 wineItem->dwState = tabItem->dwState;
2642 if (tabItem->mask & TCIF_TEXT)
2643 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2645 /* Update and repaint tabs */
2646 TAB_SetItemBounds(hwnd);
2647 TAB_InvalidateTabArea(hwnd,infoPtr);
2654 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2656 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2658 return infoPtr->uNumItem;
2663 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2665 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2670 iItem = (INT)wParam;
2671 tabItem = (LPTCITEMA)lParam;
2673 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2676 wineItem = &infoPtr->items[iItem];
2678 if (tabItem->mask & TCIF_IMAGE)
2679 tabItem->iImage = wineItem->iImage;
2681 if (tabItem->mask & TCIF_PARAM)
2682 tabItem->lParam = wineItem->lParam;
2684 if (tabItem->mask & TCIF_RTLREADING)
2685 FIXME("TCIF_RTLREADING\n");
2687 if (tabItem->mask & TCIF_STATE)
2688 tabItem->dwState = wineItem->dwState;
2690 if (tabItem->mask & TCIF_TEXT)
2691 Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2693 TAB_DumpItemExternalA(tabItem, iItem);
2700 TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2702 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2707 iItem = (INT)wParam;
2708 tabItem = (LPTCITEMW)lParam;
2710 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2713 wineItem=& infoPtr->items[iItem];
2715 if (tabItem->mask & TCIF_IMAGE)
2716 tabItem->iImage = wineItem->iImage;
2718 if (tabItem->mask & TCIF_PARAM)
2719 tabItem->lParam = wineItem->lParam;
2721 if (tabItem->mask & TCIF_RTLREADING)
2722 FIXME("TCIF_RTLREADING\n");
2724 if (tabItem->mask & TCIF_STATE)
2725 tabItem->dwState = wineItem->dwState;
2727 if (tabItem->mask & TCIF_TEXT)
2728 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2730 TAB_DumpItemExternalW(tabItem, iItem);
2737 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2739 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2740 INT iItem = (INT) wParam;
2741 BOOL bResult = FALSE;
2743 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2745 TAB_ITEM *oldItems = infoPtr->items;
2747 infoPtr->uNumItem--;
2748 infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
2751 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
2753 if (iItem < infoPtr->uNumItem)
2754 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
2755 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
2757 COMCTL32_Free(oldItems);
2759 /* Readjust the selected index */
2760 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2761 infoPtr->iSelected--;
2763 if (iItem < infoPtr->iSelected)
2764 infoPtr->iSelected--;
2766 if (infoPtr->uNumItem == 0)
2767 infoPtr->iSelected = -1;
2769 /* Reposition and repaint tabs */
2770 TAB_SetItemBounds(hwnd);
2771 TAB_InvalidateTabArea(hwnd,infoPtr);
2780 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2782 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2784 COMCTL32_Free (infoPtr->items);
2785 infoPtr->uNumItem = 0;
2786 infoPtr->iSelected = -1;
2787 if (infoPtr->iHotTracked >= 0)
2788 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2789 infoPtr->iHotTracked = -1;
2791 TAB_SetItemBounds(hwnd);
2792 TAB_InvalidateTabArea(hwnd,infoPtr);
2798 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2800 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2803 return (LRESULT)infoPtr->hFont;
2807 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2810 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2812 TRACE("%x %lx\n",wParam, lParam);
2814 infoPtr->hFont = (HFONT)wParam;
2816 TAB_SetItemBounds(hwnd);
2818 TAB_InvalidateTabArea(hwnd, infoPtr);
2825 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2827 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2830 return (LRESULT)infoPtr->himl;
2834 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2836 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2837 HIMAGELIST himlPrev;
2840 himlPrev = infoPtr->himl;
2841 infoPtr->himl= (HIMAGELIST)lParam;
2842 return (LRESULT)himlPrev;
2846 TAB_GetUnicodeFormat (HWND hwnd)
2848 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2849 return infoPtr->bUnicode;
2853 TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
2855 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2856 BOOL bTemp = infoPtr->bUnicode;
2858 infoPtr->bUnicode = (BOOL)wParam;
2864 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2867 /* I'm not really sure what the following code was meant to do.
2868 This is what it is doing:
2869 When WM_SIZE is sent with SIZE_RESTORED, the control
2870 gets positioned in the top left corner.
2874 UINT uPosFlags,cx,cy;
2878 parent = GetParent (hwnd);
2879 GetClientRect(parent, &parent_rect);
2882 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2883 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2885 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2886 cx, cy, uPosFlags | SWP_NOZORDER);
2888 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2891 /* Recompute the size/position of the tabs. */
2892 TAB_SetItemBounds (hwnd);
2894 /* Force a repaint of the control. */
2895 InvalidateRect(hwnd, NULL, TRUE);
2902 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2905 TEXTMETRICA fontMetrics;
2910 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
2912 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
2914 infoPtr->uNumItem = 0;
2915 infoPtr->uNumRows = 0;
2916 infoPtr->uHItemPadding = 6;
2917 infoPtr->uVItemPadding = 3;
2920 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
2921 infoPtr->iSelected = -1;
2922 infoPtr->iHotTracked = -1;
2923 infoPtr->uFocus = -1;
2924 infoPtr->hwndToolTip = 0;
2925 infoPtr->DoRedraw = TRUE;
2926 infoPtr->needsScrolling = FALSE;
2927 infoPtr->hwndUpDown = 0;
2928 infoPtr->leftmostVisible = 0;
2929 infoPtr->fSizeSet = FALSE;
2930 infoPtr->bUnicode = IsWindowUnicode (hwnd);
2932 TRACE("Created tab control, hwnd [%p]\n", hwnd);
2934 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2935 if you don't specify it in CreateWindow. This is necessary in
2936 order for paint to work correctly. This follows windows behaviour. */
2937 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
2938 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2940 if (dwStyle & TCS_TOOLTIPS) {
2941 /* Create tooltip control */
2942 infoPtr->hwndToolTip =
2943 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2944 CW_USEDEFAULT, CW_USEDEFAULT,
2945 CW_USEDEFAULT, CW_USEDEFAULT,
2948 /* Send NM_TOOLTIPSCREATED notification */
2949 if (infoPtr->hwndToolTip) {
2950 NMTOOLTIPSCREATED nmttc;
2952 nmttc.hdr.hwndFrom = hwnd;
2953 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
2954 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2955 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2957 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2958 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
2963 * We need to get text information so we need a DC and we need to select
2967 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
2969 /* Use the system font to determine the initial height of a tab. */
2970 GetTextMetricsA(hdc, &fontMetrics);
2973 * Make sure there is enough space for the letters + growing the
2974 * selected item + extra space for the selected item.
2976 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
2977 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
2978 infoPtr->uVItemPadding;
2980 /* Initialize the width of a tab. */
2981 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
2983 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
2985 SelectObject (hdc, hOldFont);
2986 ReleaseDC(hwnd, hdc);
2992 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
2994 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3000 if (infoPtr->items) {
3001 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3002 if (infoPtr->items[iItem].pszText)
3003 COMCTL32_Free (infoPtr->items[iItem].pszText);
3005 COMCTL32_Free (infoPtr->items);
3008 if (infoPtr->hwndToolTip)
3009 DestroyWindow (infoPtr->hwndToolTip);
3011 if (infoPtr->hwndUpDown)
3012 DestroyWindow(infoPtr->hwndUpDown);
3014 if (infoPtr->iHotTracked >= 0)
3015 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
3017 COMCTL32_Free (infoPtr);
3018 SetWindowLongA(hwnd, 0, 0);
3022 static LRESULT WINAPI
3023 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3026 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3027 if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
3028 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3032 case TCM_GETIMAGELIST:
3033 return TAB_GetImageList (hwnd, wParam, lParam);
3035 case TCM_SETIMAGELIST:
3036 return TAB_SetImageList (hwnd, wParam, lParam);
3038 case TCM_GETITEMCOUNT:
3039 return TAB_GetItemCount (hwnd, wParam, lParam);
3042 return TAB_GetItemA (hwnd, wParam, lParam);
3045 return TAB_GetItemW (hwnd, wParam, lParam);
3048 return TAB_SetItemA (hwnd, wParam, lParam);
3051 return TAB_SetItemW (hwnd, wParam, lParam);
3053 case TCM_DELETEITEM:
3054 return TAB_DeleteItem (hwnd, wParam, lParam);
3056 case TCM_DELETEALLITEMS:
3057 return TAB_DeleteAllItems (hwnd, wParam, lParam);
3059 case TCM_GETITEMRECT:
3060 return TAB_GetItemRect (hwnd, wParam, lParam);
3063 return TAB_GetCurSel (hwnd);
3066 return TAB_HitTest (hwnd, wParam, lParam);
3069 return TAB_SetCurSel (hwnd, wParam);
3071 case TCM_INSERTITEMA:
3072 return TAB_InsertItemA (hwnd, wParam, lParam);
3074 case TCM_INSERTITEMW:
3075 return TAB_InsertItemW (hwnd, wParam, lParam);
3077 case TCM_SETITEMEXTRA:
3078 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
3081 case TCM_ADJUSTRECT:
3082 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
3084 case TCM_SETITEMSIZE:
3085 return TAB_SetItemSize (hwnd, wParam, lParam);
3087 case TCM_REMOVEIMAGE:
3088 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3091 case TCM_SETPADDING:
3092 return TAB_SetPadding (hwnd, wParam, lParam);
3094 case TCM_GETROWCOUNT:
3095 return TAB_GetRowCount(hwnd);
3097 case TCM_GETUNICODEFORMAT:
3098 return TAB_GetUnicodeFormat (hwnd);
3100 case TCM_SETUNICODEFORMAT:
3101 return TAB_SetUnicodeFormat (hwnd, wParam);
3103 case TCM_HIGHLIGHTITEM:
3104 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
3107 case TCM_GETTOOLTIPS:
3108 return TAB_GetToolTips (hwnd, wParam, lParam);
3110 case TCM_SETTOOLTIPS:
3111 return TAB_SetToolTips (hwnd, wParam, lParam);
3113 case TCM_GETCURFOCUS:
3114 return TAB_GetCurFocus (hwnd);
3116 case TCM_SETCURFOCUS:
3117 return TAB_SetCurFocus (hwnd, wParam);
3119 case TCM_SETMINTABWIDTH:
3120 FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
3123 case TCM_DESELECTALL:
3124 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3127 case TCM_GETEXTENDEDSTYLE:
3128 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3131 case TCM_SETEXTENDEDSTYLE:
3132 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3136 return TAB_GetFont (hwnd, wParam, lParam);
3139 return TAB_SetFont (hwnd, wParam, lParam);
3142 return TAB_Create (hwnd, wParam, lParam);
3145 return TAB_Destroy (hwnd, wParam, lParam);
3148 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3150 case WM_LBUTTONDOWN:
3151 return TAB_LButtonDown (hwnd, wParam, lParam);
3154 return TAB_LButtonUp (hwnd, wParam, lParam);
3157 return SendMessageA(GetParent(hwnd), WM_NOTIFY, wParam, lParam);
3159 case WM_RBUTTONDOWN:
3160 return TAB_RButtonDown (hwnd, wParam, lParam);
3163 return TAB_MouseMove (hwnd, wParam, lParam);
3166 return TAB_EraseBackground (hwnd, (HDC)wParam);
3169 return TAB_Paint (hwnd, wParam);
3172 return TAB_Size (hwnd, wParam, lParam);
3175 return TAB_SetRedraw (hwnd, wParam);
3178 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3180 case WM_STYLECHANGED:
3181 TAB_SetItemBounds (hwnd);
3182 InvalidateRect(hwnd, NULL, TRUE);
3185 case WM_SYSCOLORCHANGE:
3186 COMCTL32_RefreshSysColors();
3191 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
3194 return TAB_KeyUp(hwnd, wParam);
3196 return TAB_NCHitTest(hwnd, lParam);
3199 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
3200 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3201 uMsg, wParam, lParam);
3202 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
3214 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3215 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3216 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
3217 wndClass.cbClsExtra = 0;
3218 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3219 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
3220 wndClass.hbrBackground = NULL;
3221 wndClass.lpszClassName = WC_TABCONTROLA;
3223 RegisterClassA (&wndClass);
3228 TAB_Unregister (void)
3230 UnregisterClassA (WC_TABCONTROLA, NULL);