wer: Add new stubbed wer.dll.
[wine/hramrach.git] / dlls / comctl32 / tab.c
blob8807a113047b852d9e70f37d182fe3e66b6ff160
1 /*
2 * Tab control
4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
7 * Copyright 2003 Vitaliy Margolen
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 * NOTES
25 * This code was audited for completeness against the documented features
26 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
28 * Unless otherwise noted, we believe this code to be complete, as per
29 * the specification mentioned above.
30 * If you discover missing features, or bugs, please note them below.
32 * TODO:
34 * Styles:
35 * TCS_MULTISELECT - implement for VK_SPACE selection
36 * TCS_RIGHT
37 * TCS_RIGHTJUSTIFY
38 * TCS_SCROLLOPPOSITE
39 * TCS_SINGLELINE
40 * TCIF_RTLREADING
42 * Extended Styles:
43 * TCS_EX_REGISTERDROP
45 * Notifications:
46 * NM_RELEASEDCAPTURE
47 * TCN_FOCUSCHANGE
48 * TCN_GETOBJECT
50 * Macros:
51 * TabCtrl_AdjustRect
55 #include <stdarg.h>
56 #include <string.h>
58 #include "windef.h"
59 #include "winbase.h"
60 #include "wingdi.h"
61 #include "winuser.h"
62 #include "winnls.h"
63 #include "commctrl.h"
64 #include "comctl32.h"
65 #include "uxtheme.h"
66 #include "tmschema.h"
67 #include "wine/debug.h"
68 #include <math.h>
70 WINE_DEFAULT_DEBUG_CHANNEL(tab);
72 typedef struct
74 DWORD dwState;
75 LPWSTR pszText;
76 INT iImage;
77 RECT rect; /* bounding rectangle of the item relative to the
78 * leftmost item (the leftmost item, 0, would have a
79 * "left" member of 0 in this rectangle)
81 * additionally the top member holds the row number
82 * and bottom is unused and should be 0 */
83 BYTE extra[1]; /* Space for caller supplied info, variable size */
84 } TAB_ITEM;
86 /* The size of a tab item depends on how much extra data is requested */
87 #define TAB_ITEM_SIZE(infoPtr) (FIELD_OFFSET(TAB_ITEM, extra[(infoPtr)->cbInfo]))
89 typedef struct
91 HWND hwnd; /* Tab control window */
92 HWND hwndNotify; /* notification window (parent) */
93 UINT uNumItem; /* number of tab items */
94 UINT uNumRows; /* number of tab rows */
95 INT tabHeight; /* height of the tab row */
96 INT tabWidth; /* width of tabs */
97 INT tabMinWidth; /* minimum width of items */
98 USHORT uHItemPadding; /* amount of horizontal padding, in pixels */
99 USHORT uVItemPadding; /* amount of vertical padding, in pixels */
100 USHORT uHItemPadding_s; /* Set amount of horizontal padding, in pixels */
101 USHORT uVItemPadding_s; /* Set amount of vertical padding, in pixels */
102 HFONT hFont; /* handle to the current font */
103 HCURSOR hcurArrow; /* handle to the current cursor */
104 HIMAGELIST himl; /* handle to an image list (may be 0) */
105 HWND hwndToolTip; /* handle to tab's tooltip */
106 INT leftmostVisible; /* Used for scrolling, this member contains
107 * the index of the first visible item */
108 INT iSelected; /* the currently selected item */
109 INT iHotTracked; /* the highlighted item under the mouse */
110 INT uFocus; /* item which has the focus */
111 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
112 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
113 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
114 * the size of the control */
115 BOOL fHeightSet; /* was the height of the tabs explicitly set? */
116 BOOL bUnicode; /* Unicode control? */
117 HWND hwndUpDown; /* Updown control used for scrolling */
118 INT cbInfo; /* Number of bytes of caller supplied info per tab */
120 DWORD exStyle; /* Extended style used, currently:
121 TCS_EX_FLATSEPARATORS, TCS_EX_REGISTERDROP */
122 DWORD dwStyle; /* the cached window GWL_STYLE */
123 } TAB_INFO;
125 /******************************************************************************
126 * Positioning constants
128 #define SELECTED_TAB_OFFSET 2
129 #define ROUND_CORNER_SIZE 2
130 #define DISPLAY_AREA_PADDINGX 2
131 #define DISPLAY_AREA_PADDINGY 2
132 #define CONTROL_BORDER_SIZEX 2
133 #define CONTROL_BORDER_SIZEY 2
134 #define BUTTON_SPACINGX 3
135 #define BUTTON_SPACINGY 3
136 #define FLAT_BTN_SPACINGX 8
137 #define DEFAULT_MIN_TAB_WIDTH 54
138 #define DEFAULT_PADDING_X 6
139 #define EXTRA_ICON_PADDING 3
141 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongPtrW(hwnd,0))
142 /* Since items are variable sized, cannot directly access them */
143 #define TAB_GetItem(info,i) \
144 ((TAB_ITEM*)((LPBYTE)info->items + (i) * TAB_ITEM_SIZE(info)))
146 #define GET_DEFAULT_MIN_TAB_WIDTH(infoPtr) (DEFAULT_MIN_TAB_WIDTH - (DEFAULT_PADDING_X - (infoPtr)->uHItemPadding) * 2)
148 /******************************************************************************
149 * Hot-tracking timer constants
151 #define TAB_HOTTRACK_TIMER 1
152 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
154 static const WCHAR themeClass[] = { 'T','a','b',0 };
156 /******************************************************************************
157 * Prototypes
159 static void TAB_InvalidateTabArea(const TAB_INFO *);
160 static void TAB_EnsureSelectionVisible(TAB_INFO *);
161 static void TAB_DrawItemInterior(const TAB_INFO *, HDC, INT, RECT*);
162 static LRESULT TAB_DeselectAll(TAB_INFO *, BOOL);
163 static BOOL TAB_InternalGetItemRect(const TAB_INFO *, INT, RECT*, RECT*);
165 static BOOL
166 TAB_SendSimpleNotify (const TAB_INFO *infoPtr, UINT code)
168 NMHDR nmhdr;
170 nmhdr.hwndFrom = infoPtr->hwnd;
171 nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID);
172 nmhdr.code = code;
174 return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
175 nmhdr.idFrom, (LPARAM) &nmhdr);
178 static void
179 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
180 WPARAM wParam, LPARAM lParam)
182 MSG msg;
184 msg.hwnd = hwndMsg;
185 msg.message = uMsg;
186 msg.wParam = wParam;
187 msg.lParam = lParam;
188 msg.time = GetMessageTime ();
189 msg.pt.x = (short)LOWORD(GetMessagePos ());
190 msg.pt.y = (short)HIWORD(GetMessagePos ());
192 SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
195 static void
196 TAB_DumpItemExternalT(const TCITEMW *pti, UINT iItem, BOOL isW)
198 if (TRACE_ON(tab)) {
199 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
200 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
201 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
202 iItem, pti->iImage, pti->lParam, isW ? debugstr_w(pti->pszText) : debugstr_a((LPSTR)pti->pszText));
206 static void
207 TAB_DumpItemInternal(const TAB_INFO *infoPtr, UINT iItem)
209 if (TRACE_ON(tab)) {
210 TAB_ITEM *ti;
212 ti = TAB_GetItem(infoPtr, iItem);
213 TRACE("tab %d, dwState=0x%08x, pszText=%s, iImage=%d\n",
214 iItem, ti->dwState, debugstr_w(ti->pszText), ti->iImage);
215 TRACE("tab %d, rect.left=%d, rect.top(row)=%d\n",
216 iItem, ti->rect.left, ti->rect.top);
220 /* RETURNS
221 * the index of the selected tab, or -1 if no tab is selected. */
222 static inline LRESULT TAB_GetCurSel (const TAB_INFO *infoPtr)
224 TRACE("(%p)\n", infoPtr);
225 return infoPtr->iSelected;
228 /* RETURNS
229 * the index of the tab item that has the focus. */
230 static inline LRESULT
231 TAB_GetCurFocus (const TAB_INFO *infoPtr)
233 TRACE("(%p)\n", infoPtr);
234 return infoPtr->uFocus;
237 static inline LRESULT TAB_GetToolTips (const TAB_INFO *infoPtr)
239 TRACE("(%p)\n", infoPtr);
240 return (LRESULT)infoPtr->hwndToolTip;
243 static inline LRESULT TAB_SetCurSel (TAB_INFO *infoPtr, INT iItem)
245 INT prevItem = infoPtr->iSelected;
247 TRACE("(%p %d)\n", infoPtr, iItem);
249 if (iItem < 0)
250 infoPtr->iSelected = -1;
251 else if (iItem >= infoPtr->uNumItem)
252 return -1;
253 else {
254 if (prevItem != iItem) {
255 if (prevItem != -1)
256 TAB_GetItem(infoPtr, prevItem)->dwState &= ~TCIS_BUTTONPRESSED;
257 TAB_GetItem(infoPtr, iItem)->dwState |= TCIS_BUTTONPRESSED;
259 infoPtr->iSelected = iItem;
260 infoPtr->uFocus = iItem;
261 TAB_EnsureSelectionVisible(infoPtr);
262 TAB_InvalidateTabArea(infoPtr);
265 return prevItem;
268 static LRESULT TAB_SetCurFocus (TAB_INFO *infoPtr, INT iItem)
270 TRACE("(%p %d)\n", infoPtr, iItem);
272 if (iItem < 0) {
273 infoPtr->uFocus = -1;
274 if (infoPtr->iSelected != -1) {
275 infoPtr->iSelected = -1;
276 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
277 TAB_InvalidateTabArea(infoPtr);
280 else if (iItem < infoPtr->uNumItem) {
281 if (infoPtr->dwStyle & TCS_BUTTONS) {
282 /* set focus to new item, leave selection as is */
283 if (infoPtr->uFocus != iItem) {
284 INT prev_focus = infoPtr->uFocus;
285 RECT r;
287 infoPtr->uFocus = iItem;
289 if (prev_focus != infoPtr->iSelected) {
290 if (TAB_InternalGetItemRect(infoPtr, prev_focus, &r, NULL))
291 InvalidateRect(infoPtr->hwnd, &r, FALSE);
294 if (TAB_InternalGetItemRect(infoPtr, iItem, &r, NULL))
295 InvalidateRect(infoPtr->hwnd, &r, FALSE);
297 TAB_SendSimpleNotify(infoPtr, TCN_FOCUSCHANGE);
299 } else {
300 INT oldFocus = infoPtr->uFocus;
301 if (infoPtr->iSelected != iItem || oldFocus == -1 ) {
302 infoPtr->uFocus = iItem;
303 if (oldFocus != -1) {
304 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING)) {
305 infoPtr->iSelected = iItem;
306 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
308 else
309 infoPtr->iSelected = iItem;
310 TAB_EnsureSelectionVisible(infoPtr);
311 TAB_InvalidateTabArea(infoPtr);
316 return 0;
319 static inline LRESULT
320 TAB_SetToolTips (TAB_INFO *infoPtr, HWND hwndToolTip)
322 TRACE("%p %p\n", infoPtr, hwndToolTip);
323 infoPtr->hwndToolTip = hwndToolTip;
324 return 0;
327 static inline LRESULT
328 TAB_SetPadding (TAB_INFO *infoPtr, LPARAM lParam)
330 TRACE("(%p %d %d)\n", infoPtr, LOWORD(lParam), HIWORD(lParam));
331 infoPtr->uHItemPadding_s = LOWORD(lParam);
332 infoPtr->uVItemPadding_s = HIWORD(lParam);
334 return 0;
337 /******************************************************************************
338 * TAB_InternalGetItemRect
340 * This method will calculate the rectangle representing a given tab item in
341 * client coordinates. This method takes scrolling into account.
343 * This method returns TRUE if the item is visible in the window and FALSE
344 * if it is completely outside the client area.
346 static BOOL TAB_InternalGetItemRect(
347 const TAB_INFO* infoPtr,
348 INT itemIndex,
349 RECT* itemRect,
350 RECT* selectedRect)
352 RECT tmpItemRect,clientRect;
354 /* Perform a sanity check and a trivial visibility check. */
355 if ( (infoPtr->uNumItem <= 0) ||
356 (itemIndex >= infoPtr->uNumItem) ||
357 (!(((infoPtr->dwStyle & TCS_MULTILINE) || (infoPtr->dwStyle & TCS_VERTICAL))) &&
358 (itemIndex < infoPtr->leftmostVisible)))
360 TRACE("Not Visible\n");
361 /* need to initialize these to empty rects */
362 if (itemRect)
364 memset(itemRect,0,sizeof(RECT));
365 itemRect->bottom = infoPtr->tabHeight;
367 if (selectedRect)
368 memset(selectedRect,0,sizeof(RECT));
369 return FALSE;
373 * Avoid special cases in this procedure by assigning the "out"
374 * parameters if the caller didn't supply them
376 if (itemRect == NULL)
377 itemRect = &tmpItemRect;
379 /* Retrieve the unmodified item rect. */
380 *itemRect = TAB_GetItem(infoPtr,itemIndex)->rect;
382 /* calculate the times bottom and top based on the row */
383 GetClientRect(infoPtr->hwnd, &clientRect);
385 if ((infoPtr->dwStyle & TCS_BOTTOM) && (infoPtr->dwStyle & TCS_VERTICAL))
387 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * infoPtr->tabHeight -
388 ((infoPtr->dwStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
389 itemRect->left = itemRect->right - infoPtr->tabHeight;
391 else if (infoPtr->dwStyle & TCS_VERTICAL)
393 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * infoPtr->tabHeight +
394 ((infoPtr->dwStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0);
395 itemRect->right = itemRect->left + infoPtr->tabHeight;
397 else if (infoPtr->dwStyle & TCS_BOTTOM)
399 itemRect->bottom = clientRect.bottom - itemRect->top * infoPtr->tabHeight -
400 ((infoPtr->dwStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
401 itemRect->top = itemRect->bottom - infoPtr->tabHeight;
403 else /* not TCS_BOTTOM and not TCS_VERTICAL */
405 itemRect->top = clientRect.top + itemRect->top * infoPtr->tabHeight +
406 ((infoPtr->dwStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET);
407 itemRect->bottom = itemRect->top + infoPtr->tabHeight;
411 * "scroll" it to make sure the item at the very left of the
412 * tab control is the leftmost visible tab.
414 if(infoPtr->dwStyle & TCS_VERTICAL)
416 OffsetRect(itemRect,
418 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.top);
421 * Move the rectangle so the first item is slightly offset from
422 * the bottom of the tab control.
424 OffsetRect(itemRect,
426 SELECTED_TAB_OFFSET);
428 } else
430 OffsetRect(itemRect,
431 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.left,
435 * Move the rectangle so the first item is slightly offset from
436 * the left of the tab control.
438 OffsetRect(itemRect,
439 SELECTED_TAB_OFFSET,
442 TRACE("item %d tab h=%d, rect=(%s)\n",
443 itemIndex, infoPtr->tabHeight, wine_dbgstr_rect(itemRect));
445 /* Now, calculate the position of the item as if it were selected. */
446 if (selectedRect!=NULL)
448 CopyRect(selectedRect, itemRect);
450 /* The rectangle of a selected item is a bit wider. */
451 if(infoPtr->dwStyle & TCS_VERTICAL)
452 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
453 else
454 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
456 /* If it also a bit higher. */
457 if ((infoPtr->dwStyle & TCS_BOTTOM) && (infoPtr->dwStyle & TCS_VERTICAL))
459 selectedRect->left -= 2; /* the border is thicker on the right */
460 selectedRect->right += SELECTED_TAB_OFFSET;
462 else if (infoPtr->dwStyle & TCS_VERTICAL)
464 selectedRect->left -= SELECTED_TAB_OFFSET;
465 selectedRect->right += 1;
467 else if (infoPtr->dwStyle & TCS_BOTTOM)
469 selectedRect->bottom += SELECTED_TAB_OFFSET;
471 else /* not TCS_BOTTOM and not TCS_VERTICAL */
473 selectedRect->top -= SELECTED_TAB_OFFSET;
474 selectedRect->bottom -= 1;
478 /* Check for visibility */
479 if (infoPtr->dwStyle & TCS_VERTICAL)
480 return (itemRect->top < clientRect.bottom) && (itemRect->bottom > clientRect.top);
481 else
482 return (itemRect->left < clientRect.right) && (itemRect->right > clientRect.left);
485 static inline BOOL
486 TAB_GetItemRect(const TAB_INFO *infoPtr, INT item, RECT *rect)
488 TRACE("(%p, %d, %p)\n", infoPtr, item, rect);
489 return TAB_InternalGetItemRect(infoPtr, item, rect, NULL);
492 /******************************************************************************
493 * TAB_KeyDown
495 * This method is called to handle keyboard input
497 static LRESULT TAB_KeyDown(TAB_INFO* infoPtr, WPARAM keyCode, LPARAM lParam)
499 INT newItem = -1;
500 NMTCKEYDOWN nm;
502 /* TCN_KEYDOWN notification sent always */
503 nm.hdr.hwndFrom = infoPtr->hwnd;
504 nm.hdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID);
505 nm.hdr.code = TCN_KEYDOWN;
506 nm.wVKey = keyCode;
507 nm.flags = lParam;
508 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nm.hdr.idFrom, (LPARAM)&nm);
510 switch (keyCode)
512 case VK_LEFT:
513 newItem = infoPtr->uFocus - 1;
514 break;
515 case VK_RIGHT:
516 newItem = infoPtr->uFocus + 1;
517 break;
520 /* If we changed to a valid item, change focused item */
521 if (newItem >= 0 && newItem < infoPtr->uNumItem && infoPtr->uFocus != newItem)
522 TAB_SetCurFocus(infoPtr, newItem);
524 return 0;
528 * WM_KILLFOCUS handler
530 static void TAB_KillFocus(TAB_INFO *infoPtr)
532 /* clear current focused item back to selected for TCS_BUTTONS */
533 if ((infoPtr->dwStyle & TCS_BUTTONS) && (infoPtr->uFocus != infoPtr->iSelected))
535 RECT r;
537 if (TAB_InternalGetItemRect(infoPtr, infoPtr->uFocus, &r, NULL))
538 InvalidateRect(infoPtr->hwnd, &r, FALSE);
540 infoPtr->uFocus = infoPtr->iSelected;
544 /******************************************************************************
545 * TAB_FocusChanging
547 * This method is called whenever the focus goes in or out of this control
548 * it is used to update the visual state of the control.
550 static void TAB_FocusChanging(const TAB_INFO *infoPtr)
552 RECT selectedRect;
553 BOOL isVisible;
556 * Get the rectangle for the item.
558 isVisible = TAB_InternalGetItemRect(infoPtr,
559 infoPtr->uFocus,
560 NULL,
561 &selectedRect);
564 * If the rectangle is not completely invisible, invalidate that
565 * portion of the window.
567 if (isVisible)
569 TRACE("invalidate (%s)\n", wine_dbgstr_rect(&selectedRect));
570 InvalidateRect(infoPtr->hwnd, &selectedRect, TRUE);
574 static INT TAB_InternalHitTest (const TAB_INFO *infoPtr, POINT pt, UINT *flags)
576 RECT rect;
577 INT iCount;
579 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
581 TAB_InternalGetItemRect(infoPtr, iCount, &rect, NULL);
583 if (PtInRect(&rect, pt))
585 *flags = TCHT_ONITEM;
586 return iCount;
590 *flags = TCHT_NOWHERE;
591 return -1;
594 static inline LRESULT
595 TAB_HitTest (const TAB_INFO *infoPtr, LPTCHITTESTINFO lptest)
597 TRACE("(%p, %p)\n", infoPtr, lptest);
598 return TAB_InternalHitTest (infoPtr, lptest->pt, &lptest->flags);
601 /******************************************************************************
602 * TAB_NCHitTest
604 * Napster v2b5 has a tab control for its main navigation which has a client
605 * area that covers the whole area of the dialog pages.
606 * That's why it receives all msgs for that area and the underlying dialog ctrls
607 * are dead.
608 * So I decided that we should handle WM_NCHITTEST here and return
609 * HTTRANSPARENT if we don't hit the tab control buttons.
610 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
611 * doesn't do it that way. Maybe depends on tab control styles ?
613 static inline LRESULT
614 TAB_NCHitTest (const TAB_INFO *infoPtr, LPARAM lParam)
616 POINT pt;
617 UINT dummyflag;
619 pt.x = (short)LOWORD(lParam);
620 pt.y = (short)HIWORD(lParam);
621 ScreenToClient(infoPtr->hwnd, &pt);
623 if (TAB_InternalHitTest(infoPtr, pt, &dummyflag) == -1)
624 return HTTRANSPARENT;
625 else
626 return HTCLIENT;
629 static LRESULT
630 TAB_LButtonDown (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
632 POINT pt;
633 INT newItem;
634 UINT dummy;
636 if (infoPtr->hwndToolTip)
637 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
638 WM_LBUTTONDOWN, wParam, lParam);
640 if (!(infoPtr->dwStyle & TCS_FOCUSNEVER)) {
641 SetFocus (infoPtr->hwnd);
644 if (infoPtr->hwndToolTip)
645 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
646 WM_LBUTTONDOWN, wParam, lParam);
648 pt.x = (short)LOWORD(lParam);
649 pt.y = (short)HIWORD(lParam);
651 newItem = TAB_InternalHitTest (infoPtr, pt, &dummy);
653 TRACE("On Tab, item %d\n", newItem);
655 if ((newItem != -1) && (infoPtr->iSelected != newItem))
657 if ((infoPtr->dwStyle & TCS_BUTTONS) && (infoPtr->dwStyle & TCS_MULTISELECT) &&
658 (wParam & MK_CONTROL))
660 RECT r;
662 /* toggle multiselection */
663 TAB_GetItem(infoPtr, newItem)->dwState ^= TCIS_BUTTONPRESSED;
664 if (TAB_InternalGetItemRect (infoPtr, newItem, &r, NULL))
665 InvalidateRect (infoPtr->hwnd, &r, TRUE);
667 else
669 INT i;
670 BOOL pressed = FALSE;
672 /* any button pressed ? */
673 for (i = 0; i < infoPtr->uNumItem; i++)
674 if ((TAB_GetItem (infoPtr, i)->dwState & TCIS_BUTTONPRESSED) &&
675 (infoPtr->iSelected != i))
677 pressed = TRUE;
678 break;
681 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING);
683 if (pressed)
684 TAB_DeselectAll (infoPtr, FALSE);
685 else
686 TAB_SetCurSel(infoPtr, newItem);
688 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
692 return 0;
695 static inline LRESULT
696 TAB_LButtonUp (const TAB_INFO *infoPtr)
698 TAB_SendSimpleNotify(infoPtr, NM_CLICK);
700 return 0;
703 static inline LRESULT
704 TAB_RButtonDown (const TAB_INFO *infoPtr)
706 TAB_SendSimpleNotify(infoPtr, NM_RCLICK);
707 return 0;
710 /******************************************************************************
711 * TAB_DrawLoneItemInterior
713 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
714 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
715 * up the device context and font. This routine does the same setup but
716 * only calls TAB_DrawItemInterior for the single specified item.
718 static void
719 TAB_DrawLoneItemInterior(const TAB_INFO* infoPtr, int iItem)
721 HDC hdc = GetDC(infoPtr->hwnd);
722 RECT r, rC;
724 /* Clip UpDown control to not draw over it */
725 if (infoPtr->needsScrolling)
727 GetWindowRect(infoPtr->hwnd, &rC);
728 GetWindowRect(infoPtr->hwndUpDown, &r);
729 ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top);
731 TAB_DrawItemInterior(infoPtr, hdc, iItem, NULL);
732 ReleaseDC(infoPtr->hwnd, hdc);
735 /* update a tab after hottracking - invalidate it or just redraw the interior,
736 * based on whether theming is used or not */
737 static inline void hottrack_refresh(const TAB_INFO *infoPtr, int tabIndex)
739 if (tabIndex == -1) return;
741 if (GetWindowTheme (infoPtr->hwnd))
743 RECT rect;
744 TAB_InternalGetItemRect(infoPtr, tabIndex, &rect, NULL);
745 InvalidateRect (infoPtr->hwnd, &rect, FALSE);
747 else
748 TAB_DrawLoneItemInterior(infoPtr, tabIndex);
751 /******************************************************************************
752 * TAB_HotTrackTimerProc
754 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
755 * timer is setup so we can check if the mouse is moved out of our window.
756 * (We don't get an event when the mouse leaves, the mouse-move events just
757 * stop being delivered to our window and just start being delivered to
758 * another window.) This function is called when the timer triggers so
759 * we can check if the mouse has left our window. If so, we un-highlight
760 * the hot-tracked tab.
762 static void CALLBACK
763 TAB_HotTrackTimerProc
765 HWND hwnd, /* handle of window for timer messages */
766 UINT uMsg, /* WM_TIMER message */
767 UINT_PTR idEvent, /* timer identifier */
768 DWORD dwTime /* current system time */
771 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
773 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
775 POINT pt;
778 ** If we can't get the cursor position, or if the cursor is outside our
779 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
780 ** "outside" even if it is within our bounding rect if another window
781 ** overlaps. Note also that the case where the cursor stayed within our
782 ** window but has moved off the hot-tracked tab will be handled by the
783 ** WM_MOUSEMOVE event.
785 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
787 /* Redraw iHotTracked to look normal */
788 INT iRedraw = infoPtr->iHotTracked;
789 infoPtr->iHotTracked = -1;
790 hottrack_refresh (infoPtr, iRedraw);
792 /* Kill this timer */
793 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
798 /******************************************************************************
799 * TAB_RecalcHotTrack
801 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
802 * should be highlighted. This function determines which tab in a tab control,
803 * if any, is under the mouse and records that information. The caller may
804 * supply output parameters to receive the item number of the tab item which
805 * was highlighted but isn't any longer and of the tab item which is now
806 * highlighted but wasn't previously. The caller can use this information to
807 * selectively redraw those tab items.
809 * If the caller has a mouse position, it can supply it through the pos
810 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
811 * supplies NULL and this function determines the current mouse position
812 * itself.
814 static void
815 TAB_RecalcHotTrack
817 TAB_INFO* infoPtr,
818 const LPARAM* pos,
819 int* out_redrawLeave,
820 int* out_redrawEnter
823 int item = -1;
826 if (out_redrawLeave != NULL)
827 *out_redrawLeave = -1;
828 if (out_redrawEnter != NULL)
829 *out_redrawEnter = -1;
831 if ((infoPtr->dwStyle & TCS_HOTTRACK) || GetWindowTheme(infoPtr->hwnd))
833 POINT pt;
834 UINT flags;
836 if (pos == NULL)
838 GetCursorPos(&pt);
839 ScreenToClient(infoPtr->hwnd, &pt);
841 else
843 pt.x = (short)LOWORD(*pos);
844 pt.y = (short)HIWORD(*pos);
847 item = TAB_InternalHitTest(infoPtr, pt, &flags);
850 if (item != infoPtr->iHotTracked)
852 if (infoPtr->iHotTracked >= 0)
854 /* Mark currently hot-tracked to be redrawn to look normal */
855 if (out_redrawLeave != NULL)
856 *out_redrawLeave = infoPtr->iHotTracked;
858 if (item < 0)
860 /* Kill timer which forces recheck of mouse pos */
861 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
864 else
866 /* Start timer so we recheck mouse pos */
867 UINT timerID = SetTimer
869 infoPtr->hwnd,
870 TAB_HOTTRACK_TIMER,
871 TAB_HOTTRACK_TIMER_INTERVAL,
872 TAB_HotTrackTimerProc
875 if (timerID == 0)
876 return; /* Hot tracking not available */
879 infoPtr->iHotTracked = item;
881 if (item >= 0)
883 /* Mark new hot-tracked to be redrawn to look highlighted */
884 if (out_redrawEnter != NULL)
885 *out_redrawEnter = item;
890 /******************************************************************************
891 * TAB_MouseMove
893 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
895 static LRESULT
896 TAB_MouseMove (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
898 int redrawLeave;
899 int redrawEnter;
901 if (infoPtr->hwndToolTip)
902 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
903 WM_LBUTTONDOWN, wParam, lParam);
905 /* Determine which tab to highlight. Redraw tabs which change highlight
906 ** status. */
907 TAB_RecalcHotTrack(infoPtr, &lParam, &redrawLeave, &redrawEnter);
909 hottrack_refresh (infoPtr, redrawLeave);
910 hottrack_refresh (infoPtr, redrawEnter);
912 return 0;
915 /******************************************************************************
916 * TAB_AdjustRect
918 * Calculates the tab control's display area given the window rectangle or
919 * the window rectangle given the requested display rectangle.
921 static LRESULT TAB_AdjustRect(const TAB_INFO *infoPtr, WPARAM fLarger, LPRECT prc)
923 LONG *iRightBottom, *iLeftTop;
925 TRACE ("hwnd=%p fLarger=%ld (%s)\n", infoPtr->hwnd, fLarger,
926 wine_dbgstr_rect(prc));
928 if (!prc) return -1;
930 if(infoPtr->dwStyle & TCS_VERTICAL)
932 iRightBottom = &(prc->right);
933 iLeftTop = &(prc->left);
935 else
937 iRightBottom = &(prc->bottom);
938 iLeftTop = &(prc->top);
941 if (fLarger) /* Go from display rectangle */
943 /* Add the height of the tabs. */
944 if (infoPtr->dwStyle & TCS_BOTTOM)
945 *iRightBottom += infoPtr->tabHeight * infoPtr->uNumRows;
946 else
947 *iLeftTop -= infoPtr->tabHeight * infoPtr->uNumRows +
948 ((infoPtr->dwStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
950 /* Inflate the rectangle for the padding */
951 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
953 /* Inflate for the border */
954 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEY);
956 else /* Go from window rectangle. */
958 /* Deflate the rectangle for the border */
959 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEY);
961 /* Deflate the rectangle for the padding */
962 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
964 /* Remove the height of the tabs. */
965 if (infoPtr->dwStyle & TCS_BOTTOM)
966 *iRightBottom -= infoPtr->tabHeight * infoPtr->uNumRows;
967 else
968 *iLeftTop += (infoPtr->tabHeight) * infoPtr->uNumRows +
969 ((infoPtr->dwStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0);
972 return 0;
975 /******************************************************************************
976 * TAB_OnHScroll
978 * This method will handle the notification from the scroll control and
979 * perform the scrolling operation on the tab control.
981 static LRESULT TAB_OnHScroll(TAB_INFO *infoPtr, int nScrollCode, int nPos)
983 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
985 if(nPos < infoPtr->leftmostVisible)
986 infoPtr->leftmostVisible--;
987 else
988 infoPtr->leftmostVisible++;
990 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
991 TAB_InvalidateTabArea(infoPtr);
992 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
993 MAKELONG(infoPtr->leftmostVisible, 0));
996 return 0;
999 /******************************************************************************
1000 * TAB_SetupScrolling
1002 * This method will check the current scrolling state and make sure the
1003 * scrolling control is displayed (or not).
1005 static void TAB_SetupScrolling(
1006 TAB_INFO* infoPtr,
1007 const RECT* clientRect)
1009 static const WCHAR emptyW[] = { 0 };
1010 INT maxRange = 0;
1012 if (infoPtr->needsScrolling)
1014 RECT controlPos;
1015 INT vsize, tabwidth;
1018 * Calculate the position of the scroll control.
1020 if(infoPtr->dwStyle & TCS_VERTICAL)
1022 controlPos.right = clientRect->right;
1023 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
1025 if (infoPtr->dwStyle & TCS_BOTTOM)
1027 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
1028 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
1030 else
1032 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
1033 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
1036 else
1038 controlPos.right = clientRect->right;
1039 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
1041 if (infoPtr->dwStyle & TCS_BOTTOM)
1043 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
1044 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
1046 else
1048 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
1049 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
1054 * If we don't have a scroll control yet, we want to create one.
1055 * If we have one, we want to make sure it's positioned properly.
1057 if (infoPtr->hwndUpDown==0)
1059 infoPtr->hwndUpDown = CreateWindowW(UPDOWN_CLASSW, emptyW,
1060 WS_VISIBLE | WS_CHILD | UDS_HORZ,
1061 controlPos.left, controlPos.top,
1062 controlPos.right - controlPos.left,
1063 controlPos.bottom - controlPos.top,
1064 infoPtr->hwnd, NULL, NULL, NULL);
1066 else
1068 SetWindowPos(infoPtr->hwndUpDown,
1069 NULL,
1070 controlPos.left, controlPos.top,
1071 controlPos.right - controlPos.left,
1072 controlPos.bottom - controlPos.top,
1073 SWP_SHOWWINDOW | SWP_NOZORDER);
1076 /* Now calculate upper limit of the updown control range.
1077 * We do this by calculating how many tabs will be offscreen when the
1078 * last tab is visible.
1080 if(infoPtr->uNumItem)
1082 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
1083 maxRange = infoPtr->uNumItem;
1084 tabwidth = TAB_GetItem(infoPtr, infoPtr->uNumItem - 1)->rect.right;
1086 for(; maxRange > 0; maxRange--)
1088 if(tabwidth - TAB_GetItem(infoPtr,maxRange - 1)->rect.left > vsize)
1089 break;
1092 if(maxRange == infoPtr->uNumItem)
1093 maxRange--;
1096 else
1098 /* If we once had a scroll control... hide it */
1099 if (infoPtr->hwndUpDown)
1100 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1102 if (infoPtr->hwndUpDown)
1103 SendMessageW(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1106 /******************************************************************************
1107 * TAB_SetItemBounds
1109 * This method will calculate the position rectangles of all the items in the
1110 * control. The rectangle calculated starts at 0 for the first item in the
1111 * list and ignores scrolling and selection.
1112 * It also uses the current font to determine the height of the tab row and
1113 * it checks if all the tabs fit in the client area of the window. If they
1114 * don't, a scrolling control is added.
1116 static void TAB_SetItemBounds (TAB_INFO *infoPtr)
1118 TEXTMETRICW fontMetrics;
1119 UINT curItem;
1120 INT curItemLeftPos;
1121 INT curItemRowCount;
1122 HFONT hFont, hOldFont;
1123 HDC hdc;
1124 RECT clientRect;
1125 INT iTemp;
1126 RECT* rcItem;
1127 INT iIndex;
1128 INT icon_width = 0;
1131 * We need to get text information so we need a DC and we need to select
1132 * a font.
1134 hdc = GetDC(infoPtr->hwnd);
1136 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1137 hOldFont = SelectObject (hdc, hFont);
1140 * We will base the rectangle calculations on the client rectangle
1141 * of the control.
1143 GetClientRect(infoPtr->hwnd, &clientRect);
1145 /* if TCS_VERTICAL then swap the height and width so this code places the
1146 tabs along the top of the rectangle and we can just rotate them after
1147 rather than duplicate all of the below code */
1148 if(infoPtr->dwStyle & TCS_VERTICAL)
1150 iTemp = clientRect.bottom;
1151 clientRect.bottom = clientRect.right;
1152 clientRect.right = iTemp;
1155 /* Now use hPadding and vPadding */
1156 infoPtr->uHItemPadding = infoPtr->uHItemPadding_s;
1157 infoPtr->uVItemPadding = infoPtr->uVItemPadding_s;
1159 /* The leftmost item will be "0" aligned */
1160 curItemLeftPos = 0;
1161 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1163 if (!(infoPtr->fHeightSet))
1165 int item_height;
1166 INT icon_height = 0, cx;
1168 /* Use the current font to determine the height of a tab. */
1169 GetTextMetricsW(hdc, &fontMetrics);
1171 /* Get the icon height */
1172 if (infoPtr->himl)
1173 ImageList_GetIconSize(infoPtr->himl, &cx, &icon_height);
1175 /* Take the highest between font or icon */
1176 if (fontMetrics.tmHeight > icon_height)
1177 item_height = fontMetrics.tmHeight + 2;
1178 else
1179 item_height = icon_height;
1182 * Make sure there is enough space for the letters + icon + growing the
1183 * selected item + extra space for the selected item.
1185 infoPtr->tabHeight = item_height +
1186 ((infoPtr->dwStyle & TCS_BUTTONS) ? 2 : 1) *
1187 infoPtr->uVItemPadding;
1189 TRACE("tabH=%d, tmH=%d, iconh=%d\n",
1190 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1193 TRACE("client right=%d\n", clientRect.right);
1195 /* Get the icon width */
1196 if (infoPtr->himl)
1198 INT cy;
1200 ImageList_GetIconSize(infoPtr->himl, &icon_width, &cy);
1202 if (infoPtr->dwStyle & TCS_FIXEDWIDTH)
1203 icon_width += 4;
1204 else
1205 /* Add padding if icon is present */
1206 icon_width += infoPtr->uHItemPadding;
1209 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1211 TAB_ITEM *curr = TAB_GetItem(infoPtr, curItem);
1213 /* Set the leftmost position of the tab. */
1214 curr->rect.left = curItemLeftPos;
1216 if (infoPtr->dwStyle & TCS_FIXEDWIDTH)
1218 curr->rect.right = curr->rect.left +
1219 max(infoPtr->tabWidth, icon_width);
1221 else if (!curr->pszText)
1223 /* If no text use minimum tab width including padding. */
1224 if (infoPtr->tabMinWidth < 0)
1225 curr->rect.right = curr->rect.left + GET_DEFAULT_MIN_TAB_WIDTH(infoPtr);
1226 else
1228 curr->rect.right = curr->rect.left + infoPtr->tabMinWidth;
1230 /* Add extra padding if icon is present */
1231 if (infoPtr->himl && infoPtr->tabMinWidth > 0 && infoPtr->tabMinWidth < DEFAULT_MIN_TAB_WIDTH
1232 && infoPtr->uHItemPadding > 1)
1233 curr->rect.right += EXTRA_ICON_PADDING * (infoPtr->uHItemPadding-1);
1236 else
1238 int tabwidth;
1239 SIZE size;
1240 /* Calculate how wide the tab is depending on the text it contains */
1241 GetTextExtentPoint32W(hdc, curr->pszText,
1242 lstrlenW(curr->pszText), &size);
1244 tabwidth = size.cx + icon_width + 2 * infoPtr->uHItemPadding;
1246 if (infoPtr->tabMinWidth < 0)
1247 tabwidth = max(tabwidth, GET_DEFAULT_MIN_TAB_WIDTH(infoPtr));
1248 else
1249 tabwidth = max(tabwidth, infoPtr->tabMinWidth);
1251 curr->rect.right = curr->rect.left + tabwidth;
1252 TRACE("for <%s>, l,r=%d,%d\n",
1253 debugstr_w(curr->pszText), curr->rect.left, curr->rect.right);
1257 * Check if this is a multiline tab control and if so
1258 * check to see if we should wrap the tabs
1260 * Wrap all these tabs. We will arrange them evenly later.
1264 if (((infoPtr->dwStyle & TCS_MULTILINE) || (infoPtr->dwStyle & TCS_VERTICAL)) &&
1265 (curr->rect.right >
1266 (clientRect.right - CONTROL_BORDER_SIZEX - DISPLAY_AREA_PADDINGX)))
1268 curr->rect.right -= curr->rect.left;
1270 curr->rect.left = 0;
1271 curItemRowCount++;
1272 TRACE("wrapping <%s>, l,r=%d,%d\n", debugstr_w(curr->pszText),
1273 curr->rect.left, curr->rect.right);
1276 curr->rect.bottom = 0;
1277 curr->rect.top = curItemRowCount - 1;
1279 TRACE("Rect: %s\n", wine_dbgstr_rect(&curr->rect));
1282 * The leftmost position of the next item is the rightmost position
1283 * of this one.
1285 if (infoPtr->dwStyle & TCS_BUTTONS)
1287 curItemLeftPos = curr->rect.right + BUTTON_SPACINGX;
1288 if (infoPtr->dwStyle & TCS_FLATBUTTONS)
1289 curItemLeftPos += FLAT_BTN_SPACINGX;
1291 else
1292 curItemLeftPos = curr->rect.right;
1295 if (!((infoPtr->dwStyle & TCS_MULTILINE) || (infoPtr->dwStyle & TCS_VERTICAL)))
1298 * Check if we need a scrolling control.
1300 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1301 clientRect.right);
1303 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1304 if(!infoPtr->needsScrolling)
1305 infoPtr->leftmostVisible = 0;
1307 else
1310 * No scrolling in Multiline or Vertical styles.
1312 infoPtr->needsScrolling = FALSE;
1313 infoPtr->leftmostVisible = 0;
1315 TAB_SetupScrolling(infoPtr, &clientRect);
1317 /* Set the number of rows */
1318 infoPtr->uNumRows = curItemRowCount;
1320 /* Arrange all tabs evenly if style says so */
1321 if (!(infoPtr->dwStyle & TCS_RAGGEDRIGHT) &&
1322 ((infoPtr->dwStyle & TCS_MULTILINE) || (infoPtr->dwStyle & TCS_VERTICAL)) &&
1323 (infoPtr->uNumItem > 0) &&
1324 (infoPtr->uNumRows > 1))
1326 INT tabPerRow,remTab,iRow;
1327 UINT iItm;
1328 INT iCount=0;
1331 * Ok windows tries to even out the rows. place the same
1332 * number of tabs in each row. So lets give that a shot
1335 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1336 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1338 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1339 iItm<infoPtr->uNumItem;
1340 iItm++,iCount++)
1342 /* normalize the current rect */
1343 TAB_ITEM *curr = TAB_GetItem(infoPtr, iItm);
1345 /* shift the item to the left side of the clientRect */
1346 curr->rect.right -= curr->rect.left;
1347 curr->rect.left = 0;
1349 TRACE("r=%d, cl=%d, cl.r=%d, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1350 curr->rect.right, curItemLeftPos, clientRect.right,
1351 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1353 /* if we have reached the maximum number of tabs on this row */
1354 /* move to the next row, reset our current item left position and */
1355 /* the count of items on this row */
1357 if (infoPtr->dwStyle & TCS_VERTICAL) {
1358 /* Vert: Add the remaining tabs in the *last* remainder rows */
1359 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1360 iRow++;
1361 curItemLeftPos = 0;
1362 iCount = 0;
1364 } else {
1365 /* Horz: Add the remaining tabs in the *first* remainder rows */
1366 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1367 iRow++;
1368 curItemLeftPos = 0;
1369 iCount = 0;
1373 /* shift the item to the right to place it as the next item in this row */
1374 curr->rect.left += curItemLeftPos;
1375 curr->rect.right += curItemLeftPos;
1376 curr->rect.top = iRow;
1377 if (infoPtr->dwStyle & TCS_BUTTONS)
1379 curItemLeftPos = curr->rect.right + 1;
1380 if (infoPtr->dwStyle & TCS_FLATBUTTONS)
1381 curItemLeftPos += FLAT_BTN_SPACINGX;
1383 else
1384 curItemLeftPos = curr->rect.right;
1386 TRACE("arranging <%s>, l,r=%d,%d, row=%d\n",
1387 debugstr_w(curr->pszText), curr->rect.left,
1388 curr->rect.right, curr->rect.top);
1392 * Justify the rows
1395 INT widthDiff, iIndexStart=0, iIndexEnd=0;
1396 INT remainder;
1397 INT iCount=0;
1399 while(iIndexStart < infoPtr->uNumItem)
1401 TAB_ITEM *start = TAB_GetItem(infoPtr, iIndexStart);
1404 * find the index of the row
1406 /* find the first item on the next row */
1407 for (iIndexEnd=iIndexStart;
1408 (iIndexEnd < infoPtr->uNumItem) &&
1409 (TAB_GetItem(infoPtr, iIndexEnd)->rect.top ==
1410 start->rect.top) ;
1411 iIndexEnd++)
1412 /* intentionally blank */;
1415 * we need to justify these tabs so they fill the whole given
1416 * client area
1419 /* find the amount of space remaining on this row */
1420 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1421 TAB_GetItem(infoPtr, iIndexEnd - 1)->rect.right;
1423 /* iCount is the number of tab items on this row */
1424 iCount = iIndexEnd - iIndexStart;
1426 if (iCount > 1)
1428 remainder = widthDiff % iCount;
1429 widthDiff = widthDiff / iCount;
1430 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1431 for (iIndex=iIndexStart, iCount=0; iIndex < iIndexEnd; iIndex++, iCount++)
1433 TAB_ITEM *item = TAB_GetItem(infoPtr, iIndex);
1435 item->rect.left += iCount * widthDiff;
1436 item->rect.right += (iCount + 1) * widthDiff;
1438 TRACE("adjusting 1 <%s>, l,r=%d,%d\n",
1439 debugstr_w(item->pszText),
1440 item->rect.left, item->rect.right);
1443 TAB_GetItem(infoPtr, iIndex - 1)->rect.right += remainder;
1445 else /* we have only one item on this row, make it take up the entire row */
1447 start->rect.left = clientRect.left;
1448 start->rect.right = clientRect.right - 4;
1450 TRACE("adjusting 2 <%s>, l,r=%d,%d\n",
1451 debugstr_w(start->pszText),
1452 start->rect.left, start->rect.right);
1457 iIndexStart = iIndexEnd;
1462 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1463 if(infoPtr->dwStyle & TCS_VERTICAL)
1465 RECT rcOriginal;
1466 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1468 rcItem = &TAB_GetItem(infoPtr, iIndex)->rect;
1470 rcOriginal = *rcItem;
1472 /* this is rotating the items by 90 degrees clockwise around the center of the control */
1473 rcItem->top = (rcOriginal.left - clientRect.left);
1474 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1475 rcItem->left = rcOriginal.top;
1476 rcItem->right = rcOriginal.bottom;
1480 TAB_EnsureSelectionVisible(infoPtr);
1481 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
1483 /* Cleanup */
1484 SelectObject (hdc, hOldFont);
1485 ReleaseDC (infoPtr->hwnd, hdc);
1489 static void
1490 TAB_EraseTabInterior(const TAB_INFO *infoPtr, HDC hdc, INT iItem, const RECT *drawRect)
1492 HBRUSH hbr = CreateSolidBrush (comctl32_color.clrBtnFace);
1493 BOOL deleteBrush = TRUE;
1494 RECT rTemp = *drawRect;
1496 if (infoPtr->dwStyle & TCS_BUTTONS)
1498 if (iItem == infoPtr->iSelected)
1500 /* Background color */
1501 if (!(infoPtr->dwStyle & TCS_OWNERDRAWFIXED))
1503 DeleteObject(hbr);
1504 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1506 SetTextColor(hdc, comctl32_color.clr3dFace);
1507 SetBkColor(hdc, comctl32_color.clr3dHilight);
1509 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1510 * we better use 0x55aa bitmap brush to make scrollbar's background
1511 * look different from the window background.
1513 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1514 hbr = COMCTL32_hPattern55AABrush;
1516 deleteBrush = FALSE;
1518 FillRect(hdc, &rTemp, hbr);
1520 else /* ! selected */
1522 if (infoPtr->dwStyle & TCS_FLATBUTTONS)
1524 InflateRect(&rTemp, 2, 2);
1525 FillRect(hdc, &rTemp, hbr);
1526 if (iItem == infoPtr->iHotTracked ||
1527 (iItem != infoPtr->iSelected && iItem == infoPtr->uFocus))
1528 DrawEdge(hdc, &rTemp, BDR_RAISEDINNER, BF_RECT);
1530 else
1531 FillRect(hdc, &rTemp, hbr);
1535 else /* !TCS_BUTTONS */
1537 InflateRect(&rTemp, -2, -2);
1538 if (!GetWindowTheme (infoPtr->hwnd))
1539 FillRect(hdc, &rTemp, hbr);
1542 /* highlighting is drawn on top of previous fills */
1543 if (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED)
1545 if (deleteBrush)
1547 DeleteObject(hbr);
1548 deleteBrush = FALSE;
1550 hbr = GetSysColorBrush(COLOR_HIGHLIGHT);
1551 FillRect(hdc, &rTemp, hbr);
1554 /* Cleanup */
1555 if (deleteBrush) DeleteObject(hbr);
1558 /******************************************************************************
1559 * TAB_DrawItemInterior
1561 * This method is used to draw the interior (text and icon) of a single tab
1562 * into the tab control.
1564 static void
1565 TAB_DrawItemInterior(const TAB_INFO *infoPtr, HDC hdc, INT iItem, RECT *drawRect)
1567 RECT localRect;
1569 HPEN htextPen;
1570 HPEN holdPen;
1571 INT oldBkMode;
1572 HFONT hOldFont;
1574 /* if (drawRect == NULL) */
1576 BOOL isVisible;
1577 RECT itemRect;
1578 RECT selectedRect;
1581 * Get the rectangle for the item.
1583 isVisible = TAB_InternalGetItemRect(infoPtr, iItem, &itemRect, &selectedRect);
1584 if (!isVisible)
1585 return;
1588 * Make sure drawRect points to something valid; simplifies code.
1590 drawRect = &localRect;
1593 * This logic copied from the part of TAB_DrawItem which draws
1594 * the tab background. It's important to keep it in sync. I
1595 * would have liked to avoid code duplication, but couldn't figure
1596 * out how without making spaghetti of TAB_DrawItem.
1598 if (iItem == infoPtr->iSelected)
1599 *drawRect = selectedRect;
1600 else
1601 *drawRect = itemRect;
1603 if (infoPtr->dwStyle & TCS_BUTTONS)
1605 if (iItem == infoPtr->iSelected)
1607 drawRect->left += 4;
1608 drawRect->top += 4;
1609 drawRect->right -= 4;
1611 if (infoPtr->dwStyle & TCS_VERTICAL)
1613 if (!(infoPtr->dwStyle & TCS_BOTTOM)) drawRect->right += 1;
1614 drawRect->bottom -= 4;
1616 else
1618 if (infoPtr->dwStyle & TCS_BOTTOM)
1620 drawRect->top -= 2;
1621 drawRect->bottom -= 4;
1623 else
1624 drawRect->bottom -= 1;
1627 else
1629 drawRect->left += 2;
1630 drawRect->top += 2;
1631 drawRect->right -= 2;
1632 drawRect->bottom -= 2;
1635 else
1637 if ((infoPtr->dwStyle & TCS_VERTICAL) && (infoPtr->dwStyle & TCS_BOTTOM))
1639 if (iItem != infoPtr->iSelected)
1641 drawRect->left += 2;
1642 drawRect->top += 2;
1643 drawRect->bottom -= 2;
1646 else if (infoPtr->dwStyle & TCS_VERTICAL)
1648 if (iItem == infoPtr->iSelected)
1650 drawRect->right += 1;
1652 else
1654 drawRect->top += 2;
1655 drawRect->right -= 2;
1656 drawRect->bottom -= 2;
1659 else if (infoPtr->dwStyle & TCS_BOTTOM)
1661 if (iItem == infoPtr->iSelected)
1663 drawRect->top -= 2;
1665 else
1667 InflateRect(drawRect, -2, -2);
1668 drawRect->bottom += 2;
1671 else
1673 if (iItem == infoPtr->iSelected)
1675 drawRect->bottom += 3;
1677 else
1679 drawRect->bottom -= 2;
1680 InflateRect(drawRect, -2, 0);
1685 TRACE("drawRect=(%s)\n", wine_dbgstr_rect(drawRect));
1687 /* Clear interior */
1688 TAB_EraseTabInterior (infoPtr, hdc, iItem, drawRect);
1690 /* Draw the focus rectangle */
1691 if (!(infoPtr->dwStyle & TCS_FOCUSNEVER) &&
1692 (GetFocus() == infoPtr->hwnd) &&
1693 (iItem == infoPtr->uFocus) )
1695 RECT rFocus = *drawRect;
1697 if (!(infoPtr->dwStyle & TCS_BUTTONS)) InflateRect(&rFocus, -3, -3);
1698 if (infoPtr->dwStyle & TCS_BOTTOM && !(infoPtr->dwStyle & TCS_VERTICAL))
1699 rFocus.top -= 3;
1701 /* focus should stay on selected item for TCS_BUTTONS style */
1702 if (!((infoPtr->dwStyle & TCS_BUTTONS) && (infoPtr->iSelected != iItem)))
1703 DrawFocusRect(hdc, &rFocus);
1707 * Text pen
1709 htextPen = CreatePen( PS_SOLID, 1, comctl32_color.clrBtnText );
1710 holdPen = SelectObject(hdc, htextPen);
1711 hOldFont = SelectObject(hdc, infoPtr->hFont);
1714 * Setup for text output
1716 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1717 if (!GetWindowTheme (infoPtr->hwnd) || (infoPtr->dwStyle & TCS_BUTTONS))
1719 if ((infoPtr->dwStyle & TCS_HOTTRACK) && (iItem == infoPtr->iHotTracked) &&
1720 !(infoPtr->dwStyle & TCS_FLATBUTTONS))
1721 SetTextColor(hdc, comctl32_color.clrHighlight);
1722 else if (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED)
1723 SetTextColor(hdc, comctl32_color.clrHighlightText);
1724 else
1725 SetTextColor(hdc, comctl32_color.clrBtnText);
1729 * if owner draw, tell the owner to draw
1731 if ((infoPtr->dwStyle & TCS_OWNERDRAWFIXED) && GetParent(infoPtr->hwnd))
1733 DRAWITEMSTRUCT dis;
1734 UINT id;
1736 drawRect->top += 2;
1737 drawRect->right -= 1;
1738 if ( iItem == infoPtr->iSelected )
1740 drawRect->right -= 1;
1741 drawRect->left += 1;
1745 * get the control id
1747 id = (UINT)GetWindowLongPtrW( infoPtr->hwnd, GWLP_ID );
1750 * put together the DRAWITEMSTRUCT
1752 dis.CtlType = ODT_TAB;
1753 dis.CtlID = id;
1754 dis.itemID = iItem;
1755 dis.itemAction = ODA_DRAWENTIRE;
1756 dis.itemState = 0;
1757 if ( iItem == infoPtr->iSelected )
1758 dis.itemState |= ODS_SELECTED;
1759 if (infoPtr->uFocus == iItem)
1760 dis.itemState |= ODS_FOCUS;
1761 dis.hwndItem = infoPtr->hwnd;
1762 dis.hDC = hdc;
1763 CopyRect(&dis.rcItem,drawRect);
1764 dis.itemData = (ULONG_PTR)TAB_GetItem(infoPtr, iItem)->extra;
1767 * send the draw message
1769 SendMessageW( infoPtr->hwndNotify, WM_DRAWITEM, id, (LPARAM)&dis );
1771 else
1773 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
1774 RECT rcTemp;
1775 RECT rcImage;
1777 /* used to center the icon and text in the tab */
1778 RECT rcText;
1779 INT center_offset_h, center_offset_v;
1781 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1782 rcImage = *drawRect;
1784 rcTemp = *drawRect;
1786 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1788 /* get the rectangle that the text fits in */
1789 if (item->pszText)
1791 DrawTextW(hdc, item->pszText, -1, &rcText, DT_CALCRECT);
1794 * If not owner draw, then do the drawing ourselves.
1796 * Draw the icon.
1798 if (infoPtr->himl && item->iImage != -1)
1800 INT cx;
1801 INT cy;
1803 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1805 if(infoPtr->dwStyle & TCS_VERTICAL)
1807 center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1808 center_offset_v = ((drawRect->right - drawRect->left) - cx) / 2;
1810 else
1812 center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2;
1813 center_offset_v = ((drawRect->bottom - drawRect->top) - cy) / 2;
1816 /* if an item is selected, the icon is shifted up instead of down */
1817 if (iItem == infoPtr->iSelected)
1818 center_offset_v -= infoPtr->uVItemPadding / 2;
1819 else
1820 center_offset_v += infoPtr->uVItemPadding / 2;
1822 if (infoPtr->dwStyle & TCS_FIXEDWIDTH && infoPtr->dwStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT))
1823 center_offset_h = infoPtr->uHItemPadding;
1825 if (center_offset_h < 2)
1826 center_offset_h = 2;
1828 if (center_offset_v < 0)
1829 center_offset_v = 0;
1831 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%s), textlen=%d\n",
1832 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1833 wine_dbgstr_rect(drawRect), (rcText.right-rcText.left));
1835 if((infoPtr->dwStyle & TCS_VERTICAL) && (infoPtr->dwStyle & TCS_BOTTOM))
1837 rcImage.top = drawRect->top + center_offset_h;
1838 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1839 /* right side of the tab, but the image still uses the left as its x position */
1840 /* this keeps the image always drawn off of the same side of the tab */
1841 rcImage.left = drawRect->right - cx - center_offset_v;
1842 drawRect->top += cy + infoPtr->uHItemPadding;
1844 else if(infoPtr->dwStyle & TCS_VERTICAL)
1846 rcImage.top = drawRect->bottom - cy - center_offset_h;
1847 rcImage.left = drawRect->left + center_offset_v;
1848 drawRect->bottom -= cy + infoPtr->uHItemPadding;
1850 else /* normal style, whether TCS_BOTTOM or not */
1852 rcImage.left = drawRect->left + center_offset_h;
1853 rcImage.top = drawRect->top + center_offset_v;
1854 drawRect->left += cx + infoPtr->uHItemPadding;
1857 TRACE("drawing image=%d, left=%d, top=%d\n",
1858 item->iImage, rcImage.left, rcImage.top-1);
1859 ImageList_Draw
1861 infoPtr->himl,
1862 item->iImage,
1863 hdc,
1864 rcImage.left,
1865 rcImage.top,
1866 ILD_NORMAL
1870 /* Now position text */
1871 if (infoPtr->dwStyle & TCS_FIXEDWIDTH && infoPtr->dwStyle & TCS_FORCELABELLEFT)
1872 center_offset_h = infoPtr->uHItemPadding;
1873 else
1874 if(infoPtr->dwStyle & TCS_VERTICAL)
1875 center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1876 else
1877 center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1879 if(infoPtr->dwStyle & TCS_VERTICAL)
1881 if(infoPtr->dwStyle & TCS_BOTTOM)
1882 drawRect->top+=center_offset_h;
1883 else
1884 drawRect->bottom-=center_offset_h;
1886 center_offset_v = ((drawRect->right - drawRect->left) - (rcText.bottom - rcText.top)) / 2;
1888 else
1890 drawRect->left += center_offset_h;
1891 center_offset_v = ((drawRect->bottom - drawRect->top) - (rcText.bottom - rcText.top)) / 2;
1894 /* if an item is selected, the text is shifted up instead of down */
1895 if (iItem == infoPtr->iSelected)
1896 center_offset_v -= infoPtr->uVItemPadding / 2;
1897 else
1898 center_offset_v += infoPtr->uVItemPadding / 2;
1900 if (center_offset_v < 0)
1901 center_offset_v = 0;
1903 if(infoPtr->dwStyle & TCS_VERTICAL)
1904 drawRect->left += center_offset_v;
1905 else
1906 drawRect->top += center_offset_v;
1908 /* Draw the text */
1909 if(infoPtr->dwStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1911 static const WCHAR ArialW[] = { 'A','r','i','a','l',0 };
1912 LOGFONTW logfont;
1913 HFONT hFont = 0;
1914 INT nEscapement = 900;
1915 INT nOrientation = 900;
1917 if(infoPtr->dwStyle & TCS_BOTTOM)
1919 nEscapement = -900;
1920 nOrientation = -900;
1923 /* to get a font with the escapement and orientation we are looking for, we need to */
1924 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1925 if (!GetObjectW((infoPtr->hFont) ?
1926 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1927 sizeof(LOGFONTW),&logfont))
1929 INT iPointSize = 9;
1931 lstrcpyW(logfont.lfFaceName, ArialW);
1932 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1933 72);
1934 logfont.lfWeight = FW_NORMAL;
1935 logfont.lfItalic = 0;
1936 logfont.lfUnderline = 0;
1937 logfont.lfStrikeOut = 0;
1940 logfont.lfEscapement = nEscapement;
1941 logfont.lfOrientation = nOrientation;
1942 hFont = CreateFontIndirectW(&logfont);
1943 SelectObject(hdc, hFont);
1945 if (item->pszText)
1947 ExtTextOutW(hdc,
1948 (infoPtr->dwStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1949 (!(infoPtr->dwStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1950 ETO_CLIPPED,
1951 drawRect,
1952 item->pszText,
1953 lstrlenW(item->pszText),
1957 DeleteObject(hFont);
1959 else
1961 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%s), textlen=%d\n",
1962 debugstr_w(item->pszText), center_offset_h, center_offset_v,
1963 wine_dbgstr_rect(drawRect), (rcText.right-rcText.left));
1964 if (item->pszText)
1966 DrawTextW
1968 hdc,
1969 item->pszText,
1970 lstrlenW(item->pszText),
1971 drawRect,
1972 DT_LEFT | DT_SINGLELINE
1977 *drawRect = rcTemp; /* restore drawRect */
1981 * Cleanup
1983 SelectObject(hdc, hOldFont);
1984 SetBkMode(hdc, oldBkMode);
1985 SelectObject(hdc, holdPen);
1986 DeleteObject( htextPen );
1989 /******************************************************************************
1990 * TAB_DrawItem
1992 * This method is used to draw a single tab into the tab control.
1994 static void TAB_DrawItem(const TAB_INFO *infoPtr, HDC hdc, INT iItem)
1996 RECT itemRect;
1997 RECT selectedRect;
1998 BOOL isVisible;
1999 RECT r, fillRect, r1;
2000 INT clRight = 0;
2001 INT clBottom = 0;
2002 COLORREF bkgnd, corner;
2003 HTHEME theme;
2006 * Get the rectangle for the item.
2008 isVisible = TAB_InternalGetItemRect(infoPtr,
2009 iItem,
2010 &itemRect,
2011 &selectedRect);
2013 if (isVisible)
2015 RECT rUD, rC;
2017 /* Clip UpDown control to not draw over it */
2018 if (infoPtr->needsScrolling)
2020 GetWindowRect(infoPtr->hwnd, &rC);
2021 GetWindowRect(infoPtr->hwndUpDown, &rUD);
2022 ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top);
2025 /* If you need to see what the control is doing,
2026 * then override these variables. They will change what
2027 * fill colors are used for filling the tabs, and the
2028 * corners when drawing the edge.
2030 bkgnd = comctl32_color.clrBtnFace;
2031 corner = comctl32_color.clrBtnFace;
2033 if (infoPtr->dwStyle & TCS_BUTTONS)
2035 /* Get item rectangle */
2036 r = itemRect;
2038 /* Separators between flat buttons */
2039 if ((infoPtr->dwStyle & TCS_FLATBUTTONS) && (infoPtr->exStyle & TCS_EX_FLATSEPARATORS))
2041 r1 = r;
2042 r1.right += (FLAT_BTN_SPACINGX -2);
2043 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
2046 if (iItem == infoPtr->iSelected)
2048 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
2050 OffsetRect(&r, 1, 1);
2052 else /* ! selected */
2054 DWORD state = TAB_GetItem(infoPtr, iItem)->dwState;
2056 if ((state & TCIS_BUTTONPRESSED) || (iItem == infoPtr->uFocus))
2057 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
2058 else
2059 if (!(infoPtr->dwStyle & TCS_FLATBUTTONS))
2060 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
2063 else /* !TCS_BUTTONS */
2065 /* We draw a rectangle of different sizes depending on the selection
2066 * state. */
2067 if (iItem == infoPtr->iSelected) {
2068 RECT rect;
2069 GetClientRect (infoPtr->hwnd, &rect);
2070 clRight = rect.right;
2071 clBottom = rect.bottom;
2072 r = selectedRect;
2074 else
2075 r = itemRect;
2078 * Erase the background. (Delay it but setup rectangle.)
2079 * This is necessary when drawing the selected item since it is larger
2080 * than the others, it might overlap with stuff already drawn by the
2081 * other tabs
2083 fillRect = r;
2085 /* Draw themed tabs - but only if they are at the top.
2086 * Windows draws even side or bottom tabs themed, with wacky results.
2087 * However, since in Wine apps may get themed that did not opt in via
2088 * a manifest avoid theming when we know the result will be wrong */
2089 if ((theme = GetWindowTheme (infoPtr->hwnd))
2090 && ((infoPtr->dwStyle & (TCS_VERTICAL | TCS_BOTTOM)) == 0))
2092 static const int partIds[8] = {
2093 /* Normal item */
2094 TABP_TABITEM,
2095 TABP_TABITEMLEFTEDGE,
2096 TABP_TABITEMRIGHTEDGE,
2097 TABP_TABITEMBOTHEDGE,
2098 /* Selected tab */
2099 TABP_TOPTABITEM,
2100 TABP_TOPTABITEMLEFTEDGE,
2101 TABP_TOPTABITEMRIGHTEDGE,
2102 TABP_TOPTABITEMBOTHEDGE,
2104 int partIndex = 0;
2105 int stateId = TIS_NORMAL;
2107 /* selected and unselected tabs have different parts */
2108 if (iItem == infoPtr->iSelected)
2109 partIndex += 4;
2110 /* The part also differs on the position of a tab on a line.
2111 * "Visually" determining the position works well enough. */
2112 if(selectedRect.left == 0)
2113 partIndex += 1;
2114 if(selectedRect.right == clRight)
2115 partIndex += 2;
2117 if (iItem == infoPtr->iSelected)
2118 stateId = TIS_SELECTED;
2119 else if (iItem == infoPtr->iHotTracked)
2120 stateId = TIS_HOT;
2121 else if (iItem == infoPtr->uFocus)
2122 stateId = TIS_FOCUSED;
2124 /* Adjust rectangle for bottommost row */
2125 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2126 r.bottom += 3;
2128 DrawThemeBackground (theme, hdc, partIds[partIndex], stateId, &r, NULL);
2129 GetThemeBackgroundContentRect (theme, hdc, partIds[partIndex], stateId, &r, &r);
2131 else if(infoPtr->dwStyle & TCS_VERTICAL)
2133 /* These are for adjusting the drawing of a Selected tab */
2134 /* The initial values are for the normal case of non-Selected */
2135 int ZZ = 1; /* Do not stretch if selected */
2136 if (iItem == infoPtr->iSelected) {
2137 ZZ = 0;
2139 /* if leftmost draw the line longer */
2140 if(selectedRect.top == 0)
2141 fillRect.top += CONTROL_BORDER_SIZEY;
2142 /* if rightmost draw the line longer */
2143 if(selectedRect.bottom == clBottom)
2144 fillRect.bottom -= CONTROL_BORDER_SIZEY;
2147 if (infoPtr->dwStyle & TCS_BOTTOM)
2149 /* Adjust both rectangles to match native */
2150 r.left += (1-ZZ);
2152 TRACE("<right> item=%d, fill=(%s), edge=(%s)\n",
2153 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r));
2155 /* Clear interior */
2156 SetBkColor(hdc, bkgnd);
2157 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2159 /* Draw rectangular edge around tab */
2160 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
2162 /* Now erase the top corner and draw diagonal edge */
2163 SetBkColor(hdc, corner);
2164 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2165 r1.top = r.top;
2166 r1.right = r.right;
2167 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2168 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2169 r1.right--;
2170 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2172 /* Now erase the bottom corner and draw diagonal edge */
2173 r1.left = r.right - ROUND_CORNER_SIZE - 1;
2174 r1.bottom = r.bottom;
2175 r1.right = r.right;
2176 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2177 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2178 r1.right--;
2179 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2181 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
2182 r1 = r;
2183 r1.right = r1.left;
2184 r1.left--;
2185 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
2189 else
2191 TRACE("<left> item=%d, fill=(%s), edge=(%s)\n",
2192 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r));
2194 /* Clear interior */
2195 SetBkColor(hdc, bkgnd);
2196 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2198 /* Draw rectangular edge around tab */
2199 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
2201 /* Now erase the top corner and draw diagonal edge */
2202 SetBkColor(hdc, corner);
2203 r1.left = r.left;
2204 r1.top = r.top;
2205 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2206 r1.bottom = r1.top + ROUND_CORNER_SIZE;
2207 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2208 r1.left++;
2209 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2211 /* Now erase the bottom corner and draw diagonal edge */
2212 r1.left = r.left;
2213 r1.bottom = r.bottom;
2214 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
2215 r1.top = r1.bottom - ROUND_CORNER_SIZE;
2216 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2217 r1.left++;
2218 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
2221 else /* ! TCS_VERTICAL */
2223 /* These are for adjusting the drawing of a Selected tab */
2224 /* The initial values are for the normal case of non-Selected */
2225 if (iItem == infoPtr->iSelected) {
2226 /* if leftmost draw the line longer */
2227 if(selectedRect.left == 0)
2228 fillRect.left += CONTROL_BORDER_SIZEX;
2229 /* if rightmost draw the line longer */
2230 if(selectedRect.right == clRight)
2231 fillRect.right -= CONTROL_BORDER_SIZEX;
2234 if (infoPtr->dwStyle & TCS_BOTTOM)
2236 /* Adjust both rectangles for topmost row */
2237 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2239 fillRect.top -= 2;
2240 r.top -= 1;
2243 TRACE("<bottom> item=%d, fill=(%s), edge=(%s)\n",
2244 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r));
2246 /* Clear interior */
2247 SetBkColor(hdc, bkgnd);
2248 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2250 /* Draw rectangular edge around tab */
2251 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
2253 /* Now erase the righthand corner and draw diagonal edge */
2254 SetBkColor(hdc, corner);
2255 r1.left = r.right - ROUND_CORNER_SIZE;
2256 r1.bottom = r.bottom;
2257 r1.right = r.right;
2258 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2259 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2260 r1.bottom--;
2261 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
2263 /* Now erase the lefthand corner and draw diagonal edge */
2264 r1.left = r.left;
2265 r1.bottom = r.bottom;
2266 r1.right = r1.left + ROUND_CORNER_SIZE;
2267 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
2268 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2269 r1.bottom--;
2270 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
2272 if (iItem == infoPtr->iSelected)
2274 r.top += 2;
2275 r.left += 1;
2276 if (selectedRect.left == 0)
2278 r1 = r;
2279 r1.bottom = r1.top;
2280 r1.top--;
2281 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2286 else
2288 /* Adjust both rectangles for bottommost row */
2289 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1)
2291 fillRect.bottom += 3;
2292 r.bottom += 2;
2295 TRACE("<top> item=%d, fill=(%s), edge=(%s)\n",
2296 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r));
2298 /* Clear interior */
2299 SetBkColor(hdc, bkgnd);
2300 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2302 /* Draw rectangular edge around tab */
2303 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2305 /* Now erase the righthand corner and draw diagonal edge */
2306 SetBkColor(hdc, corner);
2307 r1.left = r.right - ROUND_CORNER_SIZE;
2308 r1.top = r.top;
2309 r1.right = r.right;
2310 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2311 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2312 r1.top++;
2313 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2315 /* Now erase the lefthand corner and draw diagonal edge */
2316 r1.left = r.left;
2317 r1.top = r.top;
2318 r1.right = r1.left + ROUND_CORNER_SIZE;
2319 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2320 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2321 r1.top++;
2322 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2327 TAB_DumpItemInternal(infoPtr, iItem);
2329 /* This modifies r to be the text rectangle. */
2330 TAB_DrawItemInterior(infoPtr, hdc, iItem, &r);
2334 /******************************************************************************
2335 * TAB_DrawBorder
2337 * This method is used to draw the raised border around the tab control
2338 * "content" area.
2340 static void TAB_DrawBorder(const TAB_INFO *infoPtr, HDC hdc)
2342 RECT rect;
2343 HTHEME theme = GetWindowTheme (infoPtr->hwnd);
2345 GetClientRect (infoPtr->hwnd, &rect);
2348 * Adjust for the style
2351 if (infoPtr->uNumItem)
2353 if ((infoPtr->dwStyle & TCS_BOTTOM) && !(infoPtr->dwStyle & TCS_VERTICAL))
2354 rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2355 else if((infoPtr->dwStyle & TCS_BOTTOM) && (infoPtr->dwStyle & TCS_VERTICAL))
2356 rect.right -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2357 else if(infoPtr->dwStyle & TCS_VERTICAL)
2358 rect.left += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2359 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2360 rect.top += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX;
2363 TRACE("border=(%s)\n", wine_dbgstr_rect(&rect));
2365 if (theme)
2366 DrawThemeBackground (theme, hdc, TABP_PANE, 0, &rect, NULL);
2367 else
2368 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2371 /******************************************************************************
2372 * TAB_Refresh
2374 * This method repaints the tab control..
2376 static void TAB_Refresh (const TAB_INFO *infoPtr, HDC hdc)
2378 HFONT hOldFont;
2379 INT i;
2381 if (!infoPtr->DoRedraw)
2382 return;
2384 hOldFont = SelectObject (hdc, infoPtr->hFont);
2386 if (infoPtr->dwStyle & TCS_BUTTONS)
2388 for (i = 0; i < infoPtr->uNumItem; i++)
2389 TAB_DrawItem (infoPtr, hdc, i);
2391 else
2393 /* Draw all the non selected item first */
2394 for (i = 0; i < infoPtr->uNumItem; i++)
2396 if (i != infoPtr->iSelected)
2397 TAB_DrawItem (infoPtr, hdc, i);
2400 /* Now, draw the border, draw it before the selected item
2401 * since the selected item overwrites part of the border. */
2402 TAB_DrawBorder (infoPtr, hdc);
2404 /* Then, draw the selected item */
2405 TAB_DrawItem (infoPtr, hdc, infoPtr->iSelected);
2408 SelectObject (hdc, hOldFont);
2411 static inline DWORD TAB_GetRowCount (const TAB_INFO *infoPtr)
2413 TRACE("(%p)\n", infoPtr);
2414 return infoPtr->uNumRows;
2417 static inline LRESULT TAB_SetRedraw (TAB_INFO *infoPtr, BOOL doRedraw)
2419 infoPtr->DoRedraw = doRedraw;
2420 return 0;
2423 /******************************************************************************
2424 * TAB_EnsureSelectionVisible
2426 * This method will make sure that the current selection is completely
2427 * visible by scrolling until it is.
2429 static void TAB_EnsureSelectionVisible(
2430 TAB_INFO* infoPtr)
2432 INT iSelected = infoPtr->iSelected;
2433 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2435 /* set the items row to the bottommost row or topmost row depending on
2436 * style */
2437 if ((infoPtr->uNumRows > 1) && !(infoPtr->dwStyle & TCS_BUTTONS))
2439 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2440 INT newselected;
2441 INT iTargetRow;
2443 if(infoPtr->dwStyle & TCS_VERTICAL)
2444 newselected = selected->rect.left;
2445 else
2446 newselected = selected->rect.top;
2448 /* the target row is always (number of rows - 1)
2449 as row 0 is furthest from the clientRect */
2450 iTargetRow = infoPtr->uNumRows - 1;
2452 if (newselected != iTargetRow)
2454 UINT i;
2455 if(infoPtr->dwStyle & TCS_VERTICAL)
2457 for (i=0; i < infoPtr->uNumItem; i++)
2459 /* move everything in the row of the selected item to the iTargetRow */
2460 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2462 if (item->rect.left == newselected )
2463 item->rect.left = iTargetRow;
2464 else
2466 if (item->rect.left > newselected)
2467 item->rect.left-=1;
2471 else
2473 for (i=0; i < infoPtr->uNumItem; i++)
2475 TAB_ITEM *item = TAB_GetItem(infoPtr, i);
2477 if (item->rect.top == newselected )
2478 item->rect.top = iTargetRow;
2479 else
2481 if (item->rect.top > newselected)
2482 item->rect.top-=1;
2486 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2491 * Do the trivial cases first.
2493 if ( (!infoPtr->needsScrolling) ||
2494 (infoPtr->hwndUpDown==0) || (infoPtr->dwStyle & TCS_VERTICAL))
2495 return;
2497 if (infoPtr->leftmostVisible >= iSelected)
2499 infoPtr->leftmostVisible = iSelected;
2501 else
2503 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected);
2504 RECT r;
2505 INT width;
2506 UINT i;
2508 /* Calculate the part of the client area that is visible */
2509 GetClientRect(infoPtr->hwnd, &r);
2510 width = r.right;
2512 GetClientRect(infoPtr->hwndUpDown, &r);
2513 width -= r.right;
2515 if ((selected->rect.right -
2516 selected->rect.left) >= width )
2518 /* Special case: width of selected item is greater than visible
2519 * part of control.
2521 infoPtr->leftmostVisible = iSelected;
2523 else
2525 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2527 if ((selected->rect.right - TAB_GetItem(infoPtr, i)->rect.left) < width)
2528 break;
2530 infoPtr->leftmostVisible = i;
2534 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2535 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL);
2537 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2538 MAKELONG(infoPtr->leftmostVisible, 0));
2541 /******************************************************************************
2542 * TAB_InvalidateTabArea
2544 * This method will invalidate the portion of the control that contains the
2545 * tabs. It is called when the state of the control changes and needs
2546 * to be redisplayed
2548 static void TAB_InvalidateTabArea(const TAB_INFO *infoPtr)
2550 RECT clientRect, rInvalidate, rAdjClient;
2551 INT lastRow = infoPtr->uNumRows - 1;
2552 RECT rect;
2554 if (lastRow < 0) return;
2556 GetClientRect(infoPtr->hwnd, &clientRect);
2557 rInvalidate = clientRect;
2558 rAdjClient = clientRect;
2560 TAB_AdjustRect(infoPtr, 0, &rAdjClient);
2562 TAB_InternalGetItemRect(infoPtr, infoPtr->uNumItem-1 , &rect, NULL);
2563 if ((infoPtr->dwStyle & TCS_BOTTOM) && (infoPtr->dwStyle & TCS_VERTICAL))
2565 rInvalidate.left = rAdjClient.right;
2566 if (infoPtr->uNumRows == 1)
2567 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2569 else if(infoPtr->dwStyle & TCS_VERTICAL)
2571 rInvalidate.right = rAdjClient.left;
2572 if (infoPtr->uNumRows == 1)
2573 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET;
2575 else if (infoPtr->dwStyle & TCS_BOTTOM)
2577 rInvalidate.top = rAdjClient.bottom;
2578 if (infoPtr->uNumRows == 1)
2579 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2581 else
2583 rInvalidate.bottom = rAdjClient.top;
2584 if (infoPtr->uNumRows == 1)
2585 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET;
2588 /* Punch out the updown control */
2589 if (infoPtr->needsScrolling && (rInvalidate.right > 0)) {
2590 RECT r;
2591 GetClientRect(infoPtr->hwndUpDown, &r);
2592 if (rInvalidate.right > clientRect.right - r.left)
2593 rInvalidate.right = rInvalidate.right - (r.right - r.left);
2594 else
2595 rInvalidate.right = clientRect.right - r.left;
2598 TRACE("invalidate (%s)\n", wine_dbgstr_rect(&rInvalidate));
2600 InvalidateRect(infoPtr->hwnd, &rInvalidate, TRUE);
2603 static inline LRESULT TAB_Paint (TAB_INFO *infoPtr, HDC hdcPaint)
2605 HDC hdc;
2606 PAINTSTRUCT ps;
2608 if (hdcPaint)
2609 hdc = hdcPaint;
2610 else
2612 hdc = BeginPaint (infoPtr->hwnd, &ps);
2613 TRACE("erase %d, rect=(%s)\n", ps.fErase, wine_dbgstr_rect(&ps.rcPaint));
2616 TAB_Refresh (infoPtr, hdc);
2618 if (!hdcPaint)
2619 EndPaint (infoPtr->hwnd, &ps);
2621 return 0;
2624 static LRESULT
2625 TAB_InsertItemT (TAB_INFO *infoPtr, INT iItem, const TCITEMW *pti, BOOL bUnicode)
2627 TAB_ITEM *item;
2628 RECT rect;
2630 GetClientRect (infoPtr->hwnd, &rect);
2631 TRACE("Rect: %p %s\n", infoPtr->hwnd, wine_dbgstr_rect(&rect));
2633 if (iItem < 0) return -1;
2634 if (iItem > infoPtr->uNumItem)
2635 iItem = infoPtr->uNumItem;
2637 TAB_DumpItemExternalT(pti, iItem, bUnicode);
2640 if (infoPtr->uNumItem == 0) {
2641 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr));
2642 infoPtr->uNumItem++;
2643 infoPtr->iSelected = 0;
2645 else {
2646 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2648 infoPtr->uNumItem++;
2649 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2651 /* pre insert copy */
2652 if (iItem > 0) {
2653 memcpy (infoPtr->items, oldItems,
2654 iItem * TAB_ITEM_SIZE(infoPtr));
2657 /* post insert copy */
2658 if (iItem < infoPtr->uNumItem - 1) {
2659 memcpy (TAB_GetItem(infoPtr, iItem + 1),
2660 oldItems + iItem * TAB_ITEM_SIZE(infoPtr),
2661 (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr));
2665 if (iItem <= infoPtr->iSelected)
2666 infoPtr->iSelected++;
2668 Free (oldItems);
2671 item = TAB_GetItem(infoPtr, iItem);
2673 item->pszText = NULL;
2675 if (pti->mask & TCIF_TEXT)
2677 if (bUnicode)
2678 Str_SetPtrW (&item->pszText, pti->pszText);
2679 else
2680 Str_SetPtrAtoW (&item->pszText, (LPSTR)pti->pszText);
2683 if (pti->mask & TCIF_IMAGE)
2684 item->iImage = pti->iImage;
2685 else
2686 item->iImage = -1;
2688 if (pti->mask & TCIF_PARAM)
2689 memcpy(item->extra, &pti->lParam, infoPtr->cbInfo);
2690 else
2691 memset(item->extra, 0, infoPtr->cbInfo);
2693 TAB_SetItemBounds(infoPtr);
2694 if (infoPtr->uNumItem > 1)
2695 TAB_InvalidateTabArea(infoPtr);
2696 else
2697 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2699 TRACE("[%p]: added item %d %s\n",
2700 infoPtr->hwnd, iItem, debugstr_w(item->pszText));
2702 /* If we haven't set the current focus yet, set it now. */
2703 if (infoPtr->uFocus == -1)
2704 TAB_SetCurFocus(infoPtr, iItem);
2706 return iItem;
2709 static LRESULT
2710 TAB_SetItemSize (TAB_INFO *infoPtr, INT cx, INT cy)
2712 LONG lResult = 0;
2713 BOOL bNeedPaint = FALSE;
2715 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2717 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */
2718 if (infoPtr->dwStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != cx))
2720 infoPtr->tabWidth = cx;
2721 bNeedPaint = TRUE;
2724 if (infoPtr->tabHeight != cy)
2726 if ((infoPtr->fHeightSet = (cy != 0)))
2727 infoPtr->tabHeight = cy;
2729 bNeedPaint = TRUE;
2731 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2732 HIWORD(lResult), LOWORD(lResult),
2733 infoPtr->tabHeight, infoPtr->tabWidth);
2735 if (bNeedPaint)
2737 TAB_SetItemBounds(infoPtr);
2738 RedrawWindow(infoPtr->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
2741 return lResult;
2744 static inline LRESULT TAB_SetMinTabWidth (TAB_INFO *infoPtr, INT cx)
2746 INT oldcx = 0;
2748 TRACE("(%p,%d)\n", infoPtr, cx);
2750 if (infoPtr->tabMinWidth < 0)
2751 oldcx = DEFAULT_MIN_TAB_WIDTH;
2752 else
2753 oldcx = infoPtr->tabMinWidth;
2754 infoPtr->tabMinWidth = cx;
2755 TAB_SetItemBounds(infoPtr);
2756 return oldcx;
2759 static inline LRESULT
2760 TAB_HighlightItem (TAB_INFO *infoPtr, INT iItem, BOOL fHighlight)
2762 LPDWORD lpState;
2763 DWORD oldState;
2764 RECT r;
2766 TRACE("(%p,%d,%s)\n", infoPtr, iItem, fHighlight ? "true" : "false");
2768 if (iItem < 0 || iItem >= infoPtr->uNumItem)
2769 return FALSE;
2771 lpState = &TAB_GetItem(infoPtr, iItem)->dwState;
2772 oldState = *lpState;
2774 if (fHighlight)
2775 *lpState |= TCIS_HIGHLIGHTED;
2776 else
2777 *lpState &= ~TCIS_HIGHLIGHTED;
2779 if ((oldState != *lpState) && TAB_InternalGetItemRect (infoPtr, iItem, &r, NULL))
2780 InvalidateRect (infoPtr->hwnd, &r, TRUE);
2782 return TRUE;
2785 static LRESULT
2786 TAB_SetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2788 TAB_ITEM *wineItem;
2790 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2792 if (iItem < 0 || iItem >= infoPtr->uNumItem)
2793 return FALSE;
2795 TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2797 wineItem = TAB_GetItem(infoPtr, iItem);
2799 if (tabItem->mask & TCIF_IMAGE)
2800 wineItem->iImage = tabItem->iImage;
2802 if (tabItem->mask & TCIF_PARAM)
2803 memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo);
2805 if (tabItem->mask & TCIF_RTLREADING)
2806 FIXME("TCIF_RTLREADING\n");
2808 if (tabItem->mask & TCIF_STATE)
2809 wineItem->dwState = (wineItem->dwState & ~tabItem->dwStateMask) |
2810 ( tabItem->dwState & tabItem->dwStateMask);
2812 if (tabItem->mask & TCIF_TEXT)
2814 Free(wineItem->pszText);
2815 wineItem->pszText = NULL;
2816 if (bUnicode)
2817 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2818 else
2819 Str_SetPtrAtoW(&wineItem->pszText, (LPSTR)tabItem->pszText);
2822 /* Update and repaint tabs */
2823 TAB_SetItemBounds(infoPtr);
2824 TAB_InvalidateTabArea(infoPtr);
2826 return TRUE;
2829 static inline LRESULT TAB_GetItemCount (const TAB_INFO *infoPtr)
2831 TRACE("\n");
2832 return infoPtr->uNumItem;
2836 static LRESULT
2837 TAB_GetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode)
2839 TAB_ITEM *wineItem;
2841 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false");
2843 if (!tabItem) return FALSE;
2845 if (iItem < 0 || iItem >= infoPtr->uNumItem)
2847 /* init requested fields */
2848 if (tabItem->mask & TCIF_IMAGE) tabItem->iImage = 0;
2849 if (tabItem->mask & TCIF_PARAM) tabItem->lParam = 0;
2850 if (tabItem->mask & TCIF_STATE) tabItem->dwState = 0;
2851 return FALSE;
2854 wineItem = TAB_GetItem(infoPtr, iItem);
2856 if (tabItem->mask & TCIF_IMAGE)
2857 tabItem->iImage = wineItem->iImage;
2859 if (tabItem->mask & TCIF_PARAM)
2860 memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo);
2862 if (tabItem->mask & TCIF_RTLREADING)
2863 FIXME("TCIF_RTLREADING\n");
2865 if (tabItem->mask & TCIF_STATE)
2866 tabItem->dwState = wineItem->dwState & tabItem->dwStateMask;
2868 if (tabItem->mask & TCIF_TEXT)
2870 if (bUnicode)
2871 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2872 else
2873 Str_GetPtrWtoA (wineItem->pszText, (LPSTR)tabItem->pszText, tabItem->cchTextMax);
2876 TAB_DumpItemExternalT(tabItem, iItem, bUnicode);
2878 return TRUE;
2882 static LRESULT TAB_DeleteItem (TAB_INFO *infoPtr, INT iItem)
2884 BOOL bResult = FALSE;
2886 TRACE("(%p, %d)\n", infoPtr, iItem);
2888 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2890 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem);
2891 LPBYTE oldItems = (LPBYTE)infoPtr->items;
2893 TAB_InvalidateTabArea(infoPtr);
2894 Free(item->pszText);
2895 infoPtr->uNumItem--;
2897 if (!infoPtr->uNumItem)
2899 infoPtr->items = NULL;
2900 if (infoPtr->iHotTracked >= 0)
2902 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
2903 infoPtr->iHotTracked = -1;
2906 else
2908 infoPtr->items = Alloc(TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem);
2910 if (iItem > 0)
2911 memcpy(infoPtr->items, oldItems, iItem * TAB_ITEM_SIZE(infoPtr));
2913 if (iItem < infoPtr->uNumItem)
2914 memcpy(TAB_GetItem(infoPtr, iItem),
2915 oldItems + (iItem + 1) * TAB_ITEM_SIZE(infoPtr),
2916 (infoPtr->uNumItem - iItem) * TAB_ITEM_SIZE(infoPtr));
2918 if (iItem <= infoPtr->iHotTracked)
2920 /* When tabs move left/up, the hot track item may change */
2921 FIXME("Recalc hot track\n");
2924 Free(oldItems);
2926 /* Readjust the selected index */
2927 if (iItem == infoPtr->iSelected)
2928 infoPtr->iSelected = -1;
2929 else if (iItem < infoPtr->iSelected)
2930 infoPtr->iSelected--;
2932 if (infoPtr->uNumItem == 0)
2933 infoPtr->iSelected = -1;
2935 /* Reposition and repaint tabs */
2936 TAB_SetItemBounds(infoPtr);
2938 bResult = TRUE;
2941 return bResult;
2944 static inline LRESULT TAB_DeleteAllItems (TAB_INFO *infoPtr)
2946 TRACE("(%p)\n", infoPtr);
2947 while (infoPtr->uNumItem)
2948 TAB_DeleteItem (infoPtr, 0);
2949 return TRUE;
2953 static inline LRESULT TAB_GetFont (const TAB_INFO *infoPtr)
2955 TRACE("(%p) returning %p\n", infoPtr, infoPtr->hFont);
2956 return (LRESULT)infoPtr->hFont;
2959 static inline LRESULT TAB_SetFont (TAB_INFO *infoPtr, HFONT hNewFont)
2961 TRACE("(%p,%p)\n", infoPtr, hNewFont);
2963 infoPtr->hFont = hNewFont;
2965 TAB_SetItemBounds(infoPtr);
2967 TAB_InvalidateTabArea(infoPtr);
2969 return 0;
2973 static inline LRESULT TAB_GetImageList (const TAB_INFO *infoPtr)
2975 TRACE("\n");
2976 return (LRESULT)infoPtr->himl;
2979 static inline LRESULT TAB_SetImageList (TAB_INFO *infoPtr, HIMAGELIST himlNew)
2981 HIMAGELIST himlPrev = infoPtr->himl;
2982 TRACE("himl=%p\n", himlNew);
2983 infoPtr->himl = himlNew;
2984 TAB_SetItemBounds(infoPtr);
2985 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2986 return (LRESULT)himlPrev;
2989 static inline LRESULT TAB_GetUnicodeFormat (const TAB_INFO *infoPtr)
2991 TRACE("(%p)\n", infoPtr);
2992 return infoPtr->bUnicode;
2995 static inline LRESULT TAB_SetUnicodeFormat (TAB_INFO *infoPtr, BOOL bUnicode)
2997 BOOL bTemp = infoPtr->bUnicode;
2999 TRACE("(%p %d)\n", infoPtr, bUnicode);
3000 infoPtr->bUnicode = bUnicode;
3002 return bTemp;
3005 static inline LRESULT TAB_Size (TAB_INFO *infoPtr)
3007 /* I'm not really sure what the following code was meant to do.
3008 This is what it is doing:
3009 When WM_SIZE is sent with SIZE_RESTORED, the control
3010 gets positioned in the top left corner.
3012 RECT parent_rect;
3013 HWND parent;
3014 UINT uPosFlags,cx,cy;
3016 uPosFlags=0;
3017 if (!wParam) {
3018 parent = GetParent (hwnd);
3019 GetClientRect(parent, &parent_rect);
3020 cx=LOWORD (lParam);
3021 cy=HIWORD (lParam);
3022 if (GetWindowLongW(hwnd, GWL_STYLE) & CCS_NORESIZE)
3023 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
3025 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
3026 cx, cy, uPosFlags | SWP_NOZORDER);
3027 } else {
3028 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
3029 } */
3031 /* Recompute the size/position of the tabs. */
3032 TAB_SetItemBounds (infoPtr);
3034 /* Force a repaint of the control. */
3035 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
3037 return 0;
3041 static LRESULT TAB_Create (HWND hwnd, LPARAM lParam)
3043 TAB_INFO *infoPtr;
3044 TEXTMETRICW fontMetrics;
3045 HDC hdc;
3046 HFONT hOldFont;
3047 DWORD dwStyle;
3049 infoPtr = Alloc (sizeof(TAB_INFO));
3051 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
3053 infoPtr->hwnd = hwnd;
3054 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
3055 infoPtr->uNumItem = 0;
3056 infoPtr->uNumRows = 0;
3057 infoPtr->uHItemPadding = 6;
3058 infoPtr->uVItemPadding = 3;
3059 infoPtr->uHItemPadding_s = 6;
3060 infoPtr->uVItemPadding_s = 3;
3061 infoPtr->hFont = 0;
3062 infoPtr->items = 0;
3063 infoPtr->hcurArrow = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3064 infoPtr->iSelected = -1;
3065 infoPtr->iHotTracked = -1;
3066 infoPtr->uFocus = -1;
3067 infoPtr->hwndToolTip = 0;
3068 infoPtr->DoRedraw = TRUE;
3069 infoPtr->needsScrolling = FALSE;
3070 infoPtr->hwndUpDown = 0;
3071 infoPtr->leftmostVisible = 0;
3072 infoPtr->fHeightSet = FALSE;
3073 infoPtr->bUnicode = IsWindowUnicode (hwnd);
3074 infoPtr->cbInfo = sizeof(LPARAM);
3076 TRACE("Created tab control, hwnd [%p]\n", hwnd);
3078 /* The tab control always has the WS_CLIPSIBLINGS style. Even
3079 if you don't specify it in CreateWindow. This is necessary in
3080 order for paint to work correctly. This follows windows behaviour. */
3081 dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
3082 SetWindowLongW(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
3084 infoPtr->dwStyle = dwStyle | WS_CLIPSIBLINGS;
3085 infoPtr->exStyle = (dwStyle & TCS_FLATBUTTONS) ? TCS_EX_FLATSEPARATORS : 0;
3087 if (infoPtr->dwStyle & TCS_TOOLTIPS) {
3088 /* Create tooltip control */
3089 infoPtr->hwndToolTip =
3090 CreateWindowExW (0, TOOLTIPS_CLASSW, NULL, WS_POPUP,
3091 CW_USEDEFAULT, CW_USEDEFAULT,
3092 CW_USEDEFAULT, CW_USEDEFAULT,
3093 hwnd, 0, 0, 0);
3095 /* Send NM_TOOLTIPSCREATED notification */
3096 if (infoPtr->hwndToolTip) {
3097 NMTOOLTIPSCREATED nmttc;
3099 nmttc.hdr.hwndFrom = hwnd;
3100 nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
3101 nmttc.hdr.code = NM_TOOLTIPSCREATED;
3102 nmttc.hwndToolTips = infoPtr->hwndToolTip;
3104 SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
3105 GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc);
3109 OpenThemeData (infoPtr->hwnd, themeClass);
3112 * We need to get text information so we need a DC and we need to select
3113 * a font.
3115 hdc = GetDC(hwnd);
3116 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
3118 /* Use the system font to determine the initial height of a tab. */
3119 GetTextMetricsW(hdc, &fontMetrics);
3122 * Make sure there is enough space for the letters + growing the
3123 * selected item + extra space for the selected item.
3125 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
3126 ((infoPtr->dwStyle & TCS_BUTTONS) ? 2 : 1) *
3127 infoPtr->uVItemPadding;
3129 /* Initialize the width of a tab. */
3130 if (infoPtr->dwStyle & TCS_FIXEDWIDTH)
3131 infoPtr->tabWidth = GetDeviceCaps(hdc, LOGPIXELSX);
3133 infoPtr->tabMinWidth = -1;
3135 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
3137 SelectObject (hdc, hOldFont);
3138 ReleaseDC(hwnd, hdc);
3140 return 0;
3143 static LRESULT
3144 TAB_Destroy (TAB_INFO *infoPtr)
3146 UINT iItem;
3148 SetWindowLongPtrW(infoPtr->hwnd, 0, 0);
3150 if (infoPtr->items) {
3151 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
3152 Free (TAB_GetItem(infoPtr, iItem)->pszText);
3154 Free (infoPtr->items);
3157 if (infoPtr->hwndToolTip)
3158 DestroyWindow (infoPtr->hwndToolTip);
3160 if (infoPtr->hwndUpDown)
3161 DestroyWindow(infoPtr->hwndUpDown);
3163 if (infoPtr->iHotTracked >= 0)
3164 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
3166 CloseThemeData (GetWindowTheme (infoPtr->hwnd));
3168 Free (infoPtr);
3169 return 0;
3172 /* update theme after a WM_THEMECHANGED message */
3173 static LRESULT theme_changed(const TAB_INFO *infoPtr)
3175 HTHEME theme = GetWindowTheme (infoPtr->hwnd);
3176 CloseThemeData (theme);
3177 OpenThemeData (infoPtr->hwnd, themeClass);
3178 return 0;
3181 static LRESULT TAB_NCCalcSize(WPARAM wParam)
3183 if (!wParam)
3184 return 0;
3185 return WVR_ALIGNTOP;
3188 static inline LRESULT
3189 TAB_SetItemExtra (TAB_INFO *infoPtr, INT cbInfo)
3191 TRACE("(%p %d)\n", infoPtr, cbInfo);
3193 if (cbInfo <= 0)
3194 return FALSE;
3196 if (infoPtr->uNumItem)
3198 /* FIXME: MSDN says this is not allowed, but this hasn't been verified */
3199 return FALSE;
3202 infoPtr->cbInfo = cbInfo;
3203 return TRUE;
3206 static LRESULT TAB_RemoveImage (TAB_INFO *infoPtr, INT image)
3208 TRACE("%p %d\n", infoPtr, image);
3210 if (ImageList_Remove (infoPtr->himl, image))
3212 INT i, *idx;
3213 RECT r;
3215 /* shift indices, repaint items if needed */
3216 for (i = 0; i < infoPtr->uNumItem; i++)
3218 idx = &TAB_GetItem(infoPtr, i)->iImage;
3219 if (*idx >= image)
3221 if (*idx == image)
3222 *idx = -1;
3223 else
3224 (*idx)--;
3226 /* repaint item */
3227 if (TAB_InternalGetItemRect (infoPtr, i, &r, NULL))
3228 InvalidateRect (infoPtr->hwnd, &r, TRUE);
3233 return 0;
3236 static LRESULT
3237 TAB_SetExtendedStyle (TAB_INFO *infoPtr, DWORD exMask, DWORD exStyle)
3239 DWORD prevstyle = infoPtr->exStyle;
3241 /* zero mask means all styles */
3242 if (exMask == 0) exMask = ~0;
3244 if (exMask & TCS_EX_REGISTERDROP)
3246 FIXME("TCS_EX_REGISTERDROP style unimplemented\n");
3247 exMask &= ~TCS_EX_REGISTERDROP;
3248 exStyle &= ~TCS_EX_REGISTERDROP;
3251 if (exMask & TCS_EX_FLATSEPARATORS)
3253 if ((prevstyle ^ exStyle) & TCS_EX_FLATSEPARATORS)
3255 infoPtr->exStyle ^= TCS_EX_FLATSEPARATORS;
3256 TAB_InvalidateTabArea(infoPtr);
3260 return prevstyle;
3263 static inline LRESULT
3264 TAB_GetExtendedStyle (const TAB_INFO *infoPtr)
3266 return infoPtr->exStyle;
3269 static LRESULT
3270 TAB_DeselectAll (TAB_INFO *infoPtr, BOOL excludesel)
3272 BOOL paint = FALSE;
3273 INT i, selected = infoPtr->iSelected;
3275 TRACE("(%p, %d)\n", infoPtr, excludesel);
3277 if (!(infoPtr->dwStyle & TCS_BUTTONS))
3278 return 0;
3280 for (i = 0; i < infoPtr->uNumItem; i++)
3282 if ((TAB_GetItem(infoPtr, i)->dwState & TCIS_BUTTONPRESSED) &&
3283 (selected != i))
3285 TAB_GetItem(infoPtr, i)->dwState &= ~TCIS_BUTTONPRESSED;
3286 paint = TRUE;
3290 if (!excludesel && (selected != -1))
3292 TAB_GetItem(infoPtr, selected)->dwState &= ~TCIS_BUTTONPRESSED;
3293 infoPtr->iSelected = -1;
3294 paint = TRUE;
3297 if (paint)
3298 TAB_InvalidateTabArea (infoPtr);
3300 return 0;
3303 /***
3304 * DESCRIPTION:
3305 * Processes WM_STYLECHANGED messages.
3307 * PARAMETER(S):
3308 * [I] infoPtr : valid pointer to the tab data structure
3309 * [I] wStyleType : window style type (normal or extended)
3310 * [I] lpss : window style information
3312 * RETURN:
3313 * Zero
3315 static INT TAB_StyleChanged(TAB_INFO *infoPtr, WPARAM wStyleType,
3316 const STYLESTRUCT *lpss)
3318 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
3319 wStyleType, lpss->styleOld, lpss->styleNew);
3321 if (wStyleType != GWL_STYLE) return 0;
3323 infoPtr->dwStyle = lpss->styleNew;
3325 TAB_SetItemBounds (infoPtr);
3326 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
3328 return 0;
3331 static LRESULT WINAPI
3332 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3334 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
3336 TRACE("hwnd=%p msg=%x wParam=%lx lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3337 if (!infoPtr && (uMsg != WM_CREATE))
3338 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
3340 switch (uMsg)
3342 case TCM_GETIMAGELIST:
3343 return TAB_GetImageList (infoPtr);
3345 case TCM_SETIMAGELIST:
3346 return TAB_SetImageList (infoPtr, (HIMAGELIST)lParam);
3348 case TCM_GETITEMCOUNT:
3349 return TAB_GetItemCount (infoPtr);
3351 case TCM_GETITEMA:
3352 case TCM_GETITEMW:
3353 return TAB_GetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_GETITEMW);
3355 case TCM_SETITEMA:
3356 case TCM_SETITEMW:
3357 return TAB_SetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_SETITEMW);
3359 case TCM_DELETEITEM:
3360 return TAB_DeleteItem (infoPtr, (INT)wParam);
3362 case TCM_DELETEALLITEMS:
3363 return TAB_DeleteAllItems (infoPtr);
3365 case TCM_GETITEMRECT:
3366 return TAB_GetItemRect (infoPtr, (INT)wParam, (LPRECT)lParam);
3368 case TCM_GETCURSEL:
3369 return TAB_GetCurSel (infoPtr);
3371 case TCM_HITTEST:
3372 return TAB_HitTest (infoPtr, (LPTCHITTESTINFO)lParam);
3374 case TCM_SETCURSEL:
3375 return TAB_SetCurSel (infoPtr, (INT)wParam);
3377 case TCM_INSERTITEMA:
3378 case TCM_INSERTITEMW:
3379 return TAB_InsertItemT (infoPtr, (INT)wParam, (TCITEMW*)lParam, uMsg == TCM_INSERTITEMW);
3381 case TCM_SETITEMEXTRA:
3382 return TAB_SetItemExtra (infoPtr, (INT)wParam);
3384 case TCM_ADJUSTRECT:
3385 return TAB_AdjustRect (infoPtr, (BOOL)wParam, (LPRECT)lParam);
3387 case TCM_SETITEMSIZE:
3388 return TAB_SetItemSize (infoPtr, (INT)LOWORD(lParam), (INT)HIWORD(lParam));
3390 case TCM_REMOVEIMAGE:
3391 return TAB_RemoveImage (infoPtr, (INT)wParam);
3393 case TCM_SETPADDING:
3394 return TAB_SetPadding (infoPtr, lParam);
3396 case TCM_GETROWCOUNT:
3397 return TAB_GetRowCount(infoPtr);
3399 case TCM_GETUNICODEFORMAT:
3400 return TAB_GetUnicodeFormat (infoPtr);
3402 case TCM_SETUNICODEFORMAT:
3403 return TAB_SetUnicodeFormat (infoPtr, (BOOL)wParam);
3405 case TCM_HIGHLIGHTITEM:
3406 return TAB_HighlightItem (infoPtr, (INT)wParam, (BOOL)LOWORD(lParam));
3408 case TCM_GETTOOLTIPS:
3409 return TAB_GetToolTips (infoPtr);
3411 case TCM_SETTOOLTIPS:
3412 return TAB_SetToolTips (infoPtr, (HWND)wParam);
3414 case TCM_GETCURFOCUS:
3415 return TAB_GetCurFocus (infoPtr);
3417 case TCM_SETCURFOCUS:
3418 return TAB_SetCurFocus (infoPtr, (INT)wParam);
3420 case TCM_SETMINTABWIDTH:
3421 return TAB_SetMinTabWidth(infoPtr, (INT)lParam);
3423 case TCM_DESELECTALL:
3424 return TAB_DeselectAll (infoPtr, (BOOL)wParam);
3426 case TCM_GETEXTENDEDSTYLE:
3427 return TAB_GetExtendedStyle (infoPtr);
3429 case TCM_SETEXTENDEDSTYLE:
3430 return TAB_SetExtendedStyle (infoPtr, wParam, lParam);
3432 case WM_GETFONT:
3433 return TAB_GetFont (infoPtr);
3435 case WM_SETFONT:
3436 return TAB_SetFont (infoPtr, (HFONT)wParam);
3438 case WM_CREATE:
3439 return TAB_Create (hwnd, lParam);
3441 case WM_NCDESTROY:
3442 return TAB_Destroy (infoPtr);
3444 case WM_GETDLGCODE:
3445 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3447 case WM_LBUTTONDOWN:
3448 return TAB_LButtonDown (infoPtr, wParam, lParam);
3450 case WM_LBUTTONUP:
3451 return TAB_LButtonUp (infoPtr);
3453 case WM_NOTIFY:
3454 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
3456 case WM_RBUTTONDOWN:
3457 return TAB_RButtonDown (infoPtr);
3459 case WM_MOUSEMOVE:
3460 return TAB_MouseMove (infoPtr, wParam, lParam);
3462 case WM_PRINTCLIENT:
3463 case WM_PAINT:
3464 return TAB_Paint (infoPtr, (HDC)wParam);
3466 case WM_SIZE:
3467 return TAB_Size (infoPtr);
3469 case WM_SETREDRAW:
3470 return TAB_SetRedraw (infoPtr, (BOOL)wParam);
3472 case WM_HSCROLL:
3473 return TAB_OnHScroll(infoPtr, (int)LOWORD(wParam), (int)HIWORD(wParam));
3475 case WM_STYLECHANGED:
3476 return TAB_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
3478 case WM_SYSCOLORCHANGE:
3479 COMCTL32_RefreshSysColors();
3480 return 0;
3482 case WM_THEMECHANGED:
3483 return theme_changed (infoPtr);
3485 case WM_KILLFOCUS:
3486 TAB_KillFocus(infoPtr);
3487 case WM_SETFOCUS:
3488 TAB_FocusChanging(infoPtr);
3489 break; /* Don't disturb normal focus behavior */
3491 case WM_KEYDOWN:
3492 return TAB_KeyDown(infoPtr, wParam, lParam);
3494 case WM_NCHITTEST:
3495 return TAB_NCHitTest(infoPtr, lParam);
3497 case WM_NCCALCSIZE:
3498 return TAB_NCCalcSize(wParam);
3500 default:
3501 if (uMsg >= WM_USER && uMsg < WM_APP && !COMCTL32_IsReflectedMessage(uMsg))
3502 WARN("unknown msg %04x wp=%08lx lp=%08lx\n",
3503 uMsg, wParam, lParam);
3504 break;
3506 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
3510 void
3511 TAB_Register (void)
3513 WNDCLASSW wndClass;
3515 ZeroMemory (&wndClass, sizeof(WNDCLASSW));
3516 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3517 wndClass.lpfnWndProc = TAB_WindowProc;
3518 wndClass.cbClsExtra = 0;
3519 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3520 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
3521 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
3522 wndClass.lpszClassName = WC_TABCONTROLW;
3524 RegisterClassW (&wndClass);
3528 void
3529 TAB_Unregister (void)
3531 UnregisterClassW (WC_TABCONTROLW, NULL);