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 HWND hwndNotify; /* notification window (parent) */
63 UINT uNumItem; /* number of tab items */
64 UINT uNumRows; /* number of tab rows */
65 INT tabHeight; /* height of the tab row */
66 INT tabWidth; /* width of tabs */
67 INT tabMinWidth; /* minimum width of items */
68 USHORT uHItemPadding; /* amount of horizontal padding, in pixels */
69 USHORT uVItemPadding; /* amount of vertical padding, in pixels */
70 HFONT hFont; /* handle to the current font */
71 HCURSOR hcurArrow; /* handle to the current cursor */
72 HIMAGELIST himl; /* handle to a image list (may be 0) */
73 HWND hwndToolTip; /* handle to tab's tooltip */
74 INT leftmostVisible; /* Used for scrolling, this member contains
75 * the index of the first visible item */
76 INT iSelected; /* the currently selected item */
77 INT iHotTracked; /* the highlighted item under the mouse */
78 INT uFocus; /* item which has the focus */
79 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
80 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
81 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
82 * the size of the control */
83 BOOL fHeightSet; /* was the height of the tabs explicitly set? */
84 BOOL bUnicode; /* Unicode control? */
85 HWND hwndUpDown; /* Updown control used for scrolling */
88 /******************************************************************************
89 * Positioning constants
91 #define SELECTED_TAB_OFFSET 2
92 #define ROUND_CORNER_SIZE 2
93 #define DISPLAY_AREA_PADDINGX 2
94 #define DISPLAY_AREA_PADDINGY 2
95 #define CONTROL_BORDER_SIZEX 2
96 #define CONTROL_BORDER_SIZEY 2
97 #define BUTTON_SPACINGX 3
98 #define BUTTON_SPACINGY 4
99 #define FLAT_BTN_SPACINGX 8
100 #define DEFAULT_TAB_WIDTH 96
102 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
104 /******************************************************************************
105 * Hot-tracking timer constants
107 #define TAB_HOTTRACK_TIMER 1
108 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
110 /******************************************************************************
113 static void TAB_Refresh (HWND hwnd, HDC hdc);
114 static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
115 static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
116 static void TAB_DrawItem(HWND hwnd, HDC hdc, INT iItem);
117 static void TAB_DrawItemInterior(HWND hwnd, HDC hdc, INT iItem, RECT* drawRect);
120 TAB_SendSimpleNotify (HWND hwnd, UINT code)
122 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
125 nmhdr.hwndFrom = hwnd;
126 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
129 return (BOOL) SendMessageA (infoPtr->hwndNotify, WM_NOTIFY,
130 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
134 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
135 WPARAM wParam, LPARAM lParam)
143 msg.time = GetMessageTime ();
144 msg.pt.x = LOWORD(GetMessagePos ());
145 msg.pt.y = HIWORD(GetMessagePos ());
147 SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
151 TAB_DumpItemExternalA(TCITEMA *pti, UINT iItem)
154 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
155 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
156 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextA=%s\n",
157 iItem, pti->iImage, pti->lParam, debugstr_a(pti->pszText));
163 TAB_DumpItemExternalW(TCITEMW *pti, UINT iItem)
166 TRACE("external tab %d, mask=0x%08x, dwState=0x%08lx, dwStateMask=0x%08lx, cchTextMax=0x%08x\n",
167 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
168 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
169 iItem, pti->iImage, pti->lParam, debugstr_w(pti->pszText));
174 TAB_DumpItemInternal(TAB_INFO *infoPtr, UINT iItem)
179 ti = &infoPtr->items[iItem];
180 TRACE("tab %d, mask=0x%08x, dwState=0x%08lx, pszText=%s, iImage=%d\n",
181 iItem, ti->mask, ti->dwState, debugstr_w(ti->pszText),
183 TRACE("tab %d, lParam=0x%08lx, rect.left=%ld, rect.top(row)=%ld\n",
184 iItem, ti->lParam, ti->rect.left, ti->rect.top);
189 TAB_GetCurSel (HWND hwnd)
191 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
193 return infoPtr->iSelected;
197 TAB_GetCurFocus (HWND hwnd)
199 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
201 return infoPtr->uFocus;
205 TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
207 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
209 if (infoPtr == NULL) return 0;
210 return (LRESULT)infoPtr->hwndToolTip;
214 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
216 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
217 INT iItem = (INT)wParam;
221 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
222 prevItem=infoPtr->iSelected;
223 infoPtr->iSelected=iItem;
224 TAB_EnsureSelectionVisible(hwnd, infoPtr);
225 TAB_InvalidateTabArea(hwnd, infoPtr);
231 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
233 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
234 INT iItem=(INT) wParam;
236 if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
238 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
239 FIXME("Should set input focus\n");
241 int oldFocus = infoPtr->uFocus;
242 if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) {
243 infoPtr->uFocus = iItem;
244 if (oldFocus != -1) {
245 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
246 infoPtr->iSelected = iItem;
247 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
250 infoPtr->iSelected = iItem;
251 TAB_EnsureSelectionVisible(hwnd, infoPtr);
252 TAB_InvalidateTabArea(hwnd, infoPtr);
260 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
262 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
264 if (infoPtr == NULL) return 0;
265 infoPtr->hwndToolTip = (HWND)wParam;
270 TAB_SetPadding (HWND hwnd, WPARAM wParam, LPARAM lParam)
272 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
274 if (infoPtr == NULL) return 0;
275 infoPtr->uHItemPadding=LOWORD(lParam);
276 infoPtr->uVItemPadding=HIWORD(lParam);
280 /******************************************************************************
281 * TAB_InternalGetItemRect
283 * This method will calculate the rectangle representing a given tab item in
284 * client coordinates. This method takes scrolling into account.
286 * This method returns TRUE if the item is visible in the window and FALSE
287 * if it is completely outside the client area.
289 static BOOL TAB_InternalGetItemRect(
296 RECT tmpItemRect,clientRect;
297 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
299 /* Perform a sanity check and a trivial visibility check. */
300 if ( (infoPtr->uNumItem <= 0) ||
301 (itemIndex >= infoPtr->uNumItem) ||
302 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
306 * Avoid special cases in this procedure by assigning the "out"
307 * parameters if the caller didn't supply them
309 if (itemRect == NULL)
310 itemRect = &tmpItemRect;
312 /* Retrieve the unmodified item rect. */
313 *itemRect = infoPtr->items[itemIndex].rect;
315 /* calculate the times bottom and top based on the row */
316 GetClientRect(hwnd, &clientRect);
318 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
320 itemRect->bottom = clientRect.bottom -
321 SELECTED_TAB_OFFSET -
322 itemRect->top * (infoPtr->tabHeight - 2) -
323 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
325 itemRect->top = clientRect.bottom -
327 itemRect->top * (infoPtr->tabHeight - 2) -
328 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
330 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
332 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * (infoPtr->tabHeight - 2) -
333 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
334 itemRect->left = clientRect.right - infoPtr->tabHeight - itemRect->left * (infoPtr->tabHeight - 2) -
335 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
337 else if((lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM))
339 itemRect->right = clientRect.left + infoPtr->tabHeight + itemRect->left * (infoPtr->tabHeight - 2) +
340 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
341 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * (infoPtr->tabHeight - 2) +
342 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
344 else if(!(lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM)) /* not TCS_BOTTOM and not TCS_VERTICAL */
346 itemRect->bottom = clientRect.top +
348 itemRect->top * (infoPtr->tabHeight - 2) +
349 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
350 itemRect->top = clientRect.top +
351 itemRect->top * (infoPtr->tabHeight - 2) +
352 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
356 * "scroll" it to make sure the item at the very left of the
357 * tab control is the leftmost visible tab.
359 if(lStyle & TCS_VERTICAL)
363 -(clientRect.bottom - infoPtr->items[infoPtr->leftmostVisible].rect.bottom));
366 * Move the rectangle so the first item is slightly offset from
367 * the bottom of the tab control.
371 -SELECTED_TAB_OFFSET);
376 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
380 * Move the rectangle so the first item is slightly offset from
381 * the left of the tab control.
387 TRACE("item %d tab h=%d, rect=(%ld,%ld)-(%ld,%ld)\n",
388 itemIndex, infoPtr->tabHeight,
389 itemRect->left, itemRect->top, itemRect->right, itemRect->bottom);
391 /* Now, calculate the position of the item as if it were selected. */
392 if (selectedRect!=NULL)
394 CopyRect(selectedRect, itemRect);
396 /* The rectangle of a selected item is a bit wider. */
397 if(lStyle & TCS_VERTICAL)
398 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
400 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
402 /* If it also a bit higher. */
403 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
405 selectedRect->top -= 2; /* the border is thicker on the bottom */
406 selectedRect->bottom += SELECTED_TAB_OFFSET;
408 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
410 selectedRect->left -= 2; /* the border is thicker on the right */
411 selectedRect->right += SELECTED_TAB_OFFSET;
413 else if(lStyle & TCS_VERTICAL)
415 selectedRect->left -= SELECTED_TAB_OFFSET;
416 selectedRect->right += 1;
420 selectedRect->top -= SELECTED_TAB_OFFSET;
421 selectedRect->bottom += 1;
428 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
430 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
431 (LPRECT)lParam, (LPRECT)NULL);
434 /******************************************************************************
437 * This method is called to handle keyboard input
439 static LRESULT TAB_KeyUp(
443 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
449 newItem = infoPtr->uFocus - 1;
452 newItem = infoPtr->uFocus + 1;
457 * If we changed to a valid item, change the selection
459 if ((newItem >= 0) &&
460 (newItem < infoPtr->uNumItem) &&
461 (infoPtr->uFocus != newItem))
463 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
465 infoPtr->iSelected = newItem;
466 infoPtr->uFocus = newItem;
467 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
469 TAB_EnsureSelectionVisible(hwnd, infoPtr);
470 TAB_InvalidateTabArea(hwnd, infoPtr);
477 /******************************************************************************
480 * This method is called whenever the focus goes in or out of this control
481 * it is used to update the visual state of the control.
483 static LRESULT TAB_FocusChanging(
489 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
494 * Get the rectangle for the item.
496 isVisible = TAB_InternalGetItemRect(hwnd,
503 * If the rectangle is not completely invisible, invalidate that
504 * portion of the window.
508 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
509 selectedRect.left,selectedRect.top,
510 selectedRect.right,selectedRect.bottom);
511 InvalidateRect(hwnd, &selectedRect, TRUE);
515 * Don't otherwise disturb normal behavior.
517 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
520 static INT TAB_InternalHitTest (
530 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
532 TAB_InternalGetItemRect(hwnd, infoPtr, iCount, &rect, NULL);
534 if (PtInRect(&rect, pt))
536 *flags = TCHT_ONITEM;
541 *flags = TCHT_NOWHERE;
546 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
548 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
549 LPTCHITTESTINFO lptest = (LPTCHITTESTINFO) lParam;
551 return TAB_InternalHitTest (hwnd, infoPtr, lptest->pt, &lptest->flags);
554 /******************************************************************************
557 * Napster v2b5 has a tab control for its main navigation which has a client
558 * area that covers the whole area of the dialog pages.
559 * That's why it receives all msgs for that area and the underlying dialog ctrls
561 * So I decided that we should handle WM_NCHITTEST here and return
562 * HTTRANSPARENT if we don't hit the tab control buttons.
563 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
564 * doesn't do it that way. Maybe depends on tab control styles ?
567 TAB_NCHitTest (HWND hwnd, LPARAM lParam)
569 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
573 pt.x = LOWORD(lParam);
574 pt.y = HIWORD(lParam);
575 ScreenToClient(hwnd, &pt);
577 if (TAB_InternalHitTest(hwnd, infoPtr, pt, &dummyflag) == -1)
578 return HTTRANSPARENT;
584 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
586 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
590 if (infoPtr->hwndToolTip)
591 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
592 WM_LBUTTONDOWN, wParam, lParam);
594 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
598 if (infoPtr->hwndToolTip)
599 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
600 WM_LBUTTONDOWN, wParam, lParam);
602 pt.x = (INT)LOWORD(lParam);
603 pt.y = (INT)HIWORD(lParam);
605 newItem = TAB_InternalHitTest (hwnd, infoPtr, pt, &dummy);
607 TRACE("On Tab, item %d\n", newItem);
609 if ((newItem != -1) && (infoPtr->iSelected != newItem))
611 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING) != TRUE)
613 infoPtr->iSelected = newItem;
614 infoPtr->uFocus = newItem;
615 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
617 TAB_EnsureSelectionVisible(hwnd, infoPtr);
619 TAB_InvalidateTabArea(hwnd, infoPtr);
626 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
628 TAB_SendSimpleNotify(hwnd, NM_CLICK);
634 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
636 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
640 /******************************************************************************
641 * TAB_DrawLoneItemInterior
643 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
644 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
645 * up the device context and font. This routine does the same setup but
646 * only calls TAB_DrawItemInterior for the single specified item.
649 TAB_DrawLoneItemInterior(HWND hwnd, TAB_INFO* infoPtr, int iItem)
651 HDC hdc = GetDC(hwnd);
652 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
653 TAB_DrawItemInterior(hwnd, hdc, iItem, NULL);
654 SelectObject(hdc, hOldFont);
655 ReleaseDC(hwnd, hdc);
658 /******************************************************************************
659 * TAB_HotTrackTimerProc
661 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
662 * timer is setup so we can check if the mouse is moved out of our window.
663 * (We don't get an event when the mouse leaves, the mouse-move events just
664 * stop being delivered to our window and just start being delivered to
665 * another window.) This function is called when the timer triggers so
666 * we can check if the mouse has left our window. If so, we un-highlight
667 * the hot-tracked tab.
670 TAB_HotTrackTimerProc
672 HWND hwnd, /* handle of window for timer messages */
673 UINT uMsg, /* WM_TIMER message */
674 UINT idEvent, /* timer identifier */
675 DWORD dwTime /* current system time */
678 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
680 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
685 ** If we can't get the cursor position, or if the cursor is outside our
686 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
687 ** "outside" even if it is within our bounding rect if another window
688 ** overlaps. Note also that the case where the cursor stayed within our
689 ** window but has moved off the hot-tracked tab will be handled by the
690 ** WM_MOUSEMOVE event.
692 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
694 /* Redraw iHotTracked to look normal */
695 INT iRedraw = infoPtr->iHotTracked;
696 infoPtr->iHotTracked = -1;
697 TAB_DrawLoneItemInterior(hwnd, infoPtr, iRedraw);
699 /* Kill this timer */
700 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
705 /******************************************************************************
708 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
709 * should be highlighted. This function determines which tab in a tab control,
710 * if any, is under the mouse and records that information. The caller may
711 * supply output parameters to receive the item number of the tab item which
712 * was highlighted but isn't any longer and of the tab item which is now
713 * highlighted but wasn't previously. The caller can use this information to
714 * selectively redraw those tab items.
716 * If the caller has a mouse position, it can supply it through the pos
717 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
718 * supplies NULL and this function determines the current mouse position
726 int* out_redrawLeave,
730 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
735 if (out_redrawLeave != NULL)
736 *out_redrawLeave = -1;
737 if (out_redrawEnter != NULL)
738 *out_redrawEnter = -1;
740 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_HOTTRACK)
748 ScreenToClient(hwnd, &pt);
756 item = TAB_InternalHitTest(hwnd, infoPtr, pt, &flags);
759 if (item != infoPtr->iHotTracked)
761 if (infoPtr->iHotTracked >= 0)
763 /* Mark currently hot-tracked to be redrawn to look normal */
764 if (out_redrawLeave != NULL)
765 *out_redrawLeave = infoPtr->iHotTracked;
769 /* Kill timer which forces recheck of mouse pos */
770 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
775 /* Start timer so we recheck mouse pos */
776 UINT timerID = SetTimer
780 TAB_HOTTRACK_TIMER_INTERVAL,
781 TAB_HotTrackTimerProc
785 return; /* Hot tracking not available */
788 infoPtr->iHotTracked = item;
792 /* Mark new hot-tracked to be redrawn to look highlighted */
793 if (out_redrawEnter != NULL)
794 *out_redrawEnter = item;
799 /******************************************************************************
802 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
805 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
810 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
812 if (infoPtr->hwndToolTip)
813 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
814 WM_LBUTTONDOWN, wParam, lParam);
816 /* Determine which tab to highlight. Redraw tabs which change highlight
818 TAB_RecalcHotTrack(hwnd, &lParam, &redrawLeave, &redrawEnter);
820 if (redrawLeave != -1)
821 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawLeave);
822 if (redrawEnter != -1)
823 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawEnter);
828 /******************************************************************************
831 * Calculates the tab control's display area given the window rectangle or
832 * the window rectangle given the requested display rectangle.
834 static LRESULT TAB_AdjustRect(
839 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
840 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
842 if(lStyle & TCS_VERTICAL)
844 if (fLarger) /* Go from display rectangle */
846 /* Add the height of the tabs. */
847 if (lStyle & TCS_BOTTOM)
848 prc->right += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
850 prc->left -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
852 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
853 /* Inflate the rectangle for the padding */
854 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
856 /* Inflate for the border */
857 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
859 else /* Go from window rectangle. */
861 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
862 /* Deflate the rectangle for the border */
863 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
865 /* Deflate the rectangle for the padding */
866 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
868 /* Remove the height of the tabs. */
869 if (lStyle & TCS_BOTTOM)
870 prc->right -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
872 prc->left += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
876 if (fLarger) /* Go from display rectangle */
878 /* Add the height of the tabs. */
879 if (lStyle & TCS_BOTTOM)
880 prc->bottom += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
882 prc->top -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
884 /* Inflate the rectangle for the padding */
885 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
887 /* Inflate for the border */
888 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
890 else /* Go from window rectangle. */
892 /* Deflate the rectangle for the border */
893 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
895 /* Deflate the rectangle for the padding */
896 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
898 /* Remove the height of the tabs. */
899 if (lStyle & TCS_BOTTOM)
900 prc->bottom -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
902 prc->top += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
909 /******************************************************************************
912 * This method will handle the notification from the scroll control and
913 * perform the scrolling operation on the tab control.
915 static LRESULT TAB_OnHScroll(
921 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
923 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
925 if(nPos < infoPtr->leftmostVisible)
926 infoPtr->leftmostVisible--;
928 infoPtr->leftmostVisible++;
930 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
931 TAB_InvalidateTabArea(hwnd, infoPtr);
932 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
933 MAKELONG(infoPtr->leftmostVisible, 0));
939 /******************************************************************************
942 * This method will check the current scrolling state and make sure the
943 * scrolling control is displayed (or not).
945 static void TAB_SetupScrolling(
948 const RECT* clientRect)
951 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
953 if (infoPtr->needsScrolling)
959 * Calculate the position of the scroll control.
961 if(lStyle & TCS_VERTICAL)
963 controlPos.right = clientRect->right;
964 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
966 if (lStyle & TCS_BOTTOM)
968 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
969 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
973 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
974 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
979 controlPos.right = clientRect->right;
980 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
982 if (lStyle & TCS_BOTTOM)
984 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
985 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
989 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
990 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
995 * If we don't have a scroll control yet, we want to create one.
996 * If we have one, we want to make sure it's positioned properly.
998 if (infoPtr->hwndUpDown==0)
1000 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
1002 WS_VISIBLE | WS_CHILD | UDS_HORZ,
1003 controlPos.left, controlPos.top,
1004 controlPos.right - controlPos.left,
1005 controlPos.bottom - controlPos.top,
1013 SetWindowPos(infoPtr->hwndUpDown,
1015 controlPos.left, controlPos.top,
1016 controlPos.right - controlPos.left,
1017 controlPos.bottom - controlPos.top,
1018 SWP_SHOWWINDOW | SWP_NOZORDER);
1021 /* Now calculate upper limit of the updown control range.
1022 * We do this by calculating how many tabs will be offscreen when the
1023 * last tab is visible.
1025 if(infoPtr->uNumItem)
1027 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
1028 maxRange = infoPtr->uNumItem;
1029 tabwidth = infoPtr->items[maxRange - 1].rect.right;
1031 for(; maxRange > 0; maxRange--)
1033 if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
1037 if(maxRange == infoPtr->uNumItem)
1043 /* If we once had a scroll control... hide it */
1044 if (infoPtr->hwndUpDown!=0)
1045 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1047 if (infoPtr->hwndUpDown)
1048 SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1051 /******************************************************************************
1054 * This method will calculate the position rectangles of all the items in the
1055 * control. The rectangle calculated starts at 0 for the first item in the
1056 * list and ignores scrolling and selection.
1057 * It also uses the current font to determine the height of the tab row and
1058 * it checks if all the tabs fit in the client area of the window. If they
1059 * dont, a scrolling control is added.
1061 static void TAB_SetItemBounds (HWND hwnd)
1063 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1064 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1065 TEXTMETRICA fontMetrics;
1068 INT curItemRowCount;
1069 HFONT hFont, hOldFont;
1079 * We need to get text information so we need a DC and we need to select
1084 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1085 hOldFont = SelectObject (hdc, hFont);
1088 * We will base the rectangle calculations on the client rectangle
1091 GetClientRect(hwnd, &clientRect);
1093 /* if TCS_VERTICAL then swap the height and width so this code places the
1094 tabs along the top of the rectangle and we can just rotate them after
1095 rather than duplicate all of the below code */
1096 if(lStyle & TCS_VERTICAL)
1098 iTemp = clientRect.bottom;
1099 clientRect.bottom = clientRect.right;
1100 clientRect.right = iTemp;
1103 /* The leftmost item will be "0" aligned */
1105 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1107 if (!(infoPtr->fHeightSet))
1110 int icon_height = 0;
1112 /* Use the current font to determine the height of a tab. */
1113 GetTextMetricsA(hdc, &fontMetrics);
1115 /* Get the icon height */
1117 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1119 /* Take the highest between font or icon */
1120 if (fontMetrics.tmHeight > icon_height)
1121 item_height = fontMetrics.tmHeight + 2;
1123 item_height = icon_height;
1126 * Make sure there is enough space for the letters + icon + growing the
1127 * selected item + extra space for the selected item.
1129 infoPtr->tabHeight = item_height + SELECTED_TAB_OFFSET +
1130 ((lStyle & TCS_BUTTONS) ? 2 : 1) *
1131 infoPtr->uVItemPadding;
1133 TRACE("tabH=%d, tmH=%ld, iconh=%d\n",
1134 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1137 TRACE("client right=%ld\n", clientRect.right);
1139 /* Get the icon width */
1142 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1144 if (lStyle & TCS_FIXEDWIDTH)
1147 /* Add padding if icon is present */
1148 icon_width += infoPtr->uHItemPadding;
1151 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1153 /* Set the leftmost position of the tab. */
1154 infoPtr->items[curItem].rect.left = curItemLeftPos;
1156 if (lStyle & TCS_FIXEDWIDTH)
1158 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1159 max(infoPtr->tabWidth, icon_width);
1165 /* Calculate how wide the tab is depending on the text it contains */
1166 GetTextExtentPoint32W(hdc, infoPtr->items[curItem].pszText,
1167 lstrlenW(infoPtr->items[curItem].pszText), &size);
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 + BUTTON_SPACINGX;
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( infoPtr->hwndNotify, 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 */
1550 INT center_offset_h, center_offset_v;
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)
1589 center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1590 center_offset_v = ((drawRect->right - drawRect->left) - (cx + infoPtr->uVItemPadding)) / 2;
1594 center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1595 center_offset_v = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uVItemPadding)) / 2;
1598 if ((lStyle & TCS_FIXEDWIDTH &&
1599 lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT)) ||
1600 (center_offset_h < 0))
1601 center_offset_h = 0;
1603 TRACE("for <%s>, c_o=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1604 debugstr_w(infoPtr->items[iItem].pszText), center_offset_h,
1605 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1606 (rcText.right-rcText.left));
1608 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1610 rcImage.top = drawRect->top + center_offset_h;
1611 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1612 /* right side of the tab, but the image still uses the left as its x position */
1613 /* this keeps the image always drawn off of the same side of the tab */
1614 rcImage.left = drawRect->right - cx - center_offset_v;
1615 drawRect->top = rcImage.top + (cx + infoPtr->uHItemPadding);
1617 else if(lStyle & TCS_VERTICAL)
1619 rcImage.top = drawRect->bottom - cy - center_offset_h;
1620 rcImage.left = drawRect->left + center_offset_v;
1621 drawRect->bottom = rcImage.top - infoPtr->uHItemPadding;
1623 else /* normal style, whether TCS_BOTTOM or not */
1625 rcImage.left = drawRect->left + center_offset_h + 3;
1626 drawRect->left = rcImage.left + cx + infoPtr->uHItemPadding;
1627 rcImage.top = drawRect->top + center_offset_v;
1630 TRACE("drawing image=%d, left=%ld, top=%ld\n",
1631 infoPtr->items[iItem].iImage, rcImage.left, rcImage.top-1);
1635 infoPtr->items[iItem].iImage,
1642 else /* no image, so just shift the drawRect borders around */
1644 if(lStyle & TCS_VERTICAL)
1646 center_offset_h = 0;
1648 currently the rcText rect is flawed because the rotated font does not
1649 often match the horizontal font. So leave this as 0
1650 ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1652 if(lStyle & TCS_BOTTOM)
1653 drawRect->top+=center_offset_h;
1655 drawRect->bottom-=center_offset_h;
1659 center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1660 drawRect->left+=center_offset_h;
1664 if(lStyle & TCS_VERTICAL)
1666 center_offset_v = ((drawRect->right - drawRect->left) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1667 drawRect->left += center_offset_v;
1671 center_offset_v = ((drawRect->bottom - drawRect->top) - ((rcText.bottom - rcText.top) + infoPtr->uVItemPadding)) / 2;
1672 drawRect->top += center_offset_v;
1677 if ((lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT) ||
1679 uHorizAlign = DT_LEFT;
1681 uHorizAlign = DT_CENTER;
1683 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1685 if(lStyle & TCS_BOTTOM)
1688 nOrientation = -900;
1697 /* to get a font with the escapement and orientation we are looking for, we need to */
1698 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1699 if(lStyle & TCS_VERTICAL)
1701 if (!GetObjectA((infoPtr->hFont) ?
1702 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1703 sizeof(LOGFONTA),&logfont))
1707 lstrcpyA(logfont.lfFaceName, "Arial");
1708 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1710 logfont.lfWeight = FW_NORMAL;
1711 logfont.lfItalic = 0;
1712 logfont.lfUnderline = 0;
1713 logfont.lfStrikeOut = 0;
1716 logfont.lfEscapement = nEscapement;
1717 logfont.lfOrientation = nOrientation;
1718 hFont = CreateFontIndirectA(&logfont);
1719 hOldFont = SelectObject(hdc, hFont);
1722 if (lStyle & TCS_VERTICAL)
1725 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1726 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1729 infoPtr->items[iItem].pszText,
1730 lstrlenW(infoPtr->items[iItem].pszText),
1738 infoPtr->items[iItem].pszText,
1739 lstrlenW(infoPtr->items[iItem].pszText),
1741 uHorizAlign | DT_SINGLELINE
1745 /* clean things up */
1746 *drawRect = rcTemp; /* restore drawRect */
1748 if(lStyle & TCS_VERTICAL)
1750 SelectObject(hdc, hOldFont); /* restore the original font */
1752 DeleteObject(hFont);
1759 SetBkMode(hdc, oldBkMode);
1760 SelectObject(hdc, holdPen);
1761 DeleteObject( htextPen );
1764 /******************************************************************************
1767 * This method is used to draw a single tab into the tab control.
1769 static void TAB_DrawItem(
1774 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1775 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1779 RECT r, fillRect, r1;
1782 COLORREF bkgnd, corner;
1785 * Get the rectangle for the item.
1787 isVisible = TAB_InternalGetItemRect(hwnd,
1795 /* If you need to see what the control is doing,
1796 * then override these variables. They will change what
1797 * fill colors are used for filling the tabs, and the
1798 * corners when drawing the edge.
1800 bkgnd = comctl32_color.clrBtnFace;
1801 corner = comctl32_color.clrBtnFace;
1803 if (lStyle & TCS_BUTTONS)
1805 HBRUSH hbr = CreateSolidBrush (bkgnd);
1806 BOOL deleteBrush = TRUE;
1808 /* Get item rectangle */
1811 /* Separators between flat buttons */
1812 if (lStyle & TCS_FLATBUTTONS)
1815 r1.right += (FLAT_BTN_SPACINGX -2);
1816 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1819 if (iItem == infoPtr->iSelected)
1821 /* Background color */
1822 if (!(lStyle & TCS_OWNERDRAWFIXED))
1825 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1827 SetTextColor(hdc, comctl32_color.clr3dFace);
1828 SetBkColor(hdc, comctl32_color.clr3dHilight);
1830 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1831 * we better use 0x55aa bitmap brush to make scrollbar's background
1832 * look different from the window background.
1834 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1835 hbr = COMCTL32_hPattern55AABrush;
1837 deleteBrush = FALSE;
1840 /* Clear interior */
1841 FillRect(hdc, &r, hbr);
1843 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1845 else /* ! selected */
1847 if (!(lStyle & TCS_FLATBUTTONS))
1849 /* Clear interior */
1850 FillRect(hdc, &r, hbr);
1852 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1857 if (deleteBrush) DeleteObject(hbr);
1859 else /* !TCS_BUTTONS */
1861 /* We draw a rectangle of different sizes depending on the selection
1863 if (iItem == infoPtr->iSelected) {
1865 GetClientRect (hwnd, &rect);
1866 clRight = rect.right;
1867 clBottom = rect.bottom;
1874 * Erase the background. (Delay it but setup rectangle.)
1875 * This is necessary when drawing the selected item since it is larger
1876 * than the others, it might overlap with stuff already drawn by the
1881 if(lStyle & TCS_VERTICAL)
1883 /* These are for adjusting the drawing of a Selected tab */
1884 /* The initial values are for the normal case of non-Selected */
1885 int ZZ = 1; /* Do not strech if selected */
1886 if (iItem == infoPtr->iSelected) {
1889 /* if leftmost draw the line longer */
1890 if(selectedRect.top == 0)
1892 /* if rightmost draw the line longer */
1893 if(selectedRect.bottom == clBottom)
1894 fillRect.bottom -= 2;
1897 if (lStyle & TCS_BOTTOM)
1899 /* Adjust both rectangles to match native */
1902 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1904 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1905 r.left,r.top,r.right,r.bottom);
1907 /* Clear interior */
1908 SetBkColor(hdc, bkgnd);
1909 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1911 /* Draw rectangular edge around tab */
1912 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
1914 /* Now erase the top corner and draw diagonal edge */
1915 SetBkColor(hdc, corner);
1916 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1919 r1.bottom = r1.top + ROUND_CORNER_SIZE;
1920 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1922 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
1924 /* Now erase the bottom corner and draw diagonal edge */
1925 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1926 r1.bottom = r.bottom;
1928 r1.top = r1.bottom - ROUND_CORNER_SIZE;
1929 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1931 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
1933 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
1937 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
1943 /* Adjust both rectangles to match native */
1944 fillRect.right += (1-ZZ);
1946 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1948 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1949 r.left,r.top,r.right,r.bottom);
1951 /* Clear interior */
1952 SetBkColor(hdc, bkgnd);
1953 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1955 /* Draw rectangular edge around tab */
1956 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
1958 /* Now erase the top corner and draw diagonal edge */
1959 SetBkColor(hdc, corner);
1962 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
1963 r1.bottom = r1.top + ROUND_CORNER_SIZE;
1964 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1966 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
1968 /* Now erase the bottom corner and draw diagonal edge */
1970 r1.bottom = r.bottom;
1971 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
1972 r1.top = r1.bottom - ROUND_CORNER_SIZE;
1973 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1975 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
1978 else /* ! TCS_VERTICAL */
1980 /* These are for adjusting the drawing of a Selected tab */
1981 /* The initial values are for the normal case of non-Selected */
1982 int ZZ = 1; /* Do not strech if selected */
1983 if (iItem == infoPtr->iSelected) {
1986 /* if leftmost draw the line longer */
1987 if(selectedRect.left == 0)
1989 /* if rightmost draw the line longer */
1990 if(selectedRect.right == clRight)
1991 fillRect.right -= 2;
1994 if (lStyle & TCS_BOTTOM)
1997 /* Adjust both rectangles to match native */
2003 TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2005 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2006 r.left,r.top,r.right,r.bottom);
2008 /* Clear interior */
2009 SetBkColor(hdc, bkgnd);
2010 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2012 /* Draw rectangular edge around tab */
2013 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2015 /* Now erase the righthand corner and draw diagonal edge */
2016 SetBkColor(hdc, corner);
2017 r1.left = r.right - ROUND_CORNER_SIZE;
2018 r1.bottom = r.bottom;
2020 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2021 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2023 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2025 /* Now erase the lefthand corner and draw diagonal edge */
2027 r1.bottom = r.bottom;
2028 r1.right = r1.left + ROUND_CORNER_SIZE;
2029 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2030 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2032 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2034 if ((iItem == infoPtr->iSelected) && (selectedRect.left == 0)) {
2038 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2045 /* Adjust both rectangles to match native */
2046 fillRect.bottom += (1-ZZ);
2048 TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2050 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2051 r.left,r.top,r.right,r.bottom);
2053 /* Clear interior */
2054 SetBkColor(hdc, bkgnd);
2055 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2057 /* Draw rectangular edge around tab */
2058 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2060 /* Now erase the righthand corner and draw diagonal edge */
2061 SetBkColor(hdc, corner);
2062 r1.left = r.right - ROUND_CORNER_SIZE;
2065 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2066 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2068 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2070 /* Now erase the lefthand corner and draw diagonal edge */
2073 r1.right = r1.left + ROUND_CORNER_SIZE;
2074 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2075 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2077 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2083 TAB_DumpItemInternal(infoPtr, iItem);
2085 /* This modifies r to be the text rectangle. */
2087 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
2088 TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
2089 SelectObject(hdc,hOldFont);
2092 /* Draw the focus rectangle */
2093 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
2094 (GetFocus() == hwnd) &&
2095 (iItem == infoPtr->uFocus) )
2098 InflateRect(&r, -1, -1);
2100 DrawFocusRect(hdc, &r);
2105 /******************************************************************************
2108 * This method is used to draw the raised border around the tab control
2111 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
2113 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2115 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2117 GetClientRect (hwnd, &rect);
2120 * Adjust for the style
2123 if (infoPtr->uNumItem)
2125 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2127 rect.bottom -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 3;
2129 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2131 rect.right -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2133 else if(lStyle & TCS_VERTICAL)
2135 rect.left += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2137 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2139 rect.top += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2143 TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2144 rect.left, rect.top, rect.right, rect.bottom);
2146 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2149 /******************************************************************************
2152 * This method repaints the tab control..
2154 static void TAB_Refresh (HWND hwnd, HDC hdc)
2156 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2160 if (!infoPtr->DoRedraw)
2163 hOldFont = SelectObject (hdc, infoPtr->hFont);
2165 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
2167 for (i = 0; i < infoPtr->uNumItem; i++)
2168 TAB_DrawItem (hwnd, hdc, i);
2172 /* Draw all the non selected item first */
2173 for (i = 0; i < infoPtr->uNumItem; i++)
2175 if (i != infoPtr->iSelected)
2176 TAB_DrawItem (hwnd, hdc, i);
2179 /* Now, draw the border, draw it before the selected item
2180 * since the selected item overwrites part of the border. */
2181 TAB_DrawBorder (hwnd, hdc);
2183 /* Then, draw the selected item */
2184 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
2186 /* If we haven't set the current focus yet, set it now.
2187 * Only happens when we first paint the tab controls */
2188 if (infoPtr->uFocus == -1)
2189 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
2192 SelectObject (hdc, hOldFont);
2196 TAB_GetRowCount (HWND hwnd )
2198 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2200 return infoPtr->uNumRows;
2204 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
2206 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2208 infoPtr->DoRedraw=(BOOL) wParam;
2212 static LRESULT TAB_EraseBackground(
2219 HBRUSH brush = CreateSolidBrush(comctl32_color.clrBtnFace);
2221 hdc = givenDC ? givenDC : GetDC(hwnd);
2223 GetClientRect(hwnd, &clientRect);
2225 FillRect(hdc, &clientRect, brush);
2228 ReleaseDC(hwnd, hdc);
2230 DeleteObject(brush);
2235 /******************************************************************************
2236 * TAB_EnsureSelectionVisible
2238 * This method will make sure that the current selection is completely
2239 * visible by scrolling until it is.
2241 static void TAB_EnsureSelectionVisible(
2245 INT iSelected = infoPtr->iSelected;
2246 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2247 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2249 /* set the items row to the bottommost row or topmost row depending on
2251 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2256 if(lStyle & TCS_VERTICAL)
2257 newselected = infoPtr->items[iSelected].rect.left;
2259 newselected = infoPtr->items[iSelected].rect.top;
2261 /* the target row is always (number of rows - 1)
2262 as row 0 is furthest from the clientRect */
2263 iTargetRow = infoPtr->uNumRows - 1;
2265 if (newselected != iTargetRow)
2268 if(lStyle & TCS_VERTICAL)
2270 for (i=0; i < infoPtr->uNumItem; i++)
2272 /* move everything in the row of the selected item to the iTargetRow */
2273 if (infoPtr->items[i].rect.left == newselected )
2274 infoPtr->items[i].rect.left = iTargetRow;
2277 if (infoPtr->items[i].rect.left > newselected)
2278 infoPtr->items[i].rect.left-=1;
2284 for (i=0; i < infoPtr->uNumItem; i++)
2286 if (infoPtr->items[i].rect.top == newselected )
2287 infoPtr->items[i].rect.top = iTargetRow;
2290 if (infoPtr->items[i].rect.top > newselected)
2291 infoPtr->items[i].rect.top-=1;
2295 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2300 * Do the trivial cases first.
2302 if ( (!infoPtr->needsScrolling) ||
2303 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2306 if (infoPtr->leftmostVisible >= iSelected)
2308 infoPtr->leftmostVisible = iSelected;
2315 /* Calculate the part of the client area that is visible */
2316 GetClientRect(hwnd, &r);
2319 GetClientRect(infoPtr->hwndUpDown, &r);
2322 if ((infoPtr->items[iSelected].rect.right -
2323 infoPtr->items[iSelected].rect.left) >= width )
2325 /* Special case: width of selected item is greater than visible
2328 infoPtr->leftmostVisible = iSelected;
2332 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2334 if ((infoPtr->items[iSelected].rect.right -
2335 infoPtr->items[i].rect.left) < width)
2338 infoPtr->leftmostVisible = i;
2342 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2343 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2345 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2346 MAKELONG(infoPtr->leftmostVisible, 0));
2349 /******************************************************************************
2350 * TAB_InvalidateTabArea
2352 * This method will invalidate the portion of the control that contains the
2353 * tabs. It is called when the state of the control changes and needs
2356 static void TAB_InvalidateTabArea(
2361 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2362 INT lastRow = infoPtr->uNumRows - 1;
2365 if (lastRow < 0) return;
2367 GetClientRect(hwnd, &clientRect);
2369 TAB_InternalGetItemRect(hwnd, infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2370 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2372 clientRect.top = clientRect.bottom -
2373 infoPtr->tabHeight -
2374 lastRow * (infoPtr->tabHeight - 2) -
2375 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 3;
2376 clientRect.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2378 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2380 clientRect.left = clientRect.right - infoPtr->tabHeight -
2381 lastRow * (infoPtr->tabHeight - 2) -
2382 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 2;
2383 clientRect.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2385 else if(lStyle & TCS_VERTICAL)
2387 clientRect.right = clientRect.left + infoPtr->tabHeight +
2388 lastRow * (infoPtr->tabHeight - 2) -
2389 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 2;
2390 clientRect.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2394 clientRect.bottom = clientRect.top + infoPtr->tabHeight +
2395 lastRow * (infoPtr->tabHeight - 2) +
2396 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 2;
2397 clientRect.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2400 /* Punch out the updown control */
2401 if (infoPtr->needsScrolling && (clientRect.right > 0)) {
2402 GetClientRect(infoPtr->hwndUpDown, &r);
2403 clientRect.right = clientRect.right - (r.right - r.left);
2406 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2407 clientRect.left,clientRect.top,
2408 clientRect.right,clientRect.bottom);
2410 InvalidateRect(hwnd, &clientRect, TRUE);
2414 TAB_Paint (HWND hwnd, WPARAM wParam)
2421 hdc = BeginPaint (hwnd, &ps);
2422 TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2424 ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2427 TAB_EraseBackground (hwnd, hdc);
2433 TAB_Refresh (hwnd, hdc);
2436 EndPaint (hwnd, &ps);
2442 TAB_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2444 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2449 GetClientRect (hwnd, &rect);
2450 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2451 rect.top, rect.left, rect.bottom, rect.right);
2453 pti = (TCITEMA *)lParam;
2454 iItem = (INT)wParam;
2456 if (iItem < 0) return -1;
2457 if (iItem > infoPtr->uNumItem)
2458 iItem = infoPtr->uNumItem;
2460 TAB_DumpItemExternalA(pti, iItem);
2463 if (infoPtr->uNumItem == 0) {
2464 infoPtr->items = Alloc (sizeof (TAB_ITEM));
2465 infoPtr->uNumItem++;
2466 infoPtr->iSelected = 0;
2469 TAB_ITEM *oldItems = infoPtr->items;
2471 infoPtr->uNumItem++;
2472 infoPtr->items = Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2474 /* pre insert copy */
2476 memcpy (&infoPtr->items[0], &oldItems[0],
2477 iItem * sizeof(TAB_ITEM));
2480 /* post insert copy */
2481 if (iItem < infoPtr->uNumItem - 1) {
2482 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2483 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2487 if (iItem <= infoPtr->iSelected)
2488 infoPtr->iSelected++;
2493 infoPtr->items[iItem].mask = pti->mask;
2494 if (pti->mask & TCIF_TEXT)
2495 Str_SetPtrAtoW (&infoPtr->items[iItem].pszText, pti->pszText);
2497 if (pti->mask & TCIF_IMAGE)
2498 infoPtr->items[iItem].iImage = pti->iImage;
2500 if (pti->mask & TCIF_PARAM)
2501 infoPtr->items[iItem].lParam = pti->lParam;
2503 TAB_SetItemBounds(hwnd);
2504 if (infoPtr->uNumItem > 1)
2505 TAB_InvalidateTabArea(hwnd, infoPtr);
2507 InvalidateRect(hwnd, NULL, TRUE);
2509 TRACE("[%p]: added item %d %s\n",
2510 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2517 TAB_InsertItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2519 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2524 GetClientRect (hwnd, &rect);
2525 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2526 rect.top, rect.left, rect.bottom, rect.right);
2528 pti = (TCITEMW *)lParam;
2529 iItem = (INT)wParam;
2531 if (iItem < 0) return -1;
2532 if (iItem > infoPtr->uNumItem)
2533 iItem = infoPtr->uNumItem;
2535 TAB_DumpItemExternalW(pti, iItem);
2537 if (infoPtr->uNumItem == 0) {
2538 infoPtr->items = Alloc (sizeof (TAB_ITEM));
2539 infoPtr->uNumItem++;
2540 infoPtr->iSelected = 0;
2543 TAB_ITEM *oldItems = infoPtr->items;
2545 infoPtr->uNumItem++;
2546 infoPtr->items = Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2548 /* pre insert copy */
2550 memcpy (&infoPtr->items[0], &oldItems[0],
2551 iItem * sizeof(TAB_ITEM));
2554 /* post insert copy */
2555 if (iItem < infoPtr->uNumItem - 1) {
2556 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2557 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2561 if (iItem <= infoPtr->iSelected)
2562 infoPtr->iSelected++;
2567 infoPtr->items[iItem].mask = pti->mask;
2568 if (pti->mask & TCIF_TEXT)
2569 Str_SetPtrW (&infoPtr->items[iItem].pszText, pti->pszText);
2571 if (pti->mask & TCIF_IMAGE)
2572 infoPtr->items[iItem].iImage = pti->iImage;
2574 if (pti->mask & TCIF_PARAM)
2575 infoPtr->items[iItem].lParam = pti->lParam;
2577 TAB_SetItemBounds(hwnd);
2578 if (infoPtr->uNumItem > 1)
2579 TAB_InvalidateTabArea(hwnd, infoPtr);
2581 InvalidateRect(hwnd, NULL, TRUE);
2583 TRACE("[%p]: added item %d %s\n",
2584 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2591 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2593 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2594 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2596 BOOL bNeedPaint = FALSE;
2598 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2600 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2601 if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam)))
2603 infoPtr->tabWidth = max((INT)LOWORD(lParam), infoPtr->tabMinWidth);
2607 if (infoPtr->tabHeight != (INT)HIWORD(lParam))
2609 if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0)))
2610 infoPtr->tabHeight = (INT)HIWORD(lParam);
2614 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2615 HIWORD(lResult), LOWORD(lResult),
2616 infoPtr->tabHeight, infoPtr->tabWidth);
2620 TAB_SetItemBounds(hwnd);
2621 RedrawWindow(hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2628 TAB_SetMinTabWidth (HWND hwnd, LPARAM lParam)
2630 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2631 INT cx = (INT)lParam;
2635 oldcx = infoPtr->tabMinWidth;
2636 infoPtr->tabMinWidth = (cx==-1)?DEFAULT_TAB_WIDTH:cx;
2644 TAB_HighlightItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2646 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2647 INT iItem = (INT)wParam;
2648 BOOL fHighlight = (BOOL)LOWORD(lParam);
2650 if ((infoPtr) && (iItem>=0) && (iItem<infoPtr->uNumItem)) {
2652 infoPtr->items[iItem].dwState |= TCIS_HIGHLIGHTED;
2654 infoPtr->items[iItem].dwState &= ~TCIS_HIGHLIGHTED;
2662 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2664 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2669 iItem = (INT)wParam;
2670 tabItem = (LPTCITEMA)lParam;
2672 TRACE("%d %p\n", iItem, tabItem);
2673 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2675 TAB_DumpItemExternalA(tabItem, iItem);
2677 wineItem = &infoPtr->items[iItem];
2679 if (tabItem->mask & TCIF_IMAGE)
2680 wineItem->iImage = tabItem->iImage;
2682 if (tabItem->mask & TCIF_PARAM)
2683 wineItem->lParam = tabItem->lParam;
2685 if (tabItem->mask & TCIF_RTLREADING)
2686 FIXME("TCIF_RTLREADING\n");
2688 if (tabItem->mask & TCIF_STATE)
2689 wineItem->dwState = tabItem->dwState;
2691 if (tabItem->mask & TCIF_TEXT)
2692 Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2694 /* Update and repaint tabs */
2695 TAB_SetItemBounds(hwnd);
2696 TAB_InvalidateTabArea(hwnd,infoPtr);
2703 TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2705 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2710 iItem = (INT)wParam;
2711 tabItem = (LPTCITEMW)lParam;
2713 TRACE("%d %p\n", iItem, tabItem);
2714 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2716 TAB_DumpItemExternalW(tabItem, iItem);
2718 wineItem = &infoPtr->items[iItem];
2720 if (tabItem->mask & TCIF_IMAGE)
2721 wineItem->iImage = tabItem->iImage;
2723 if (tabItem->mask & TCIF_PARAM)
2724 wineItem->lParam = tabItem->lParam;
2726 if (tabItem->mask & TCIF_RTLREADING)
2727 FIXME("TCIF_RTLREADING\n");
2729 if (tabItem->mask & TCIF_STATE)
2730 wineItem->dwState = tabItem->dwState;
2732 if (tabItem->mask & TCIF_TEXT)
2733 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2735 /* Update and repaint tabs */
2736 TAB_SetItemBounds(hwnd);
2737 TAB_InvalidateTabArea(hwnd,infoPtr);
2744 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2746 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2748 return infoPtr->uNumItem;
2753 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2755 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2760 iItem = (INT)wParam;
2761 tabItem = (LPTCITEMA)lParam;
2763 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2766 wineItem = &infoPtr->items[iItem];
2768 if (tabItem->mask & TCIF_IMAGE)
2769 tabItem->iImage = wineItem->iImage;
2771 if (tabItem->mask & TCIF_PARAM)
2772 tabItem->lParam = wineItem->lParam;
2774 if (tabItem->mask & TCIF_RTLREADING)
2775 FIXME("TCIF_RTLREADING\n");
2777 if (tabItem->mask & TCIF_STATE)
2778 tabItem->dwState = wineItem->dwState;
2780 if (tabItem->mask & TCIF_TEXT)
2781 Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2783 TAB_DumpItemExternalA(tabItem, iItem);
2790 TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2792 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2797 iItem = (INT)wParam;
2798 tabItem = (LPTCITEMW)lParam;
2800 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2803 wineItem=& infoPtr->items[iItem];
2805 if (tabItem->mask & TCIF_IMAGE)
2806 tabItem->iImage = wineItem->iImage;
2808 if (tabItem->mask & TCIF_PARAM)
2809 tabItem->lParam = wineItem->lParam;
2811 if (tabItem->mask & TCIF_RTLREADING)
2812 FIXME("TCIF_RTLREADING\n");
2814 if (tabItem->mask & TCIF_STATE)
2815 tabItem->dwState = wineItem->dwState;
2817 if (tabItem->mask & TCIF_TEXT)
2818 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2820 TAB_DumpItemExternalW(tabItem, iItem);
2827 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2829 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2830 INT iItem = (INT) wParam;
2831 BOOL bResult = FALSE;
2833 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2835 TAB_ITEM *oldItems = infoPtr->items;
2837 TAB_InvalidateTabArea(hwnd, infoPtr);
2839 infoPtr->uNumItem--;
2840 infoPtr->items = Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
2843 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
2845 if (iItem < infoPtr->uNumItem)
2846 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
2847 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
2851 /* Readjust the selected index */
2852 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2853 infoPtr->iSelected--;
2855 if (iItem < infoPtr->iSelected)
2856 infoPtr->iSelected--;
2858 if (infoPtr->uNumItem == 0)
2859 infoPtr->iSelected = -1;
2861 /* Reposition and repaint tabs */
2862 TAB_SetItemBounds(hwnd);
2871 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2873 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2875 TAB_InvalidateTabArea(hwnd,infoPtr);
2877 Free (infoPtr->items);
2878 infoPtr->uNumItem = 0;
2879 infoPtr->iSelected = -1;
2880 if (infoPtr->iHotTracked >= 0)
2881 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2882 infoPtr->iHotTracked = -1;
2884 TAB_SetItemBounds(hwnd);
2890 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2892 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2895 return (LRESULT)infoPtr->hFont;
2899 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2902 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2904 TRACE("%x %lx\n",wParam, lParam);
2906 infoPtr->hFont = (HFONT)wParam;
2908 TAB_SetItemBounds(hwnd);
2910 TAB_InvalidateTabArea(hwnd, infoPtr);
2917 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2919 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2922 return (LRESULT)infoPtr->himl;
2926 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2928 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2929 HIMAGELIST himlPrev;
2932 himlPrev = infoPtr->himl;
2933 infoPtr->himl= (HIMAGELIST)lParam;
2934 return (LRESULT)himlPrev;
2938 TAB_GetUnicodeFormat (HWND hwnd)
2940 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2941 return infoPtr->bUnicode;
2945 TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
2947 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2948 BOOL bTemp = infoPtr->bUnicode;
2950 infoPtr->bUnicode = (BOOL)wParam;
2956 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2959 /* I'm not really sure what the following code was meant to do.
2960 This is what it is doing:
2961 When WM_SIZE is sent with SIZE_RESTORED, the control
2962 gets positioned in the top left corner.
2966 UINT uPosFlags,cx,cy;
2970 parent = GetParent (hwnd);
2971 GetClientRect(parent, &parent_rect);
2974 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2975 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2977 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2978 cx, cy, uPosFlags | SWP_NOZORDER);
2980 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2983 /* Recompute the size/position of the tabs. */
2984 TAB_SetItemBounds (hwnd);
2986 /* Force a repaint of the control. */
2987 InvalidateRect(hwnd, NULL, TRUE);
2994 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2997 TEXTMETRICA fontMetrics;
3002 infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO));
3004 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
3006 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
3007 infoPtr->uNumItem = 0;
3008 infoPtr->uNumRows = 0;
3009 infoPtr->uHItemPadding = 6;
3010 infoPtr->uVItemPadding = 3;
3013 infoPtr->hcurArrow = LoadCursorA (0, (LPSTR)IDC_ARROW);
3014 infoPtr->iSelected = -1;
3015 infoPtr->iHotTracked = -1;
3016 infoPtr->uFocus = -1;
3017 infoPtr->hwndToolTip = 0;
3018 infoPtr->DoRedraw = TRUE;
3019 infoPtr->needsScrolling = FALSE;
3020 infoPtr->hwndUpDown = 0;
3021 infoPtr->leftmostVisible = 0;
3022 infoPtr->fHeightSet = FALSE;
3023 infoPtr->bUnicode = IsWindowUnicode (hwnd);
3025 TRACE("Created tab control, hwnd [%p]\n", hwnd);
3027 /* The tab control always has the WS_CLIPSIBLINGS style. Even
3028 if you don't specify it in CreateWindow. This is necessary in
3029 order for paint to work correctly. This follows windows behaviour. */
3030 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
3031 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
3033 if (dwStyle & TCS_TOOLTIPS) {
3034 /* Create tooltip control */
3035 infoPtr->hwndToolTip =
3036 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
3037 CW_USEDEFAULT, CW_USEDEFAULT,
3038 CW_USEDEFAULT, CW_USEDEFAULT,
3041 /* Send NM_TOOLTIPSCREATED notification */
3042 if (infoPtr->hwndToolTip) {
3043 NMTOOLTIPSCREATED nmttc;
3045 nmttc.hdr.hwndFrom = hwnd;
3046 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
3047 nmttc.hdr.code = NM_TOOLTIPSCREATED;
3048 nmttc.hwndToolTips = infoPtr->hwndToolTip;
3050 SendMessageA (infoPtr->hwndNotify, WM_NOTIFY,
3051 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
3056 * We need to get text information so we need a DC and we need to select
3060 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3062 /* Use the system font to determine the initial height of a tab. */
3063 GetTextMetricsA(hdc, &fontMetrics);
3066 * Make sure there is enough space for the letters + growing the
3067 * selected item + extra space for the selected item.
3069 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3070 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
3071 infoPtr->uVItemPadding;
3073 /* Initialize the width of a tab. */
3074 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
3075 infoPtr->tabMinWidth = 0;
3077 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3079 SelectObject (hdc, hOldFont);
3080 ReleaseDC(hwnd, hdc);
3086 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
3088 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3094 if (infoPtr->items) {
3095 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3096 if (infoPtr->items[iItem].pszText)
3097 Free (infoPtr->items[iItem].pszText);
3099 Free (infoPtr->items);
3102 if (infoPtr->hwndToolTip)
3103 DestroyWindow (infoPtr->hwndToolTip);
3105 if (infoPtr->hwndUpDown)
3106 DestroyWindow(infoPtr->hwndUpDown);
3108 if (infoPtr->iHotTracked >= 0)
3109 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
3112 SetWindowLongA(hwnd, 0, 0);
3116 static LRESULT WINAPI
3117 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3119 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3121 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3122 if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
3123 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3127 case TCM_GETIMAGELIST:
3128 return TAB_GetImageList (hwnd, wParam, lParam);
3130 case TCM_SETIMAGELIST:
3131 return TAB_SetImageList (hwnd, wParam, lParam);
3133 case TCM_GETITEMCOUNT:
3134 return TAB_GetItemCount (hwnd, wParam, lParam);
3137 return TAB_GetItemA (hwnd, wParam, lParam);
3140 return TAB_GetItemW (hwnd, wParam, lParam);
3143 return TAB_SetItemA (hwnd, wParam, lParam);
3146 return TAB_SetItemW (hwnd, wParam, lParam);
3148 case TCM_DELETEITEM:
3149 return TAB_DeleteItem (hwnd, wParam, lParam);
3151 case TCM_DELETEALLITEMS:
3152 return TAB_DeleteAllItems (hwnd, wParam, lParam);
3154 case TCM_GETITEMRECT:
3155 return TAB_GetItemRect (hwnd, wParam, lParam);
3158 return TAB_GetCurSel (hwnd);
3161 return TAB_HitTest (hwnd, wParam, lParam);
3164 return TAB_SetCurSel (hwnd, wParam);
3166 case TCM_INSERTITEMA:
3167 return TAB_InsertItemA (hwnd, wParam, lParam);
3169 case TCM_INSERTITEMW:
3170 return TAB_InsertItemW (hwnd, wParam, lParam);
3172 case TCM_SETITEMEXTRA:
3173 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
3176 case TCM_ADJUSTRECT:
3177 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
3179 case TCM_SETITEMSIZE:
3180 return TAB_SetItemSize (hwnd, wParam, lParam);
3182 case TCM_REMOVEIMAGE:
3183 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3186 case TCM_SETPADDING:
3187 return TAB_SetPadding (hwnd, wParam, lParam);
3189 case TCM_GETROWCOUNT:
3190 return TAB_GetRowCount(hwnd);
3192 case TCM_GETUNICODEFORMAT:
3193 return TAB_GetUnicodeFormat (hwnd);
3195 case TCM_SETUNICODEFORMAT:
3196 return TAB_SetUnicodeFormat (hwnd, wParam);
3198 case TCM_HIGHLIGHTITEM:
3199 return TAB_HighlightItem (hwnd, wParam, lParam);
3201 case TCM_GETTOOLTIPS:
3202 return TAB_GetToolTips (hwnd, wParam, lParam);
3204 case TCM_SETTOOLTIPS:
3205 return TAB_SetToolTips (hwnd, wParam, lParam);
3207 case TCM_GETCURFOCUS:
3208 return TAB_GetCurFocus (hwnd);
3210 case TCM_SETCURFOCUS:
3211 return TAB_SetCurFocus (hwnd, wParam);
3213 case TCM_SETMINTABWIDTH:
3214 return TAB_SetMinTabWidth(hwnd, lParam);
3216 case TCM_DESELECTALL:
3217 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3220 case TCM_GETEXTENDEDSTYLE:
3221 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3224 case TCM_SETEXTENDEDSTYLE:
3225 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3229 return TAB_GetFont (hwnd, wParam, lParam);
3232 return TAB_SetFont (hwnd, wParam, lParam);
3235 return TAB_Create (hwnd, wParam, lParam);
3238 return TAB_Destroy (hwnd, wParam, lParam);
3241 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3243 case WM_LBUTTONDOWN:
3244 return TAB_LButtonDown (hwnd, wParam, lParam);
3247 return TAB_LButtonUp (hwnd, wParam, lParam);
3250 return SendMessageA(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3252 case WM_RBUTTONDOWN:
3253 return TAB_RButtonDown (hwnd, wParam, lParam);
3256 return TAB_MouseMove (hwnd, wParam, lParam);
3259 return TAB_EraseBackground (hwnd, (HDC)wParam);
3262 return TAB_Paint (hwnd, wParam);
3265 return TAB_Size (hwnd, wParam, lParam);
3268 return TAB_SetRedraw (hwnd, wParam);
3271 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3273 case WM_STYLECHANGED:
3274 TAB_SetItemBounds (hwnd);
3275 InvalidateRect(hwnd, NULL, TRUE);
3278 case WM_SYSCOLORCHANGE:
3279 COMCTL32_RefreshSysColors();
3284 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
3287 return TAB_KeyUp(hwnd, wParam);
3289 return TAB_NCHitTest(hwnd, lParam);
3292 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
3293 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3294 uMsg, wParam, lParam);
3295 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
3307 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3308 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3309 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
3310 wndClass.cbClsExtra = 0;
3311 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3312 wndClass.hCursor = LoadCursorA (0, (LPSTR)IDC_ARROW);
3313 wndClass.hbrBackground = NULL;
3314 wndClass.lpszClassName = WC_TABCONTROLA;
3316 RegisterClassA (&wndClass);
3321 TAB_Unregister (void)
3323 UnregisterClassA (WC_TABCONTROLA, NULL);