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
40 #include "wine/debug.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(tab);
52 RECT rect; /* bounding rectangle of the item relative to the
53 * leftmost item (the leftmost item, 0, would have a
54 * "left" member of 0 in this rectangle)
56 * additionally the top member hold the row number
57 * and bottom is unused and should be 0 */
62 UINT uNumItem; /* number of tab items */
63 UINT uNumRows; /* number of tab rows */
64 INT tabHeight; /* height of the tab row */
65 INT tabWidth; /* width of tabs */
66 INT tabMinWidth; /* minimum width of items */
67 USHORT uHItemPadding; /* amount of horizontal padding, in pixels */
68 USHORT uVItemPadding; /* amount of vertical padding, in pixels */
69 HFONT hFont; /* handle to the current font */
70 HCURSOR hcurArrow; /* handle to the current cursor */
71 HIMAGELIST himl; /* handle to a image list (may be 0) */
72 HWND hwndToolTip; /* handle to tab's tooltip */
73 INT leftmostVisible; /* Used for scrolling, this member contains
74 * the index of the first visible item */
75 INT iSelected; /* the currently selected item */
76 INT iHotTracked; /* the highlighted item under the mouse */
77 INT uFocus; /* item which has the focus */
78 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
79 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
80 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
81 * the size of the control */
82 BOOL fHeightSet; /* was the height of the tabs explicitly set? */
83 BOOL bUnicode; /* Unicode control? */
84 HWND hwndUpDown; /* Updown control used for scrolling */
87 /******************************************************************************
88 * Positioning constants
90 #define SELECTED_TAB_OFFSET 2
91 #define ROUND_CORNER_SIZE 2
92 #define DISPLAY_AREA_PADDINGX 2
93 #define DISPLAY_AREA_PADDINGY 2
94 #define CONTROL_BORDER_SIZEX 2
95 #define CONTROL_BORDER_SIZEY 2
96 #define BUTTON_SPACINGX 4
97 #define BUTTON_SPACINGY 4
98 #define FLAT_BTN_SPACINGX 8
99 #define DEFAULT_TAB_WIDTH 96
101 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
103 /******************************************************************************
104 * Hot-tracking timer constants
106 #define TAB_HOTTRACK_TIMER 1
107 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
109 /******************************************************************************
112 static void TAB_Refresh (HWND hwnd, HDC hdc);
113 static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
114 static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
115 static void TAB_DrawItem(HWND hwnd, HDC hdc, INT iItem);
116 static void TAB_DrawItemInterior(HWND hwnd, HDC hdc, INT iItem, RECT* drawRect);
119 TAB_SendSimpleNotify (HWND hwnd, UINT code)
123 nmhdr.hwndFrom = hwnd;
124 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
127 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
128 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
132 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
133 WPARAM wParam, LPARAM lParam)
141 msg.time = GetMessageTime ();
142 msg.pt.x = LOWORD(GetMessagePos ());
143 msg.pt.y = HIWORD(GetMessagePos ());
145 SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
149 TAB_DumpItemExternalA(TCITEMA *pti, UINT iItem)
152 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
153 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
154 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextA=%s\n",
155 iItem, pti->iImage, pti->lParam, debugstr_a(pti->pszText));
161 TAB_DumpItemExternalW(TCITEMW *pti, UINT iItem)
164 TRACE("external tab %d, mask=0x%08x, dwState=0x%08lx, dwStateMask=0x%08lx, cchTextMax=0x%08x\n",
165 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
166 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
167 iItem, pti->iImage, pti->lParam, debugstr_w(pti->pszText));
172 TAB_DumpItemInternal(TAB_INFO *infoPtr, UINT iItem)
177 ti = &infoPtr->items[iItem];
178 TRACE("tab %d, mask=0x%08x, dwState=0x%08lx, pszText=%s, iImage=%d\n",
179 iItem, ti->mask, ti->dwState, debugstr_w(ti->pszText),
181 TRACE("tab %d, lParam=0x%08lx, rect.left=%ld, rect.top(row)=%ld\n",
182 iItem, ti->lParam, ti->rect.left, ti->rect.top);
187 TAB_GetCurSel (HWND hwnd)
189 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
191 return infoPtr->iSelected;
195 TAB_GetCurFocus (HWND hwnd)
197 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
199 return infoPtr->uFocus;
203 TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
205 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
207 if (infoPtr == NULL) return 0;
208 return (LRESULT)infoPtr->hwndToolTip;
212 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
214 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
215 INT iItem = (INT)wParam;
219 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
220 prevItem=infoPtr->iSelected;
221 infoPtr->iSelected=iItem;
222 TAB_EnsureSelectionVisible(hwnd, infoPtr);
223 TAB_InvalidateTabArea(hwnd, infoPtr);
229 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
231 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
232 INT iItem=(INT) wParam;
234 if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
236 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
237 FIXME("Should set input focus\n");
239 int oldFocus = infoPtr->uFocus;
240 if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) {
241 infoPtr->uFocus = iItem;
242 if (oldFocus != -1) {
243 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
244 infoPtr->iSelected = iItem;
245 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
248 infoPtr->iSelected = iItem;
249 TAB_EnsureSelectionVisible(hwnd, infoPtr);
250 TAB_InvalidateTabArea(hwnd, infoPtr);
258 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
260 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
262 if (infoPtr == NULL) return 0;
263 infoPtr->hwndToolTip = (HWND)wParam;
268 TAB_SetPadding (HWND hwnd, WPARAM wParam, LPARAM lParam)
270 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
272 if (infoPtr == NULL) return 0;
273 infoPtr->uHItemPadding=LOWORD(lParam);
274 infoPtr->uVItemPadding=HIWORD(lParam);
278 /******************************************************************************
279 * TAB_InternalGetItemRect
281 * This method will calculate the rectangle representing a given tab item in
282 * client coordinates. This method takes scrolling into account.
284 * This method returns TRUE if the item is visible in the window and FALSE
285 * if it is completely outside the client area.
287 static BOOL TAB_InternalGetItemRect(
294 RECT tmpItemRect,clientRect;
295 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
297 /* Perform a sanity check and a trivial visibility check. */
298 if ( (infoPtr->uNumItem <= 0) ||
299 (itemIndex >= infoPtr->uNumItem) ||
300 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
304 * Avoid special cases in this procedure by assigning the "out"
305 * parameters if the caller didn't supply them
307 if (itemRect == NULL)
308 itemRect = &tmpItemRect;
310 /* Retrieve the unmodified item rect. */
311 *itemRect = infoPtr->items[itemIndex].rect;
313 /* calculate the times bottom and top based on the row */
314 GetClientRect(hwnd, &clientRect);
316 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
318 itemRect->bottom = clientRect.bottom -
319 SELECTED_TAB_OFFSET -
320 itemRect->top * (infoPtr->tabHeight - 2) -
321 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
323 itemRect->top = clientRect.bottom -
325 itemRect->top * (infoPtr->tabHeight - 2) -
326 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
328 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
330 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * (infoPtr->tabHeight - 2) -
331 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
332 itemRect->left = clientRect.right - infoPtr->tabHeight - itemRect->left * (infoPtr->tabHeight - 2) -
333 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
335 else if((lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM))
337 itemRect->right = clientRect.left + infoPtr->tabHeight + itemRect->left * (infoPtr->tabHeight - 2) +
338 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
339 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * (infoPtr->tabHeight - 2) +
340 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
342 else if(!(lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM)) /* not TCS_BOTTOM and not TCS_VERTICAL */
344 itemRect->bottom = clientRect.top +
346 itemRect->top * (infoPtr->tabHeight - 2) +
347 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
348 itemRect->top = clientRect.top +
349 SELECTED_TAB_OFFSET +
350 itemRect->top * (infoPtr->tabHeight - 2) +
351 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
355 * "scroll" it to make sure the item at the very left of the
356 * tab control is the leftmost visible tab.
358 if(lStyle & TCS_VERTICAL)
362 -(clientRect.bottom - infoPtr->items[infoPtr->leftmostVisible].rect.bottom));
365 * Move the rectangle so the first item is slightly offset from
366 * the bottom of the tab control.
370 -SELECTED_TAB_OFFSET);
375 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
379 * Move the rectangle so the first item is slightly offset from
380 * the left of the tab control.
386 TRACE("item %d tab h=%d, rect=(%ld,%ld)-(%ld,%ld)\n",
387 itemIndex, infoPtr->tabHeight,
388 itemRect->left, itemRect->top, itemRect->right, itemRect->bottom);
390 /* Now, calculate the position of the item as if it were selected. */
391 if (selectedRect!=NULL)
393 CopyRect(selectedRect, itemRect);
395 /* The rectangle of a selected item is a bit wider. */
396 if(lStyle & TCS_VERTICAL)
397 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
399 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
401 /* If it also a bit higher. */
402 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
404 selectedRect->top -= 2; /* the border is thicker on the bottom */
405 selectedRect->bottom += SELECTED_TAB_OFFSET;
407 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
409 selectedRect->left -= 2; /* the border is thicker on the right */
410 selectedRect->right += SELECTED_TAB_OFFSET;
412 else if(lStyle & TCS_VERTICAL)
414 selectedRect->left -= SELECTED_TAB_OFFSET;
415 selectedRect->right += 1;
419 selectedRect->top -= SELECTED_TAB_OFFSET;
420 selectedRect->bottom += 1;
427 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
429 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
430 (LPRECT)lParam, (LPRECT)NULL);
433 /******************************************************************************
436 * This method is called to handle keyboard input
438 static LRESULT TAB_KeyUp(
442 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
448 newItem = infoPtr->uFocus - 1;
451 newItem = infoPtr->uFocus + 1;
456 * If we changed to a valid item, change the selection
458 if ((newItem >= 0) &&
459 (newItem < infoPtr->uNumItem) &&
460 (infoPtr->uFocus != newItem))
462 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
464 infoPtr->iSelected = newItem;
465 infoPtr->uFocus = newItem;
466 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
468 TAB_EnsureSelectionVisible(hwnd, infoPtr);
469 TAB_InvalidateTabArea(hwnd, infoPtr);
476 /******************************************************************************
479 * This method is called whenever the focus goes in or out of this control
480 * it is used to update the visual state of the control.
482 static LRESULT TAB_FocusChanging(
488 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
493 * Get the rectangle for the item.
495 isVisible = TAB_InternalGetItemRect(hwnd,
502 * If the rectangle is not completely invisible, invalidate that
503 * portion of the window.
507 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
508 selectedRect.left,selectedRect.top,
509 selectedRect.right,selectedRect.bottom);
510 InvalidateRect(hwnd, &selectedRect, TRUE);
514 * Don't otherwise disturb normal behavior.
516 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
519 static INT TAB_InternalHitTest (
529 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
531 TAB_InternalGetItemRect(hwnd, infoPtr, iCount, &rect, NULL);
533 if (PtInRect(&rect, pt))
535 *flags = TCHT_ONITEM;
540 *flags = TCHT_NOWHERE;
545 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
547 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
548 LPTCHITTESTINFO lptest = (LPTCHITTESTINFO) lParam;
550 return TAB_InternalHitTest (hwnd, infoPtr, lptest->pt, &lptest->flags);
553 /******************************************************************************
556 * Napster v2b5 has a tab control for its main navigation which has a client
557 * area that covers the whole area of the dialog pages.
558 * That's why it receives all msgs for that area and the underlying dialog ctrls
560 * So I decided that we should handle WM_NCHITTEST here and return
561 * HTTRANSPARENT if we don't hit the tab control buttons.
562 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
563 * doesn't do it that way. Maybe depends on tab control styles ?
566 TAB_NCHitTest (HWND hwnd, LPARAM lParam)
568 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
572 pt.x = LOWORD(lParam);
573 pt.y = HIWORD(lParam);
574 ScreenToClient(hwnd, &pt);
576 if (TAB_InternalHitTest(hwnd, infoPtr, pt, &dummyflag) == -1)
577 return HTTRANSPARENT;
583 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
585 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
589 if (infoPtr->hwndToolTip)
590 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
591 WM_LBUTTONDOWN, wParam, lParam);
593 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
597 if (infoPtr->hwndToolTip)
598 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
599 WM_LBUTTONDOWN, wParam, lParam);
601 pt.x = (INT)LOWORD(lParam);
602 pt.y = (INT)HIWORD(lParam);
604 newItem = TAB_InternalHitTest (hwnd, infoPtr, pt, &dummy);
606 TRACE("On Tab, item %d\n", newItem);
608 if ((newItem != -1) && (infoPtr->iSelected != newItem))
610 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING) != TRUE)
612 infoPtr->iSelected = newItem;
613 infoPtr->uFocus = newItem;
614 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
616 TAB_EnsureSelectionVisible(hwnd, infoPtr);
618 TAB_InvalidateTabArea(hwnd, infoPtr);
625 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
627 TAB_SendSimpleNotify(hwnd, NM_CLICK);
633 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
635 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
639 /******************************************************************************
640 * TAB_DrawLoneItemInterior
642 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
643 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
644 * up the device context and font. This routine does the same setup but
645 * only calls TAB_DrawItemInterior for the single specified item.
648 TAB_DrawLoneItemInterior(HWND hwnd, TAB_INFO* infoPtr, int iItem)
650 HDC hdc = GetDC(hwnd);
651 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
652 TAB_DrawItemInterior(hwnd, hdc, iItem, NULL);
653 SelectObject(hdc, hOldFont);
654 ReleaseDC(hwnd, hdc);
657 /******************************************************************************
658 * TAB_HotTrackTimerProc
660 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
661 * timer is setup so we can check if the mouse is moved out of our window.
662 * (We don't get an event when the mouse leaves, the mouse-move events just
663 * stop being delivered to our window and just start being delivered to
664 * another window.) This function is called when the timer triggers so
665 * we can check if the mouse has left our window. If so, we un-highlight
666 * the hot-tracked tab.
669 TAB_HotTrackTimerProc
671 HWND hwnd, /* handle of window for timer messages */
672 UINT uMsg, /* WM_TIMER message */
673 UINT idEvent, /* timer identifier */
674 DWORD dwTime /* current system time */
677 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
679 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
684 ** If we can't get the cursor position, or if the cursor is outside our
685 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
686 ** "outside" even if it is within our bounding rect if another window
687 ** overlaps. Note also that the case where the cursor stayed within our
688 ** window but has moved off the hot-tracked tab will be handled by the
689 ** WM_MOUSEMOVE event.
691 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
693 /* Redraw iHotTracked to look normal */
694 INT iRedraw = infoPtr->iHotTracked;
695 infoPtr->iHotTracked = -1;
696 TAB_DrawLoneItemInterior(hwnd, infoPtr, iRedraw);
698 /* Kill this timer */
699 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
704 /******************************************************************************
707 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
708 * should be highlighted. This function determines which tab in a tab control,
709 * if any, is under the mouse and records that information. The caller may
710 * supply output parameters to receive the item number of the tab item which
711 * was highlighted but isn't any longer and of the tab item which is now
712 * highlighted but wasn't previously. The caller can use this information to
713 * selectively redraw those tab items.
715 * If the caller has a mouse position, it can supply it through the pos
716 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
717 * supplies NULL and this function determines the current mouse position
725 int* out_redrawLeave,
729 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
734 if (out_redrawLeave != NULL)
735 *out_redrawLeave = -1;
736 if (out_redrawEnter != NULL)
737 *out_redrawEnter = -1;
739 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_HOTTRACK)
747 ScreenToClient(hwnd, &pt);
755 item = TAB_InternalHitTest(hwnd, infoPtr, pt, &flags);
758 if (item != infoPtr->iHotTracked)
760 if (infoPtr->iHotTracked >= 0)
762 /* Mark currently hot-tracked to be redrawn to look normal */
763 if (out_redrawLeave != NULL)
764 *out_redrawLeave = infoPtr->iHotTracked;
768 /* Kill timer which forces recheck of mouse pos */
769 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
774 /* Start timer so we recheck mouse pos */
775 UINT timerID = SetTimer
779 TAB_HOTTRACK_TIMER_INTERVAL,
780 TAB_HotTrackTimerProc
784 return; /* Hot tracking not available */
787 infoPtr->iHotTracked = item;
791 /* Mark new hot-tracked to be redrawn to look highlighted */
792 if (out_redrawEnter != NULL)
793 *out_redrawEnter = item;
798 /******************************************************************************
801 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
804 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
809 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
811 if (infoPtr->hwndToolTip)
812 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
813 WM_LBUTTONDOWN, wParam, lParam);
815 /* Determine which tab to highlight. Redraw tabs which change highlight
817 TAB_RecalcHotTrack(hwnd, &lParam, &redrawLeave, &redrawEnter);
819 if (redrawLeave != -1)
820 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawLeave);
821 if (redrawEnter != -1)
822 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawEnter);
827 /******************************************************************************
830 * Calculates the tab control's display area given the window rectangle or
831 * the window rectangle given the requested display rectangle.
833 static LRESULT TAB_AdjustRect(
838 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
839 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
841 if(lStyle & TCS_VERTICAL)
843 if (fLarger) /* Go from display rectangle */
845 /* Add the height of the tabs. */
846 if (lStyle & TCS_BOTTOM)
847 prc->right += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
849 prc->left -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
851 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
852 /* Inflate the rectangle for the padding */
853 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
855 /* Inflate for the border */
856 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
858 else /* Go from window rectangle. */
860 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
861 /* Deflate the rectangle for the border */
862 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
864 /* Deflate the rectangle for the padding */
865 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
867 /* Remove the height of the tabs. */
868 if (lStyle & TCS_BOTTOM)
869 prc->right -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
871 prc->left += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
875 if (fLarger) /* Go from display rectangle */
877 /* Add the height of the tabs. */
878 if (lStyle & TCS_BOTTOM)
879 prc->bottom += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
881 prc->top -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
883 /* Inflate the rectangle for the padding */
884 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
886 /* Inflate for the border */
887 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
889 else /* Go from window rectangle. */
891 /* Deflate the rectangle for the border */
892 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
894 /* Deflate the rectangle for the padding */
895 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
897 /* Remove the height of the tabs. */
898 if (lStyle & TCS_BOTTOM)
899 prc->bottom -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
901 prc->top += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
908 /******************************************************************************
911 * This method will handle the notification from the scroll control and
912 * perform the scrolling operation on the tab control.
914 static LRESULT TAB_OnHScroll(
920 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
922 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
924 if(nPos < infoPtr->leftmostVisible)
925 infoPtr->leftmostVisible--;
927 infoPtr->leftmostVisible++;
929 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
930 TAB_InvalidateTabArea(hwnd, infoPtr);
931 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
932 MAKELONG(infoPtr->leftmostVisible, 0));
938 /******************************************************************************
941 * This method will check the current scrolling state and make sure the
942 * scrolling control is displayed (or not).
944 static void TAB_SetupScrolling(
947 const RECT* clientRect)
950 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
952 if (infoPtr->needsScrolling)
958 * Calculate the position of the scroll control.
960 if(lStyle & TCS_VERTICAL)
962 controlPos.right = clientRect->right;
963 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
965 if (lStyle & TCS_BOTTOM)
967 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
968 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
972 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
973 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
978 controlPos.right = clientRect->right;
979 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
981 if (lStyle & TCS_BOTTOM)
983 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
984 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
988 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
989 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
994 * If we don't have a scroll control yet, we want to create one.
995 * If we have one, we want to make sure it's positioned properly.
997 if (infoPtr->hwndUpDown==0)
999 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
1001 WS_VISIBLE | WS_CHILD | UDS_HORZ,
1002 controlPos.left, controlPos.top,
1003 controlPos.right - controlPos.left,
1004 controlPos.bottom - controlPos.top,
1012 SetWindowPos(infoPtr->hwndUpDown,
1014 controlPos.left, controlPos.top,
1015 controlPos.right - controlPos.left,
1016 controlPos.bottom - controlPos.top,
1017 SWP_SHOWWINDOW | SWP_NOZORDER);
1020 /* Now calculate upper limit of the updown control range.
1021 * We do this by calculating how many tabs will be offscreen when the
1022 * last tab is visible.
1024 if(infoPtr->uNumItem)
1026 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
1027 maxRange = infoPtr->uNumItem;
1028 tabwidth = infoPtr->items[maxRange - 1].rect.right;
1030 for(; maxRange > 0; maxRange--)
1032 if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
1036 if(maxRange == infoPtr->uNumItem)
1042 /* If we once had a scroll control... hide it */
1043 if (infoPtr->hwndUpDown!=0)
1044 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1046 if (infoPtr->hwndUpDown)
1047 SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1050 /******************************************************************************
1053 * This method will calculate the position rectangles of all the items in the
1054 * control. The rectangle calculated starts at 0 for the first item in the
1055 * list and ignores scrolling and selection.
1056 * It also uses the current font to determine the height of the tab row and
1057 * it checks if all the tabs fit in the client area of the window. If they
1058 * dont, a scrolling control is added.
1060 static void TAB_SetItemBounds (HWND hwnd)
1062 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1063 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1064 TEXTMETRICA fontMetrics;
1067 INT curItemRowCount;
1068 HFONT hFont, hOldFont;
1077 * We need to get text information so we need a DC and we need to select
1082 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1083 hOldFont = SelectObject (hdc, hFont);
1086 * We will base the rectangle calculations on the client rectangle
1089 GetClientRect(hwnd, &clientRect);
1091 /* if TCS_VERTICAL then swap the height and width so this code places the
1092 tabs along the top of the rectangle and we can just rotate them after
1093 rather than duplicate all of the below code */
1094 if(lStyle & TCS_VERTICAL)
1096 iTemp = clientRect.bottom;
1097 clientRect.bottom = clientRect.right;
1098 clientRect.right = iTemp;
1101 /* The leftmost item will be "0" aligned */
1103 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1105 if (!(infoPtr->fHeightSet))
1108 int icon_height = 0;
1110 /* Use the current font to determine the height of a tab. */
1111 GetTextMetricsA(hdc, &fontMetrics);
1113 /* Get the icon height */
1115 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1117 /* Take the highest between font or icon */
1118 if (fontMetrics.tmHeight > icon_height)
1119 item_height = fontMetrics.tmHeight + 2;
1121 item_height = icon_height;
1124 * Make sure there is enough space for the letters + icon + growing the
1125 * selected item + extra space for the selected item.
1127 infoPtr->tabHeight = item_height + SELECTED_TAB_OFFSET +
1128 ((lStyle & TCS_BUTTONS) ? 2 : 1) *
1129 infoPtr->uVItemPadding;
1131 TRACE("tabH=%d, tmH=%ld, iconh=%d\n",
1132 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1135 TRACE("client right=%ld\n", clientRect.right);
1137 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1139 /* Set the leftmost position of the tab. */
1140 infoPtr->items[curItem].rect.left = curItemLeftPos;
1142 if ( lStyle & (TCS_FIXEDWIDTH | TCS_OWNERDRAWFIXED) )
1144 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1146 2 * infoPtr->uHItemPadding;
1153 /* Calculate how wide the tab is depending on the text it contains */
1154 GetTextExtentPoint32W(hdc, infoPtr->items[curItem].pszText,
1155 lstrlenW(infoPtr->items[curItem].pszText), &size);
1157 /* under Windows, there seems to be a minimum width of 2x the height
1158 * for button style tabs */
1159 if (lStyle & TCS_BUTTONS)
1160 size.cx = max(size.cx, 2 * (infoPtr->tabHeight - 2));
1162 /* Add the icon width */
1165 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1169 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1170 size.cx + icon_width +
1171 num * infoPtr->uHItemPadding;
1172 TRACE("for <%s>, l,r=%ld,%ld, num=%d\n",
1173 debugstr_w(infoPtr->items[curItem].pszText),
1174 infoPtr->items[curItem].rect.left,
1175 infoPtr->items[curItem].rect.right,
1180 * Check if this is a multiline tab control and if so
1181 * check to see if we should wrap the tabs
1183 * Because we are going to arange all these tabs evenly
1184 * really we are basically just counting rows at this point
1188 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1189 (infoPtr->items[curItem].rect.right > clientRect.right))
1191 infoPtr->items[curItem].rect.right -=
1192 infoPtr->items[curItem].rect.left;
1194 infoPtr->items[curItem].rect.left = 0;
1196 TRACE("wrapping <%s>, l,r=%ld,%ld\n",
1197 debugstr_w(infoPtr->items[curItem].pszText),
1198 infoPtr->items[curItem].rect.left,
1199 infoPtr->items[curItem].rect.right);
1202 infoPtr->items[curItem].rect.bottom = 0;
1203 infoPtr->items[curItem].rect.top = curItemRowCount - 1;
1205 TRACE("TextSize: %li\n", size.cx);
1206 TRACE("Rect: T %li, L %li, B %li, R %li\n",
1207 infoPtr->items[curItem].rect.top,
1208 infoPtr->items[curItem].rect.left,
1209 infoPtr->items[curItem].rect.bottom,
1210 infoPtr->items[curItem].rect.right);
1213 * The leftmost position of the next item is the rightmost position
1216 if (lStyle & TCS_BUTTONS)
1218 curItemLeftPos = infoPtr->items[curItem].rect.right + 1;
1219 if (lStyle & TCS_FLATBUTTONS)
1220 curItemLeftPos += FLAT_BTN_SPACINGX;
1223 curItemLeftPos = infoPtr->items[curItem].rect.right;
1226 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1229 * Check if we need a scrolling control.
1231 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1234 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1235 if(!infoPtr->needsScrolling)
1236 infoPtr->leftmostVisible = 0;
1238 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
1241 /* Set the number of rows */
1242 infoPtr->uNumRows = curItemRowCount;
1244 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->uNumItem > 0))
1246 INT widthDiff, remainder;
1247 INT tabPerRow,remTab;
1249 INT iIndexStart=0,iIndexEnd=0, iCount=0;
1252 * Ok windows tries to even out the rows. place the same
1253 * number of tabs in each row. So lets give that a shot
1256 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1257 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1259 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1260 iItm<infoPtr->uNumItem;
1263 /* normalize the current rect */
1265 /* shift the item to the left side of the clientRect */
1266 infoPtr->items[iItm].rect.right -=
1267 infoPtr->items[iItm].rect.left;
1268 infoPtr->items[iItm].rect.left = 0;
1270 TRACE("r=%ld, cl=%d, cl.r=%ld, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1271 infoPtr->items[iItm].rect.right,
1272 curItemLeftPos, clientRect.right,
1273 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1275 /* if we have reached the maximum number of tabs on this row */
1276 /* move to the next row, reset our current item left position and */
1277 /* the count of items on this row */
1279 /* ************ FIXME FIXME FIXME *************** */
1283 /* if item n and n+1 are in the same row, */
1284 /* then the display has n+1 lower (toward the */
1285 /* bottom) than n. We do it just the */
1288 /* ************ FIXME FIXME FIXME *************** */
1290 if (lStyle & TCS_VERTICAL) {
1291 /* Vert: Add the remaining tabs in the *last* remainder rows */
1292 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1298 /* Horz: Add the remaining tabs in the *first* remainder rows */
1299 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1306 /* shift the item to the right to place it as the next item in this row */
1307 infoPtr->items[iItm].rect.left += curItemLeftPos;
1308 infoPtr->items[iItm].rect.right += curItemLeftPos;
1309 infoPtr->items[iItm].rect.top = iRow;
1310 if (lStyle & TCS_BUTTONS)
1312 curItemLeftPos = infoPtr->items[iItm].rect.right + 1;
1313 if (lStyle & TCS_FLATBUTTONS)
1314 curItemLeftPos += FLAT_BTN_SPACINGX;
1317 curItemLeftPos = infoPtr->items[iItm].rect.right;
1319 TRACE("arranging <%s>, l,r=%ld,%ld, row=%ld\n",
1320 debugstr_w(infoPtr->items[iItm].pszText),
1321 infoPtr->items[iItm].rect.left,
1322 infoPtr->items[iItm].rect.right,
1323 infoPtr->items[iItm].rect.top);
1330 while(iIndexStart < infoPtr->uNumItem)
1333 * find the indexs of the row
1335 /* find the first item on the next row */
1336 for (iIndexEnd=iIndexStart;
1337 (iIndexEnd < infoPtr->uNumItem) &&
1338 (infoPtr->items[iIndexEnd].rect.top ==
1339 infoPtr->items[iIndexStart].rect.top) ;
1341 /* intentionally blank */;
1344 * we need to justify these tabs so they fill the whole given
1348 /* find the amount of space remaining on this row */
1349 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1350 infoPtr->items[iIndexEnd - 1].rect.right;
1352 /* iCount is the number of tab items on this row */
1353 iCount = iIndexEnd - iIndexStart;
1358 remainder = widthDiff % iCount;
1359 widthDiff = widthDiff / iCount;
1360 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1361 for (iIndex=iIndexStart,iCount=0; iIndex < iIndexEnd;
1364 infoPtr->items[iIndex].rect.left += iCount * widthDiff;
1365 infoPtr->items[iIndex].rect.right += (iCount + 1) * widthDiff;
1367 TRACE("adjusting 1 <%s>, l,r=%ld,%ld\n",
1368 debugstr_w(infoPtr->items[iIndex].pszText),
1369 infoPtr->items[iIndex].rect.left,
1370 infoPtr->items[iIndex].rect.right);
1373 infoPtr->items[iIndex - 1].rect.right += remainder;
1375 else /* we have only one item on this row, make it take up the entire row */
1377 infoPtr->items[iIndexStart].rect.left = clientRect.left;
1378 infoPtr->items[iIndexStart].rect.right = clientRect.right - 4;
1380 TRACE("adjusting 2 <%s>, l,r=%ld,%ld\n",
1381 debugstr_w(infoPtr->items[iIndexStart].pszText),
1382 infoPtr->items[iIndexStart].rect.left,
1383 infoPtr->items[iIndexStart].rect.right);
1388 iIndexStart = iIndexEnd;
1393 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1394 if(lStyle & TCS_VERTICAL)
1397 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1399 rcItem = &(infoPtr->items[iIndex].rect);
1401 rcOriginal = *rcItem;
1403 /* this is rotating the items by 90 degrees around the center of the control */
1404 rcItem->top = (clientRect.right - (rcOriginal.left - clientRect.left)) - (rcOriginal.right - rcOriginal.left);
1405 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1406 rcItem->left = rcOriginal.top;
1407 rcItem->right = rcOriginal.bottom;
1411 TAB_EnsureSelectionVisible(hwnd,infoPtr);
1412 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1415 SelectObject (hdc, hOldFont);
1416 ReleaseDC (hwnd, hdc);
1419 /******************************************************************************
1420 * TAB_DrawItemInterior
1422 * This method is used to draw the interior (text and icon) of a single tab
1423 * into the tab control.
1426 TAB_DrawItemInterior
1434 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1435 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1443 if (drawRect == NULL)
1450 * Get the rectangle for the item.
1452 isVisible = TAB_InternalGetItemRect(hwnd, infoPtr, iItem, &itemRect, &selectedRect);
1457 * Make sure drawRect points to something valid; simplifies code.
1459 drawRect = &localRect;
1462 * This logic copied from the part of TAB_DrawItem which draws
1463 * the tab background. It's important to keep it in sync. I
1464 * would have liked to avoid code duplication, but couldn't figure
1465 * out how without making spaghetti of TAB_DrawItem.
1467 if (lStyle & TCS_BUTTONS)
1469 *drawRect = itemRect;
1470 if (iItem == infoPtr->iSelected)
1478 if (iItem == infoPtr->iSelected)
1479 *drawRect = selectedRect;
1481 *drawRect = itemRect;
1490 htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1491 holdPen = SelectObject(hdc, htextPen);
1493 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1494 SetTextColor(hdc, ( (iItem == infoPtr->iHotTracked) | (infoPtr->items[iItem].dwState & TCIS_HIGHLIGHTED)) ?
1495 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1499 * if owner draw, tell the owner to draw
1501 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd))
1507 * get the control id
1509 id = GetWindowLongA( hwnd, GWL_ID );
1512 * put together the DRAWITEMSTRUCT
1514 dis.CtlType = ODT_TAB;
1517 dis.itemAction = ODA_DRAWENTIRE;
1519 if ( iItem == infoPtr->iSelected )
1520 dis.itemState |= ODS_SELECTED;
1521 if (infoPtr->uFocus == iItem)
1522 dis.itemState |= ODS_FOCUS;
1523 dis.hwndItem = hwnd; /* */
1525 CopyRect(&dis.rcItem,drawRect);
1526 dis.itemData = infoPtr->items[iItem].lParam;
1529 * send the draw message
1531 SendMessageA( GetParent(hwnd), WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1542 HFONT hOldFont = 0; /* stop uninitialized warning */
1544 INT nEscapement = 0; /* stop uninitialized warning */
1545 INT nOrientation = 0; /* stop uninitialized warning */
1548 /* used to center the icon and text in the tab */
1553 * Deflate the rectangle to acount for the padding
1555 if(lStyle & TCS_VERTICAL)
1556 InflateRect(drawRect, -infoPtr->uVItemPadding, -infoPtr->uHItemPadding);
1558 InflateRect(drawRect, -infoPtr->uHItemPadding, -infoPtr->uVItemPadding);
1560 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1561 rcImage = *drawRect;
1565 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1568 * Setup for text output
1570 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1571 SetTextColor(hdc, ((iItem == infoPtr->iHotTracked) | (infoPtr->items[iItem].dwState & TCIS_HIGHLIGHTED))?
1572 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1574 /* get the rectangle that the text fits in */
1575 DrawTextW(hdc, infoPtr->items[iItem].pszText, -1,
1576 &rcText, DT_CALCRECT);
1579 * If not owner draw, then do the drawing ourselves.
1583 if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE))
1585 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1587 if(lStyle & TCS_VERTICAL)
1588 center_offset = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1590 center_offset = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1592 TRACE("for <%s>, c_o=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1593 debugstr_w(infoPtr->items[iItem].pszText), center_offset,
1594 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1595 (rcText.right-rcText.left));
1597 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1599 rcImage.top = drawRect->top + center_offset;
1600 rcImage.left = drawRect->right - cx; /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1601 /* right side of the tab, but the image still uses the left as its x position */
1602 /* this keeps the image always drawn off of the same side of the tab */
1603 drawRect->top = rcImage.top + (cx + infoPtr->uHItemPadding);
1605 else if(lStyle & TCS_VERTICAL)
1607 rcImage.top = drawRect->bottom - cy - center_offset;
1609 drawRect->bottom = rcImage.top - infoPtr->uHItemPadding;
1611 else /* normal style, whether TCS_BOTTOM or not */
1613 rcImage.left = drawRect->left + center_offset + 3;
1614 drawRect->left = rcImage.left + cx + infoPtr->uHItemPadding;
1615 rcImage.top -= (lStyle & TCS_BOTTOM) ? 2 : 1;
1618 TRACE("drawing image=%d, left=%ld, top=%ld\n",
1619 infoPtr->items[iItem].iImage, rcImage.left, rcImage.top-1);
1623 infoPtr->items[iItem].iImage,
1629 } else /* no image, so just shift the drawRect borders around */
1631 if(lStyle & TCS_VERTICAL)
1635 currently the rcText rect is flawed because the rotated font does not
1636 often match the horizontal font. So leave this as 0
1637 ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1639 if(lStyle & TCS_BOTTOM)
1640 drawRect->top+=center_offset;
1642 drawRect->bottom-=center_offset;
1646 center_offset = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1647 drawRect->left+=center_offset;
1652 if (lStyle & TCS_RIGHTJUSTIFY)
1653 uHorizAlign = DT_CENTER;
1655 uHorizAlign = DT_LEFT;
1657 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1659 if(lStyle & TCS_BOTTOM)
1662 nOrientation = -900;
1671 /* to get a font with the escapement and orientation we are looking for, we need to */
1672 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1673 if(lStyle & TCS_VERTICAL)
1675 if (!GetObjectA((infoPtr->hFont) ?
1676 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1677 sizeof(LOGFONTA),&logfont))
1681 lstrcpyA(logfont.lfFaceName, "Arial");
1682 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1684 logfont.lfWeight = FW_NORMAL;
1685 logfont.lfItalic = 0;
1686 logfont.lfUnderline = 0;
1687 logfont.lfStrikeOut = 0;
1690 logfont.lfEscapement = nEscapement;
1691 logfont.lfOrientation = nOrientation;
1692 hFont = CreateFontIndirectA(&logfont);
1693 hOldFont = SelectObject(hdc, hFont);
1696 if (lStyle & TCS_VERTICAL)
1699 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1700 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1703 infoPtr->items[iItem].pszText,
1704 lstrlenW(infoPtr->items[iItem].pszText),
1712 infoPtr->items[iItem].pszText,
1713 lstrlenW(infoPtr->items[iItem].pszText),
1715 uHorizAlign | DT_SINGLELINE
1719 /* clean things up */
1720 *drawRect = rcTemp; /* restore drawRect */
1722 if(lStyle & TCS_VERTICAL)
1724 SelectObject(hdc, hOldFont); /* restore the original font */
1726 DeleteObject(hFont);
1733 SetBkMode(hdc, oldBkMode);
1734 SelectObject(hdc, holdPen);
1735 DeleteObject( htextPen );
1738 /******************************************************************************
1741 * This method is used to draw a single tab into the tab control.
1743 static void TAB_DrawItem(
1748 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1749 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1753 RECT r, fillRect, r1;
1756 COLORREF bkgnd, corner;
1759 * Get the rectangle for the item.
1761 isVisible = TAB_InternalGetItemRect(hwnd,
1769 /* If you need to see what the control is doing,
1770 * then override these variables. They will change what
1771 * fill colors are used for filling the tabs, and the
1772 * corners when drawing the edge.
1774 bkgnd = comctl32_color.clrBtnFace;
1775 corner = comctl32_color.clrBtnFace;
1777 if (lStyle & TCS_BUTTONS)
1779 HBRUSH hbr = CreateSolidBrush (bkgnd);
1780 BOOL deleteBrush = TRUE;
1782 /* Get item rectangle */
1785 /* Separators between flat buttons */
1786 if (lStyle & TCS_FLATBUTTONS)
1789 r1.right += (FLAT_BTN_SPACINGX -2);
1790 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1793 if (iItem == infoPtr->iSelected)
1795 /* Background color */
1796 if (!(lStyle & TCS_OWNERDRAWFIXED))
1799 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1801 SetTextColor(hdc, comctl32_color.clr3dFace);
1802 SetBkColor(hdc, comctl32_color.clr3dHilight);
1804 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1805 * we better use 0x55aa bitmap brush to make scrollbar's background
1806 * look different from the window background.
1808 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1809 hbr = COMCTL32_hPattern55AABrush;
1811 deleteBrush = FALSE;
1814 /* Clear interior */
1815 FillRect(hdc, &r, hbr);
1817 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1819 else /* ! selected */
1821 if (!(lStyle & TCS_FLATBUTTONS))
1823 /* Clear interior */
1824 FillRect(hdc, &r, hbr);
1826 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1831 if (deleteBrush) DeleteObject(hbr);
1833 else /* !TCS_BUTTONS */
1835 /* We draw a rectangle of different sizes depending on the selection
1837 if (iItem == infoPtr->iSelected) {
1839 GetClientRect (hwnd, &rect);
1840 clRight = rect.right;
1841 clBottom = rect.bottom;
1848 * Erase the background. (Delay it but setup rectangle.)
1849 * This is necessary when drawing the selected item since it is larger
1850 * than the others, it might overlap with stuff already drawn by the
1855 if(lStyle & TCS_VERTICAL)
1857 /* These are for adjusting the drawing of a Selected tab */
1858 /* The initial values are for the normal case of non-Selected */
1859 int ZZ = 1; /* Do not strech if selected */
1860 if (iItem == infoPtr->iSelected) {
1863 /* if leftmost draw the line longer */
1864 if(selectedRect.top == 0)
1866 /* if rightmost draw the line longer */
1867 if(selectedRect.bottom == clBottom)
1868 fillRect.bottom -= 2;
1871 if (lStyle & TCS_BOTTOM)
1873 /* Adjust both rectangles to match native */
1876 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1878 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1879 r.left,r.top,r.right,r.bottom);
1881 /* Clear interior */
1882 SetBkColor(hdc, bkgnd);
1883 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1885 /* Draw rectangular edge around tab */
1886 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
1888 /* Now erase the top corner and draw diagonal edge */
1889 SetBkColor(hdc, corner);
1890 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1893 r1.bottom = r1.top + ROUND_CORNER_SIZE;
1894 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1896 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
1898 /* Now erase the bottom corner and draw diagonal edge */
1899 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1900 r1.bottom = r.bottom;
1902 r1.top = r1.bottom - ROUND_CORNER_SIZE;
1903 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1905 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
1907 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
1911 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
1917 /* Adjust both rectangles to match native */
1918 fillRect.right += (1-ZZ);
1920 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1922 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1923 r.left,r.top,r.right,r.bottom);
1925 /* Clear interior */
1926 SetBkColor(hdc, bkgnd);
1927 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1929 /* Draw rectangular edge around tab */
1930 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
1932 /* Now erase the top corner and draw diagonal edge */
1933 SetBkColor(hdc, corner);
1936 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
1937 r1.bottom = r1.top + ROUND_CORNER_SIZE;
1938 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1940 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
1942 /* Now erase the bottom corner and draw diagonal edge */
1944 r1.bottom = r.bottom;
1945 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
1946 r1.top = r1.bottom - ROUND_CORNER_SIZE;
1947 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1949 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
1952 else /* ! TCS_VERTICAL */
1954 /* These are for adjusting the drawing of a Selected tab */
1955 /* The initial values are for the normal case of non-Selected */
1956 int ZZ = 1; /* Do not strech if selected */
1957 if (iItem == infoPtr->iSelected) {
1960 /* if leftmost draw the line longer */
1961 if(selectedRect.left == 0)
1963 /* if rightmost draw the line longer */
1964 if(selectedRect.right == clRight)
1965 fillRect.right -= 2;
1968 if (lStyle & TCS_BOTTOM)
1971 /* Adjust both rectangles to match native */
1977 TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1979 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1980 r.left,r.top,r.right,r.bottom);
1982 /* Clear interior */
1983 SetBkColor(hdc, bkgnd);
1984 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1986 /* Draw rectangular edge around tab */
1987 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
1989 /* Now erase the righthand corner and draw diagonal edge */
1990 SetBkColor(hdc, corner);
1991 r1.left = r.right - ROUND_CORNER_SIZE;
1992 r1.bottom = r.bottom;
1994 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
1995 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1997 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
1999 /* Now erase the lefthand corner and draw diagonal edge */
2001 r1.bottom = r.bottom;
2002 r1.right = r1.left + ROUND_CORNER_SIZE;
2003 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2004 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2006 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2008 if ((iItem == infoPtr->iSelected) && (selectedRect.left == 0)) {
2012 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2019 /* Adjust both rectangles to match native */
2020 fillRect.bottom += (1-ZZ);
2022 TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2024 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2025 r.left,r.top,r.right,r.bottom);
2027 /* Clear interior */
2028 SetBkColor(hdc, bkgnd);
2029 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2031 /* Draw rectangular edge around tab */
2032 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2034 /* Now erase the righthand corner and draw diagonal edge */
2035 SetBkColor(hdc, corner);
2036 r1.left = r.right - ROUND_CORNER_SIZE;
2039 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2040 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2042 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2044 /* Now erase the lefthand corner and draw diagonal edge */
2047 r1.right = r1.left + ROUND_CORNER_SIZE;
2048 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2049 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2051 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2057 TAB_DumpItemInternal(infoPtr, iItem);
2059 /* This modifies r to be the text rectangle. */
2061 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
2062 TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
2063 SelectObject(hdc,hOldFont);
2066 /* Draw the focus rectangle */
2067 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
2068 (GetFocus() == hwnd) &&
2069 (iItem == infoPtr->uFocus) )
2072 InflateRect(&r, -1, -1);
2074 DrawFocusRect(hdc, &r);
2079 /******************************************************************************
2082 * This method is used to draw the raised border around the tab control
2085 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
2087 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2089 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2091 GetClientRect (hwnd, &rect);
2094 * Adjust for the style
2097 if (infoPtr->uNumItem)
2099 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2101 rect.bottom -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 3;
2103 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2105 rect.right -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2107 else if(lStyle & TCS_VERTICAL)
2109 rect.left += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2111 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2113 rect.top += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2117 TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2118 rect.left, rect.top, rect.right, rect.bottom);
2120 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2123 /******************************************************************************
2126 * This method repaints the tab control..
2128 static void TAB_Refresh (HWND hwnd, HDC hdc)
2130 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2134 if (!infoPtr->DoRedraw)
2137 hOldFont = SelectObject (hdc, infoPtr->hFont);
2139 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
2141 for (i = 0; i < infoPtr->uNumItem; i++)
2142 TAB_DrawItem (hwnd, hdc, i);
2146 /* Draw all the non selected item first */
2147 for (i = 0; i < infoPtr->uNumItem; i++)
2149 if (i != infoPtr->iSelected)
2150 TAB_DrawItem (hwnd, hdc, i);
2153 /* Now, draw the border, draw it before the selected item
2154 * since the selected item overwrites part of the border. */
2155 TAB_DrawBorder (hwnd, hdc);
2157 /* Then, draw the selected item */
2158 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
2160 /* If we haven't set the current focus yet, set it now.
2161 * Only happens when we first paint the tab controls */
2162 if (infoPtr->uFocus == -1)
2163 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
2166 SelectObject (hdc, hOldFont);
2170 TAB_GetRowCount (HWND hwnd )
2172 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2174 return infoPtr->uNumRows;
2178 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
2180 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2182 infoPtr->DoRedraw=(BOOL) wParam;
2186 static LRESULT TAB_EraseBackground(
2193 HBRUSH brush = CreateSolidBrush(comctl32_color.clrBtnFace);
2195 hdc = givenDC ? givenDC : GetDC(hwnd);
2197 GetClientRect(hwnd, &clientRect);
2199 FillRect(hdc, &clientRect, brush);
2202 ReleaseDC(hwnd, hdc);
2204 DeleteObject(brush);
2209 /******************************************************************************
2210 * TAB_EnsureSelectionVisible
2212 * This method will make sure that the current selection is completely
2213 * visible by scrolling until it is.
2215 static void TAB_EnsureSelectionVisible(
2219 INT iSelected = infoPtr->iSelected;
2220 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2221 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2223 /* set the items row to the bottommost row or topmost row depending on
2225 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2230 if(lStyle & TCS_VERTICAL)
2231 newselected = infoPtr->items[iSelected].rect.left;
2233 newselected = infoPtr->items[iSelected].rect.top;
2235 /* the target row is always (number of rows - 1)
2236 as row 0 is furthest from the clientRect */
2237 iTargetRow = infoPtr->uNumRows - 1;
2239 if (newselected != iTargetRow)
2242 if(lStyle & TCS_VERTICAL)
2244 for (i=0; i < infoPtr->uNumItem; i++)
2246 /* move everything in the row of the selected item to the iTargetRow */
2247 if (infoPtr->items[i].rect.left == newselected )
2248 infoPtr->items[i].rect.left = iTargetRow;
2251 if (infoPtr->items[i].rect.left > newselected)
2252 infoPtr->items[i].rect.left-=1;
2258 for (i=0; i < infoPtr->uNumItem; i++)
2260 if (infoPtr->items[i].rect.top == newselected )
2261 infoPtr->items[i].rect.top = iTargetRow;
2264 if (infoPtr->items[i].rect.top > newselected)
2265 infoPtr->items[i].rect.top-=1;
2269 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2274 * Do the trivial cases first.
2276 if ( (!infoPtr->needsScrolling) ||
2277 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2280 if (infoPtr->leftmostVisible >= iSelected)
2282 infoPtr->leftmostVisible = iSelected;
2289 /* Calculate the part of the client area that is visible */
2290 GetClientRect(hwnd, &r);
2293 GetClientRect(infoPtr->hwndUpDown, &r);
2296 if ((infoPtr->items[iSelected].rect.right -
2297 infoPtr->items[iSelected].rect.left) >= width )
2299 /* Special case: width of selected item is greater than visible
2302 infoPtr->leftmostVisible = iSelected;
2306 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2308 if ((infoPtr->items[iSelected].rect.right -
2309 infoPtr->items[i].rect.left) < width)
2312 infoPtr->leftmostVisible = i;
2316 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2317 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2319 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2320 MAKELONG(infoPtr->leftmostVisible, 0));
2323 /******************************************************************************
2324 * TAB_InvalidateTabArea
2326 * This method will invalidate the portion of the control that contains the
2327 * tabs. It is called when the state of the control changes and needs
2330 static void TAB_InvalidateTabArea(
2335 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2336 INT lastRow = infoPtr->uNumRows - 1;
2339 if (lastRow < 0) return;
2341 GetClientRect(hwnd, &clientRect);
2343 TAB_InternalGetItemRect(hwnd, infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2344 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2346 clientRect.top = clientRect.bottom -
2347 infoPtr->tabHeight -
2348 lastRow * (infoPtr->tabHeight - 2) -
2349 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 3;
2350 clientRect.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2352 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2354 clientRect.left = clientRect.right - infoPtr->tabHeight -
2355 lastRow * (infoPtr->tabHeight - 2) -
2356 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 2;
2357 clientRect.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2359 else if(lStyle & TCS_VERTICAL)
2361 clientRect.right = clientRect.left + infoPtr->tabHeight +
2362 lastRow * (infoPtr->tabHeight - 2) -
2363 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 2;
2364 clientRect.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2368 clientRect.bottom = clientRect.top + infoPtr->tabHeight +
2369 lastRow * (infoPtr->tabHeight - 2) +
2370 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 2;
2371 clientRect.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2374 /* Punch out the updown control */
2375 if (infoPtr->needsScrolling && (clientRect.right > 0)) {
2376 GetClientRect(infoPtr->hwndUpDown, &r);
2377 clientRect.right = clientRect.right - (r.right - r.left);
2380 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2381 clientRect.left,clientRect.top,
2382 clientRect.right,clientRect.bottom);
2384 InvalidateRect(hwnd, &clientRect, TRUE);
2388 TAB_Paint (HWND hwnd, WPARAM wParam)
2395 hdc = BeginPaint (hwnd, &ps);
2396 TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2398 ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2401 TAB_EraseBackground (hwnd, hdc);
2407 TAB_Refresh (hwnd, hdc);
2410 EndPaint (hwnd, &ps);
2416 TAB_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2418 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2423 GetClientRect (hwnd, &rect);
2424 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2425 rect.top, rect.left, rect.bottom, rect.right);
2427 pti = (TCITEMA *)lParam;
2428 iItem = (INT)wParam;
2430 if (iItem < 0) return -1;
2431 if (iItem > infoPtr->uNumItem)
2432 iItem = infoPtr->uNumItem;
2434 TAB_DumpItemExternalA(pti, iItem);
2437 if (infoPtr->uNumItem == 0) {
2438 infoPtr->items = Alloc (sizeof (TAB_ITEM));
2439 infoPtr->uNumItem++;
2440 infoPtr->iSelected = 0;
2443 TAB_ITEM *oldItems = infoPtr->items;
2445 infoPtr->uNumItem++;
2446 infoPtr->items = Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2448 /* pre insert copy */
2450 memcpy (&infoPtr->items[0], &oldItems[0],
2451 iItem * sizeof(TAB_ITEM));
2454 /* post insert copy */
2455 if (iItem < infoPtr->uNumItem - 1) {
2456 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2457 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2461 if (iItem <= infoPtr->iSelected)
2462 infoPtr->iSelected++;
2467 infoPtr->items[iItem].mask = pti->mask;
2468 if (pti->mask & TCIF_TEXT)
2469 Str_SetPtrAtoW (&infoPtr->items[iItem].pszText, pti->pszText);
2471 if (pti->mask & TCIF_IMAGE)
2472 infoPtr->items[iItem].iImage = pti->iImage;
2474 if (pti->mask & TCIF_PARAM)
2475 infoPtr->items[iItem].lParam = pti->lParam;
2477 TAB_SetItemBounds(hwnd);
2478 if (infoPtr->uNumItem > 1)
2479 TAB_InvalidateTabArea(hwnd, infoPtr);
2481 InvalidateRect(hwnd, NULL, TRUE);
2483 TRACE("[%p]: added item %d %s\n",
2484 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2491 TAB_InsertItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2493 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2498 GetClientRect (hwnd, &rect);
2499 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2500 rect.top, rect.left, rect.bottom, rect.right);
2502 pti = (TCITEMW *)lParam;
2503 iItem = (INT)wParam;
2505 if (iItem < 0) return -1;
2506 if (iItem > infoPtr->uNumItem)
2507 iItem = infoPtr->uNumItem;
2509 TAB_DumpItemExternalW(pti, iItem);
2511 if (infoPtr->uNumItem == 0) {
2512 infoPtr->items = Alloc (sizeof (TAB_ITEM));
2513 infoPtr->uNumItem++;
2514 infoPtr->iSelected = 0;
2517 TAB_ITEM *oldItems = infoPtr->items;
2519 infoPtr->uNumItem++;
2520 infoPtr->items = Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2522 /* pre insert copy */
2524 memcpy (&infoPtr->items[0], &oldItems[0],
2525 iItem * sizeof(TAB_ITEM));
2528 /* post insert copy */
2529 if (iItem < infoPtr->uNumItem - 1) {
2530 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2531 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2535 if (iItem <= infoPtr->iSelected)
2536 infoPtr->iSelected++;
2541 infoPtr->items[iItem].mask = pti->mask;
2542 if (pti->mask & TCIF_TEXT)
2543 Str_SetPtrW (&infoPtr->items[iItem].pszText, pti->pszText);
2545 if (pti->mask & TCIF_IMAGE)
2546 infoPtr->items[iItem].iImage = pti->iImage;
2548 if (pti->mask & TCIF_PARAM)
2549 infoPtr->items[iItem].lParam = pti->lParam;
2551 TAB_SetItemBounds(hwnd);
2552 if (infoPtr->uNumItem > 1)
2553 TAB_InvalidateTabArea(hwnd, infoPtr);
2555 InvalidateRect(hwnd, NULL, TRUE);
2557 TRACE("[%p]: added item %d %s\n",
2558 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2565 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2567 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2568 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2570 BOOL bNeedPaint = FALSE;
2572 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2574 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2575 if ((lStyle & (TCS_FIXEDWIDTH | TCS_OWNERDRAWFIXED)) &&
2576 (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2578 infoPtr->tabWidth = max((INT)LOWORD(lParam), infoPtr->tabMinWidth);
2582 if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2584 if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2585 infoPtr->tabHeight = (INT)HIWORD(lParam);
2587 TAB_SetItemBounds(hwnd);
2591 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2592 HIWORD(lResult), LOWORD(lResult),
2593 infoPtr->tabHeight, infoPtr->tabWidth);
2596 RedrawWindow(hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2602 TAB_SetMinTabWidth (HWND hwnd, LPARAM lParam)
2604 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2605 INT cx = (INT)lParam;
2609 oldcx = infoPtr->tabMinWidth;
2610 infoPtr->tabMinWidth = (cx==-1)?DEFAULT_TAB_WIDTH:cx;
2618 TAB_HighlightItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2620 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2621 INT iItem = (INT)wParam;
2622 BOOL fHighlight = (BOOL)LOWORD(lParam);
2624 if ((infoPtr) && (iItem>=0) && (iItem<infoPtr->uNumItem)) {
2626 infoPtr->items[iItem].dwState |= TCIS_HIGHLIGHTED;
2628 infoPtr->items[iItem].dwState &= ~TCIS_HIGHLIGHTED;
2636 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2638 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2643 iItem = (INT)wParam;
2644 tabItem = (LPTCITEMA)lParam;
2646 TRACE("%d %p\n", iItem, tabItem);
2647 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2649 TAB_DumpItemExternalA(tabItem, iItem);
2651 wineItem = &infoPtr->items[iItem];
2653 if (tabItem->mask & TCIF_IMAGE)
2654 wineItem->iImage = tabItem->iImage;
2656 if (tabItem->mask & TCIF_PARAM)
2657 wineItem->lParam = tabItem->lParam;
2659 if (tabItem->mask & TCIF_RTLREADING)
2660 FIXME("TCIF_RTLREADING\n");
2662 if (tabItem->mask & TCIF_STATE)
2663 wineItem->dwState = tabItem->dwState;
2665 if (tabItem->mask & TCIF_TEXT)
2666 Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2668 /* Update and repaint tabs */
2669 TAB_SetItemBounds(hwnd);
2670 TAB_InvalidateTabArea(hwnd,infoPtr);
2677 TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2679 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2684 iItem = (INT)wParam;
2685 tabItem = (LPTCITEMW)lParam;
2687 TRACE("%d %p\n", iItem, tabItem);
2688 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2690 TAB_DumpItemExternalW(tabItem, iItem);
2692 wineItem = &infoPtr->items[iItem];
2694 if (tabItem->mask & TCIF_IMAGE)
2695 wineItem->iImage = tabItem->iImage;
2697 if (tabItem->mask & TCIF_PARAM)
2698 wineItem->lParam = tabItem->lParam;
2700 if (tabItem->mask & TCIF_RTLREADING)
2701 FIXME("TCIF_RTLREADING\n");
2703 if (tabItem->mask & TCIF_STATE)
2704 wineItem->dwState = tabItem->dwState;
2706 if (tabItem->mask & TCIF_TEXT)
2707 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2709 /* Update and repaint tabs */
2710 TAB_SetItemBounds(hwnd);
2711 TAB_InvalidateTabArea(hwnd,infoPtr);
2718 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2720 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2722 return infoPtr->uNumItem;
2727 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2729 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2734 iItem = (INT)wParam;
2735 tabItem = (LPTCITEMA)lParam;
2737 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2740 wineItem = &infoPtr->items[iItem];
2742 if (tabItem->mask & TCIF_IMAGE)
2743 tabItem->iImage = wineItem->iImage;
2745 if (tabItem->mask & TCIF_PARAM)
2746 tabItem->lParam = wineItem->lParam;
2748 if (tabItem->mask & TCIF_RTLREADING)
2749 FIXME("TCIF_RTLREADING\n");
2751 if (tabItem->mask & TCIF_STATE)
2752 tabItem->dwState = wineItem->dwState;
2754 if (tabItem->mask & TCIF_TEXT)
2755 Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2757 TAB_DumpItemExternalA(tabItem, iItem);
2764 TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2766 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2771 iItem = (INT)wParam;
2772 tabItem = (LPTCITEMW)lParam;
2774 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2777 wineItem=& infoPtr->items[iItem];
2779 if (tabItem->mask & TCIF_IMAGE)
2780 tabItem->iImage = wineItem->iImage;
2782 if (tabItem->mask & TCIF_PARAM)
2783 tabItem->lParam = wineItem->lParam;
2785 if (tabItem->mask & TCIF_RTLREADING)
2786 FIXME("TCIF_RTLREADING\n");
2788 if (tabItem->mask & TCIF_STATE)
2789 tabItem->dwState = wineItem->dwState;
2791 if (tabItem->mask & TCIF_TEXT)
2792 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2794 TAB_DumpItemExternalW(tabItem, iItem);
2801 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2803 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2804 INT iItem = (INT) wParam;
2805 BOOL bResult = FALSE;
2807 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2809 TAB_ITEM *oldItems = infoPtr->items;
2811 infoPtr->uNumItem--;
2812 infoPtr->items = Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
2815 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
2817 if (iItem < infoPtr->uNumItem)
2818 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
2819 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
2823 /* Readjust the selected index */
2824 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2825 infoPtr->iSelected--;
2827 if (iItem < infoPtr->iSelected)
2828 infoPtr->iSelected--;
2830 if (infoPtr->uNumItem == 0)
2831 infoPtr->iSelected = -1;
2833 /* Reposition and repaint tabs */
2834 TAB_SetItemBounds(hwnd);
2835 TAB_InvalidateTabArea(hwnd,infoPtr);
2844 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2846 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2848 Free (infoPtr->items);
2849 infoPtr->uNumItem = 0;
2850 infoPtr->iSelected = -1;
2851 if (infoPtr->iHotTracked >= 0)
2852 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2853 infoPtr->iHotTracked = -1;
2855 TAB_SetItemBounds(hwnd);
2856 TAB_InvalidateTabArea(hwnd,infoPtr);
2862 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2864 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2867 return (LRESULT)infoPtr->hFont;
2871 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2874 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2876 TRACE("%x %lx\n",wParam, lParam);
2878 infoPtr->hFont = (HFONT)wParam;
2880 TAB_SetItemBounds(hwnd);
2882 TAB_InvalidateTabArea(hwnd, infoPtr);
2889 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2891 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2894 return (LRESULT)infoPtr->himl;
2898 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2900 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2901 HIMAGELIST himlPrev;
2904 himlPrev = infoPtr->himl;
2905 infoPtr->himl= (HIMAGELIST)lParam;
2906 return (LRESULT)himlPrev;
2910 TAB_GetUnicodeFormat (HWND hwnd)
2912 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2913 return infoPtr->bUnicode;
2917 TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
2919 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2920 BOOL bTemp = infoPtr->bUnicode;
2922 infoPtr->bUnicode = (BOOL)wParam;
2928 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2931 /* I'm not really sure what the following code was meant to do.
2932 This is what it is doing:
2933 When WM_SIZE is sent with SIZE_RESTORED, the control
2934 gets positioned in the top left corner.
2938 UINT uPosFlags,cx,cy;
2942 parent = GetParent (hwnd);
2943 GetClientRect(parent, &parent_rect);
2946 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2947 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2949 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2950 cx, cy, uPosFlags | SWP_NOZORDER);
2952 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2955 /* Recompute the size/position of the tabs. */
2956 TAB_SetItemBounds (hwnd);
2958 /* Force a repaint of the control. */
2959 InvalidateRect(hwnd, NULL, TRUE);
2966 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2969 TEXTMETRICA fontMetrics;
2974 infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
2976 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
2978 infoPtr->uNumItem = 0;
2979 infoPtr->uNumRows = 0;
2980 infoPtr->uHItemPadding = 6;
2981 infoPtr->uVItemPadding = 3;
2984 infoPtr->hcurArrow = LoadCursorA (0, (LPSTR)IDC_ARROW);
2985 infoPtr->iSelected = -1;
2986 infoPtr->iHotTracked = -1;
2987 infoPtr->uFocus = -1;
2988 infoPtr->hwndToolTip = 0;
2989 infoPtr->DoRedraw = TRUE;
2990 infoPtr->needsScrolling = FALSE;
2991 infoPtr->hwndUpDown = 0;
2992 infoPtr->leftmostVisible = 0;
2993 infoPtr->fHeightSet = FALSE;
2994 infoPtr->bUnicode = IsWindowUnicode (hwnd);
2996 TRACE("Created tab control, hwnd [%p]\n", hwnd);
2998 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2999 if you don't specify it in CreateWindow. This is necessary in
3000 order for paint to work correctly. This follows windows behaviour. */
3001 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
3002 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
3004 if (dwStyle & TCS_TOOLTIPS) {
3005 /* Create tooltip control */
3006 infoPtr->hwndToolTip =
3007 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
3008 CW_USEDEFAULT, CW_USEDEFAULT,
3009 CW_USEDEFAULT, CW_USEDEFAULT,
3012 /* Send NM_TOOLTIPSCREATED notification */
3013 if (infoPtr->hwndToolTip) {
3014 NMTOOLTIPSCREATED nmttc;
3016 nmttc.hdr.hwndFrom = hwnd;
3017 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
3018 nmttc.hdr.code = NM_TOOLTIPSCREATED;
3019 nmttc.hwndToolTips = infoPtr->hwndToolTip;
3021 SendMessageA (GetParent (hwnd), WM_NOTIFY,
3022 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
3027 * We need to get text information so we need a DC and we need to select
3031 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3033 /* Use the system font to determine the initial height of a tab. */
3034 GetTextMetricsA(hdc, &fontMetrics);
3037 * Make sure there is enough space for the letters + growing the
3038 * selected item + extra space for the selected item.
3040 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3041 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
3042 infoPtr->uVItemPadding;
3044 /* Initialize the width of a tab. */
3045 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
3046 /* The minimum width is the default width at creation */
3047 infoPtr->tabMinWidth = DEFAULT_TAB_WIDTH;
3049 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3051 SelectObject (hdc, hOldFont);
3052 ReleaseDC(hwnd, hdc);
3058 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
3060 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3066 if (infoPtr->items) {
3067 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3068 if (infoPtr->items[iItem].pszText)
3069 Free (infoPtr->items[iItem].pszText);
3071 Free (infoPtr->items);
3074 if (infoPtr->hwndToolTip)
3075 DestroyWindow (infoPtr->hwndToolTip);
3077 if (infoPtr->hwndUpDown)
3078 DestroyWindow(infoPtr->hwndUpDown);
3080 if (infoPtr->iHotTracked >= 0)
3081 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
3084 SetWindowLongA(hwnd, 0, 0);
3088 static LRESULT WINAPI
3089 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3092 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3093 if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
3094 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3098 case TCM_GETIMAGELIST:
3099 return TAB_GetImageList (hwnd, wParam, lParam);
3101 case TCM_SETIMAGELIST:
3102 return TAB_SetImageList (hwnd, wParam, lParam);
3104 case TCM_GETITEMCOUNT:
3105 return TAB_GetItemCount (hwnd, wParam, lParam);
3108 return TAB_GetItemA (hwnd, wParam, lParam);
3111 return TAB_GetItemW (hwnd, wParam, lParam);
3114 return TAB_SetItemA (hwnd, wParam, lParam);
3117 return TAB_SetItemW (hwnd, wParam, lParam);
3119 case TCM_DELETEITEM:
3120 return TAB_DeleteItem (hwnd, wParam, lParam);
3122 case TCM_DELETEALLITEMS:
3123 return TAB_DeleteAllItems (hwnd, wParam, lParam);
3125 case TCM_GETITEMRECT:
3126 return TAB_GetItemRect (hwnd, wParam, lParam);
3129 return TAB_GetCurSel (hwnd);
3132 return TAB_HitTest (hwnd, wParam, lParam);
3135 return TAB_SetCurSel (hwnd, wParam);
3137 case TCM_INSERTITEMA:
3138 return TAB_InsertItemA (hwnd, wParam, lParam);
3140 case TCM_INSERTITEMW:
3141 return TAB_InsertItemW (hwnd, wParam, lParam);
3143 case TCM_SETITEMEXTRA:
3144 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
3147 case TCM_ADJUSTRECT:
3148 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
3150 case TCM_SETITEMSIZE:
3151 return TAB_SetItemSize (hwnd, wParam, lParam);
3153 case TCM_REMOVEIMAGE:
3154 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3157 case TCM_SETPADDING:
3158 return TAB_SetPadding (hwnd, wParam, lParam);
3160 case TCM_GETROWCOUNT:
3161 return TAB_GetRowCount(hwnd);
3163 case TCM_GETUNICODEFORMAT:
3164 return TAB_GetUnicodeFormat (hwnd);
3166 case TCM_SETUNICODEFORMAT:
3167 return TAB_SetUnicodeFormat (hwnd, wParam);
3169 case TCM_HIGHLIGHTITEM:
3170 return TAB_HighlightItem (hwnd, wParam, lParam);
3172 case TCM_GETTOOLTIPS:
3173 return TAB_GetToolTips (hwnd, wParam, lParam);
3175 case TCM_SETTOOLTIPS:
3176 return TAB_SetToolTips (hwnd, wParam, lParam);
3178 case TCM_GETCURFOCUS:
3179 return TAB_GetCurFocus (hwnd);
3181 case TCM_SETCURFOCUS:
3182 return TAB_SetCurFocus (hwnd, wParam);
3184 case TCM_SETMINTABWIDTH:
3185 return TAB_SetMinTabWidth(hwnd, lParam);
3187 case TCM_DESELECTALL:
3188 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3191 case TCM_GETEXTENDEDSTYLE:
3192 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3195 case TCM_SETEXTENDEDSTYLE:
3196 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3200 return TAB_GetFont (hwnd, wParam, lParam);
3203 return TAB_SetFont (hwnd, wParam, lParam);
3206 return TAB_Create (hwnd, wParam, lParam);
3209 return TAB_Destroy (hwnd, wParam, lParam);
3212 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3214 case WM_LBUTTONDOWN:
3215 return TAB_LButtonDown (hwnd, wParam, lParam);
3218 return TAB_LButtonUp (hwnd, wParam, lParam);
3221 return SendMessageA(GetParent(hwnd), WM_NOTIFY, wParam, lParam);
3223 case WM_RBUTTONDOWN:
3224 return TAB_RButtonDown (hwnd, wParam, lParam);
3227 return TAB_MouseMove (hwnd, wParam, lParam);
3230 return TAB_EraseBackground (hwnd, (HDC)wParam);
3233 return TAB_Paint (hwnd, wParam);
3236 return TAB_Size (hwnd, wParam, lParam);
3239 return TAB_SetRedraw (hwnd, wParam);
3242 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3244 case WM_STYLECHANGED:
3245 TAB_SetItemBounds (hwnd);
3246 InvalidateRect(hwnd, NULL, TRUE);
3249 case WM_SYSCOLORCHANGE:
3250 COMCTL32_RefreshSysColors();
3255 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
3258 return TAB_KeyUp(hwnd, wParam);
3260 return TAB_NCHitTest(hwnd, lParam);
3263 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
3264 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3265 uMsg, wParam, lParam);
3266 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
3278 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3279 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3280 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
3281 wndClass.cbClsExtra = 0;
3282 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3283 wndClass.hCursor = LoadCursorA (0, (LPSTR)IDC_ARROW);
3284 wndClass.hbrBackground = NULL;
3285 wndClass.lpszClassName = WC_TABCONTROLA;
3287 RegisterClassA (&wndClass);
3292 TAB_Unregister (void)
3294 UnregisterClassA (WC_TABCONTROLA, NULL);