4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Unicode support (under construction)
27 * UpDown control not displayed until after a tab is clicked on
35 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(tab);
47 RECT rect; /* bounding rectangle of the item relative to the
48 * leftmost item (the leftmost item, 0, would have a
49 * "left" member of 0 in this rectangle)
51 * additionally the top member hold the row number
52 * and bottom is unused and should be 0 */
57 UINT uNumItem; /* number of tab items */
58 UINT uNumRows; /* number of tab rows */
59 INT tabHeight; /* height of the tab row */
60 INT tabWidth; /* width of tabs */
61 HFONT hFont; /* handle to the current font */
62 HCURSOR hcurArrow; /* handle to the current cursor */
63 HIMAGELIST himl; /* handle to a image list (may be 0) */
64 HWND hwndToolTip; /* handle to tab's tooltip */
65 INT leftmostVisible; /* Used for scrolling, this member contains
66 * the index of the first visible item */
67 INT iSelected; /* the currently selected item */
68 INT iHotTracked; /* the highlighted item under the mouse */
69 INT uFocus; /* item which has the focus */
70 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
71 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
72 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
73 * the size of the control */
74 BOOL fSizeSet; /* was the size of the tabs explicitly set? */
75 BOOL bUnicode; /* Unicode control? */
76 HWND hwndUpDown; /* Updown control used for scrolling */
79 /******************************************************************************
80 * Positioning constants
82 #define SELECTED_TAB_OFFSET 2
83 #define HORIZONTAL_ITEM_PADDING 6
84 #define VERTICAL_ITEM_PADDING 3
85 #define ROUND_CORNER_SIZE 2
86 #define DISPLAY_AREA_PADDINGX 2
87 #define DISPLAY_AREA_PADDINGY 2
88 #define CONTROL_BORDER_SIZEX 2
89 #define CONTROL_BORDER_SIZEY 2
90 #define BUTTON_SPACINGX 4
91 #define BUTTON_SPACINGY 4
92 #define FLAT_BTN_SPACINGX 8
93 #define DEFAULT_TAB_WIDTH 96
95 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
97 /******************************************************************************
98 * Hot-tracking timer constants
100 #define TAB_HOTTRACK_TIMER 1
101 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
103 /******************************************************************************
106 static void TAB_Refresh (HWND hwnd, HDC hdc);
107 static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
108 static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
109 static void TAB_DrawItem(HWND hwnd, HDC hdc, INT iItem);
110 static void TAB_DrawItemInterior(HWND hwnd, HDC hdc, INT iItem, RECT* drawRect);
113 TAB_SendSimpleNotify (HWND hwnd, UINT code)
117 nmhdr.hwndFrom = hwnd;
118 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
121 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
122 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
126 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
127 WPARAM wParam, LPARAM lParam)
135 msg.time = GetMessageTime ();
136 msg.pt.x = LOWORD(GetMessagePos ());
137 msg.pt.y = HIWORD(GetMessagePos ());
139 SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
143 TAB_DumpItemExternalA(TCITEMA *pti, UINT iItem)
146 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n",
147 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
148 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextA=%s\n",
149 iItem, pti->iImage, pti->lParam, debugstr_a(pti->pszText));
155 TAB_DumpItemExternalW(TCITEMW *pti, UINT iItem)
158 TRACE("external tab %d, mask=0x%08x, dwState=0x%08lx, dwStateMask=0x%08lx, cchTextMax=0x%08x\n",
159 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax);
160 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n",
161 iItem, pti->iImage, pti->lParam, debugstr_w(pti->pszText));
166 TAB_DumpItemInternal(TAB_INFO *infoPtr, UINT iItem)
171 ti = &infoPtr->items[iItem];
172 TRACE("tab %d, mask=0x%08x, dwState=0x%08lx, pszText=%s, iImage=%d\n",
173 iItem, ti->mask, ti->dwState, debugstr_w(ti->pszText),
175 TRACE("tab %d, lParam=0x%08lx, rect.left=%ld, rect.top(row)=%ld\n",
176 iItem, ti->lParam, ti->rect.left, ti->rect.top);
181 TAB_GetCurSel (HWND hwnd)
183 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
185 return infoPtr->iSelected;
189 TAB_GetCurFocus (HWND hwnd)
191 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
193 return infoPtr->uFocus;
197 TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
199 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
201 if (infoPtr == NULL) return 0;
202 return (LRESULT)infoPtr->hwndToolTip;
206 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
208 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
209 INT iItem = (INT)wParam;
213 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
214 prevItem=infoPtr->iSelected;
215 infoPtr->iSelected=iItem;
216 TAB_EnsureSelectionVisible(hwnd, infoPtr);
217 TAB_InvalidateTabArea(hwnd, infoPtr);
223 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
225 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
226 INT iItem=(INT) wParam;
228 if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
230 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
231 FIXME("Should set input focus\n");
233 int oldFocus = infoPtr->uFocus;
234 if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) {
235 infoPtr->uFocus = iItem;
236 if (oldFocus != -1) {
237 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
238 infoPtr->iSelected = iItem;
239 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
242 infoPtr->iSelected = iItem;
243 TAB_EnsureSelectionVisible(hwnd, infoPtr);
244 TAB_InvalidateTabArea(hwnd, infoPtr);
252 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
254 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
256 if (infoPtr == NULL) return 0;
257 infoPtr->hwndToolTip = (HWND)wParam;
261 /******************************************************************************
262 * TAB_InternalGetItemRect
264 * This method will calculate the rectangle representing a given tab item in
265 * client coordinates. This method takes scrolling into account.
267 * This method returns TRUE if the item is visible in the window and FALSE
268 * if it is completely outside the client area.
270 static BOOL TAB_InternalGetItemRect(
277 RECT tmpItemRect,clientRect;
278 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
280 /* Perform a sanity check and a trivial visibility check. */
281 if ( (infoPtr->uNumItem <= 0) ||
282 (itemIndex >= infoPtr->uNumItem) ||
283 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
287 * Avoid special cases in this procedure by assigning the "out"
288 * parameters if the caller didn't supply them
290 if (itemRect == NULL)
291 itemRect = &tmpItemRect;
293 /* Retrieve the unmodified item rect. */
294 *itemRect = infoPtr->items[itemIndex].rect;
296 /* calculate the times bottom and top based on the row */
297 GetClientRect(hwnd, &clientRect);
299 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
301 itemRect->bottom = clientRect.bottom -
302 SELECTED_TAB_OFFSET -
303 itemRect->top * (infoPtr->tabHeight - 2) -
304 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
306 itemRect->top = clientRect.bottom -
308 itemRect->top * (infoPtr->tabHeight - 2) -
309 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
311 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
313 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * (infoPtr->tabHeight - 2) -
314 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
315 itemRect->left = clientRect.right - infoPtr->tabHeight - itemRect->left * (infoPtr->tabHeight - 2) -
316 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
318 else if((lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM))
320 itemRect->right = clientRect.left + infoPtr->tabHeight + itemRect->left * (infoPtr->tabHeight - 2) +
321 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
322 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * (infoPtr->tabHeight - 2) +
323 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
325 else if(!(lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM)) /* not TCS_BOTTOM and not TCS_VERTICAL */
327 itemRect->bottom = clientRect.top +
329 itemRect->top * (infoPtr->tabHeight - 2) +
330 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
331 itemRect->top = clientRect.top +
332 SELECTED_TAB_OFFSET +
333 itemRect->top * (infoPtr->tabHeight - 2) +
334 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
338 * "scroll" it to make sure the item at the very left of the
339 * tab control is the leftmost visible tab.
341 if(lStyle & TCS_VERTICAL)
345 -(clientRect.bottom - infoPtr->items[infoPtr->leftmostVisible].rect.bottom));
348 * Move the rectangle so the first item is slightly offset from
349 * the bottom of the tab control.
353 -SELECTED_TAB_OFFSET);
358 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
362 * Move the rectangle so the first item is slightly offset from
363 * the left of the tab control.
369 TRACE("item %d tab h=%d, rect=(%ld,%ld)-(%ld,%ld)\n",
370 itemIndex, infoPtr->tabHeight,
371 itemRect->left, itemRect->top, itemRect->right, itemRect->bottom);
373 /* Now, calculate the position of the item as if it were selected. */
374 if (selectedRect!=NULL)
376 CopyRect(selectedRect, itemRect);
378 /* The rectangle of a selected item is a bit wider. */
379 if(lStyle & TCS_VERTICAL)
380 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
382 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
384 /* If it also a bit higher. */
385 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
387 selectedRect->top -= 2; /* the border is thicker on the bottom */
388 selectedRect->bottom += SELECTED_TAB_OFFSET;
390 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
392 selectedRect->left -= 2; /* the border is thicker on the right */
393 selectedRect->right += SELECTED_TAB_OFFSET;
395 else if(lStyle & TCS_VERTICAL)
397 selectedRect->left -= SELECTED_TAB_OFFSET;
398 selectedRect->right += 1;
402 selectedRect->top -= SELECTED_TAB_OFFSET;
403 selectedRect->bottom += 1;
410 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
412 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
413 (LPRECT)lParam, (LPRECT)NULL);
416 /******************************************************************************
419 * This method is called to handle keyboard input
421 static LRESULT TAB_KeyUp(
425 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
431 newItem = infoPtr->uFocus - 1;
434 newItem = infoPtr->uFocus + 1;
439 * If we changed to a valid item, change the selection
441 if ((newItem >= 0) &&
442 (newItem < infoPtr->uNumItem) &&
443 (infoPtr->uFocus != newItem))
445 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
447 infoPtr->iSelected = newItem;
448 infoPtr->uFocus = newItem;
449 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
451 TAB_EnsureSelectionVisible(hwnd, infoPtr);
452 TAB_InvalidateTabArea(hwnd, infoPtr);
459 /******************************************************************************
462 * This method is called whenever the focus goes in or out of this control
463 * it is used to update the visual state of the control.
465 static LRESULT TAB_FocusChanging(
471 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
476 * Get the rectangle for the item.
478 isVisible = TAB_InternalGetItemRect(hwnd,
485 * If the rectangle is not completely invisible, invalidate that
486 * portion of the window.
490 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
491 selectedRect.left,selectedRect.top,
492 selectedRect.right,selectedRect.bottom);
493 InvalidateRect(hwnd, &selectedRect, TRUE);
497 * Don't otherwise disturb normal behavior.
499 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
502 static INT TAB_InternalHitTest (
512 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
514 TAB_InternalGetItemRect(hwnd, infoPtr, iCount, &rect, NULL);
516 if (PtInRect(&rect, pt))
518 *flags = TCHT_ONITEM;
523 *flags = TCHT_NOWHERE;
528 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
530 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
531 LPTCHITTESTINFO lptest = (LPTCHITTESTINFO) lParam;
533 return TAB_InternalHitTest (hwnd, infoPtr, lptest->pt, &lptest->flags);
536 /******************************************************************************
539 * Napster v2b5 has a tab control for its main navigation which has a client
540 * area that covers the whole area of the dialog pages.
541 * That's why it receives all msgs for that area and the underlying dialog ctrls
543 * So I decided that we should handle WM_NCHITTEST here and return
544 * HTTRANSPARENT if we don't hit the tab control buttons.
545 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
546 * doesn't do it that way. Maybe depends on tab control styles ?
549 TAB_NCHitTest (HWND hwnd, LPARAM lParam)
551 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
555 pt.x = LOWORD(lParam);
556 pt.y = HIWORD(lParam);
557 ScreenToClient(hwnd, &pt);
559 if (TAB_InternalHitTest(hwnd, infoPtr, pt, &dummyflag) == -1)
560 return HTTRANSPARENT;
566 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
568 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
572 if (infoPtr->hwndToolTip)
573 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
574 WM_LBUTTONDOWN, wParam, lParam);
576 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
580 if (infoPtr->hwndToolTip)
581 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
582 WM_LBUTTONDOWN, wParam, lParam);
584 pt.x = (INT)LOWORD(lParam);
585 pt.y = (INT)HIWORD(lParam);
587 newItem = TAB_InternalHitTest (hwnd, infoPtr, pt, &dummy);
589 TRACE("On Tab, item %d\n", newItem);
591 if ((newItem != -1) && (infoPtr->iSelected != newItem))
593 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING) != TRUE)
595 infoPtr->iSelected = newItem;
596 infoPtr->uFocus = newItem;
597 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
599 TAB_EnsureSelectionVisible(hwnd, infoPtr);
601 TAB_InvalidateTabArea(hwnd, infoPtr);
608 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
610 TAB_SendSimpleNotify(hwnd, NM_CLICK);
616 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
618 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
622 /******************************************************************************
623 * TAB_DrawLoneItemInterior
625 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
626 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
627 * up the device context and font. This routine does the same setup but
628 * only calls TAB_DrawItemInterior for the single specified item.
631 TAB_DrawLoneItemInterior(HWND hwnd, TAB_INFO* infoPtr, int iItem)
633 HDC hdc = GetDC(hwnd);
634 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
635 TAB_DrawItemInterior(hwnd, hdc, iItem, NULL);
636 SelectObject(hdc, hOldFont);
637 ReleaseDC(hwnd, hdc);
640 /******************************************************************************
641 * TAB_HotTrackTimerProc
643 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
644 * timer is setup so we can check if the mouse is moved out of our window.
645 * (We don't get an event when the mouse leaves, the mouse-move events just
646 * stop being delivered to our window and just start being delivered to
647 * another window.) This function is called when the timer triggers so
648 * we can check if the mouse has left our window. If so, we un-highlight
649 * the hot-tracked tab.
652 TAB_HotTrackTimerProc
654 HWND hwnd, /* handle of window for timer messages */
655 UINT uMsg, /* WM_TIMER message */
656 UINT idEvent, /* timer identifier */
657 DWORD dwTime /* current system time */
660 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
662 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
667 ** If we can't get the cursor position, or if the cursor is outside our
668 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
669 ** "outside" even if it is within our bounding rect if another window
670 ** overlaps. Note also that the case where the cursor stayed within our
671 ** window but has moved off the hot-tracked tab will be handled by the
672 ** WM_MOUSEMOVE event.
674 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
676 /* Redraw iHotTracked to look normal */
677 INT iRedraw = infoPtr->iHotTracked;
678 infoPtr->iHotTracked = -1;
679 TAB_DrawLoneItemInterior(hwnd, infoPtr, iRedraw);
681 /* Kill this timer */
682 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
687 /******************************************************************************
690 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
691 * should be highlighted. This function determines which tab in a tab control,
692 * if any, is under the mouse and records that information. The caller may
693 * supply output parameters to receive the item number of the tab item which
694 * was highlighted but isn't any longer and of the tab item which is now
695 * highlighted but wasn't previously. The caller can use this information to
696 * selectively redraw those tab items.
698 * If the caller has a mouse position, it can supply it through the pos
699 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
700 * supplies NULL and this function determines the current mouse position
708 int* out_redrawLeave,
712 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
717 if (out_redrawLeave != NULL)
718 *out_redrawLeave = -1;
719 if (out_redrawEnter != NULL)
720 *out_redrawEnter = -1;
722 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_HOTTRACK)
730 ScreenToClient(hwnd, &pt);
738 item = TAB_InternalHitTest(hwnd, infoPtr, pt, &flags);
741 if (item != infoPtr->iHotTracked)
743 if (infoPtr->iHotTracked >= 0)
745 /* Mark currently hot-tracked to be redrawn to look normal */
746 if (out_redrawLeave != NULL)
747 *out_redrawLeave = infoPtr->iHotTracked;
751 /* Kill timer which forces recheck of mouse pos */
752 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
757 /* Start timer so we recheck mouse pos */
758 UINT timerID = SetTimer
762 TAB_HOTTRACK_TIMER_INTERVAL,
763 TAB_HotTrackTimerProc
767 return; /* Hot tracking not available */
770 infoPtr->iHotTracked = item;
774 /* Mark new hot-tracked to be redrawn to look highlighted */
775 if (out_redrawEnter != NULL)
776 *out_redrawEnter = item;
781 /******************************************************************************
784 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
787 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
792 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
794 if (infoPtr->hwndToolTip)
795 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
796 WM_LBUTTONDOWN, wParam, lParam);
798 /* Determine which tab to highlight. Redraw tabs which change highlight
800 TAB_RecalcHotTrack(hwnd, &lParam, &redrawLeave, &redrawEnter);
802 if (redrawLeave != -1)
803 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawLeave);
804 if (redrawEnter != -1)
805 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawEnter);
810 /******************************************************************************
813 * Calculates the tab control's display area given the window rectangle or
814 * the window rectangle given the requested display rectangle.
816 static LRESULT TAB_AdjustRect(
821 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
822 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
824 if(lStyle & TCS_VERTICAL)
826 if (fLarger) /* Go from display rectangle */
828 /* Add the height of the tabs. */
829 if (lStyle & TCS_BOTTOM)
830 prc->right += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
832 prc->left -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
834 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
835 /* Inflate the rectangle for the padding */
836 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
838 /* Inflate for the border */
839 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
841 else /* Go from window rectangle. */
843 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
844 /* Deflate the rectangle for the border */
845 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
847 /* Deflate the rectangle for the padding */
848 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
850 /* Remove the height of the tabs. */
851 if (lStyle & TCS_BOTTOM)
852 prc->right -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
854 prc->left += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
858 if (fLarger) /* Go from display rectangle */
860 /* Add the height of the tabs. */
861 if (lStyle & TCS_BOTTOM)
862 prc->bottom += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
864 prc->top -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
866 /* Inflate the rectangle for the padding */
867 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
869 /* Inflate for the border */
870 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
872 else /* Go from window rectangle. */
874 /* Deflate the rectangle for the border */
875 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
877 /* Deflate the rectangle for the padding */
878 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
880 /* Remove the height of the tabs. */
881 if (lStyle & TCS_BOTTOM)
882 prc->bottom -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
884 prc->top += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
891 /******************************************************************************
894 * This method will handle the notification from the scroll control and
895 * perform the scrolling operation on the tab control.
897 static LRESULT TAB_OnHScroll(
903 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
905 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
907 if(nPos < infoPtr->leftmostVisible)
908 infoPtr->leftmostVisible--;
910 infoPtr->leftmostVisible++;
912 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
913 TAB_InvalidateTabArea(hwnd, infoPtr);
914 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
915 MAKELONG(infoPtr->leftmostVisible, 0));
921 /******************************************************************************
924 * This method will check the current scrolling state and make sure the
925 * scrolling control is displayed (or not).
927 static void TAB_SetupScrolling(
930 const RECT* clientRect)
933 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
935 if (infoPtr->needsScrolling)
941 * Calculate the position of the scroll control.
943 if(lStyle & TCS_VERTICAL)
945 controlPos.right = clientRect->right;
946 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
948 if (lStyle & TCS_BOTTOM)
950 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
951 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
955 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
956 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
961 controlPos.right = clientRect->right;
962 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
964 if (lStyle & TCS_BOTTOM)
966 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
967 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
971 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
972 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
977 * If we don't have a scroll control yet, we want to create one.
978 * If we have one, we want to make sure it's positioned properly.
980 if (infoPtr->hwndUpDown==0)
982 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
984 WS_VISIBLE | WS_CHILD | UDS_HORZ,
985 controlPos.left, controlPos.top,
986 controlPos.right - controlPos.left,
987 controlPos.bottom - controlPos.top,
995 SetWindowPos(infoPtr->hwndUpDown,
997 controlPos.left, controlPos.top,
998 controlPos.right - controlPos.left,
999 controlPos.bottom - controlPos.top,
1000 SWP_SHOWWINDOW | SWP_NOZORDER);
1003 /* Now calculate upper limit of the updown control range.
1004 * We do this by calculating how many tabs will be offscreen when the
1005 * last tab is visible.
1007 if(infoPtr->uNumItem)
1009 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
1010 maxRange = infoPtr->uNumItem;
1011 tabwidth = infoPtr->items[maxRange - 1].rect.right;
1013 for(; maxRange > 0; maxRange--)
1015 if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
1019 if(maxRange == infoPtr->uNumItem)
1025 /* If we once had a scroll control... hide it */
1026 if (infoPtr->hwndUpDown!=0)
1027 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
1029 if (infoPtr->hwndUpDown)
1030 SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
1033 /******************************************************************************
1036 * This method will calculate the position rectangles of all the items in the
1037 * control. The rectangle calculated starts at 0 for the first item in the
1038 * list and ignores scrolling and selection.
1039 * It also uses the current font to determine the height of the tab row and
1040 * it checks if all the tabs fit in the client area of the window. If they
1041 * dont, a scrolling control is added.
1043 static void TAB_SetItemBounds (HWND hwnd)
1045 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1046 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1047 TEXTMETRICA fontMetrics;
1050 INT curItemRowCount;
1051 HFONT hFont, hOldFont;
1060 * We need to get text information so we need a DC and we need to select
1065 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1066 hOldFont = SelectObject (hdc, hFont);
1069 * We will base the rectangle calculations on the client rectangle
1072 GetClientRect(hwnd, &clientRect);
1074 /* if TCS_VERTICAL then swap the height and width so this code places the
1075 tabs along the top of the rectangle and we can just rotate them after
1076 rather than duplicate all of the below code */
1077 if(lStyle & TCS_VERTICAL)
1079 iTemp = clientRect.bottom;
1080 clientRect.bottom = clientRect.right;
1081 clientRect.right = iTemp;
1084 /* The leftmost item will be "0" aligned */
1086 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1088 if (!(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet) )
1091 int icon_height = 0;
1093 /* Use the current font to determine the height of a tab. */
1094 GetTextMetricsA(hdc, &fontMetrics);
1096 /* Get the icon height */
1098 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1100 /* Take the highest between font or icon */
1101 if (fontMetrics.tmHeight > icon_height)
1102 item_height = fontMetrics.tmHeight + 2;
1104 item_height = icon_height;
1107 * Make sure there is enough space for the letters + icon + growing the
1108 * selected item + extra space for the selected item.
1110 infoPtr->tabHeight = item_height + SELECTED_TAB_OFFSET +
1111 ((lStyle & TCS_BUTTONS) ? 2 : 1) *
1112 VERTICAL_ITEM_PADDING;
1114 TRACE("tabH=%d, tmH=%ld, iconh=%d\n",
1115 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height);
1118 TRACE("client right=%ld\n", clientRect.right);
1120 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1122 /* Set the leftmost position of the tab. */
1123 infoPtr->items[curItem].rect.left = curItemLeftPos;
1125 if ( (lStyle & TCS_FIXEDWIDTH) || ((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
1127 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1129 2 * HORIZONTAL_ITEM_PADDING;
1136 /* Calculate how wide the tab is depending on the text it contains */
1137 GetTextExtentPoint32W(hdc, infoPtr->items[curItem].pszText,
1138 lstrlenW(infoPtr->items[curItem].pszText), &size);
1140 /* under Windows, there seems to be a minimum width of 2x the height
1141 * for button style tabs */
1142 if (lStyle & TCS_BUTTONS)
1143 size.cx = max(size.cx, 2 * (infoPtr->tabHeight - 2));
1145 /* Add the icon width */
1148 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1152 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1153 size.cx + icon_width +
1154 num * HORIZONTAL_ITEM_PADDING;
1155 TRACE("for <%s>, l,r=%ld,%ld, num=%d\n",
1156 debugstr_w(infoPtr->items[curItem].pszText),
1157 infoPtr->items[curItem].rect.left,
1158 infoPtr->items[curItem].rect.right,
1163 * Check if this is a multiline tab control and if so
1164 * check to see if we should wrap the tabs
1166 * Because we are going to arange all these tabs evenly
1167 * really we are basically just counting rows at this point
1171 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1172 (infoPtr->items[curItem].rect.right > clientRect.right))
1174 infoPtr->items[curItem].rect.right -=
1175 infoPtr->items[curItem].rect.left;
1177 infoPtr->items[curItem].rect.left = 0;
1179 TRACE("wrapping <%s>, l,r=%ld,%ld\n",
1180 debugstr_w(infoPtr->items[curItem].pszText),
1181 infoPtr->items[curItem].rect.left,
1182 infoPtr->items[curItem].rect.right);
1185 infoPtr->items[curItem].rect.bottom = 0;
1186 infoPtr->items[curItem].rect.top = curItemRowCount - 1;
1188 TRACE("TextSize: %li\n", size.cx);
1189 TRACE("Rect: T %li, L %li, B %li, R %li\n",
1190 infoPtr->items[curItem].rect.top,
1191 infoPtr->items[curItem].rect.left,
1192 infoPtr->items[curItem].rect.bottom,
1193 infoPtr->items[curItem].rect.right);
1196 * The leftmost position of the next item is the rightmost position
1199 if (lStyle & TCS_BUTTONS)
1201 curItemLeftPos = infoPtr->items[curItem].rect.right + 1;
1202 if (lStyle & TCS_FLATBUTTONS)
1203 curItemLeftPos += FLAT_BTN_SPACINGX;
1206 curItemLeftPos = infoPtr->items[curItem].rect.right;
1209 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1212 * Check if we need a scrolling control.
1214 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1217 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1218 if(!infoPtr->needsScrolling)
1219 infoPtr->leftmostVisible = 0;
1221 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
1224 /* Set the number of rows */
1225 infoPtr->uNumRows = curItemRowCount;
1227 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->uNumItem > 0))
1229 INT widthDiff, remainder;
1230 INT tabPerRow,remTab;
1232 INT iIndexStart=0,iIndexEnd=0, iCount=0;
1235 * Ok windows tries to even out the rows. place the same
1236 * number of tabs in each row. So lets give that a shot
1239 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1240 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1242 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1243 iItm<infoPtr->uNumItem;
1246 /* normalize the current rect */
1248 /* shift the item to the left side of the clientRect */
1249 infoPtr->items[iItm].rect.right -=
1250 infoPtr->items[iItm].rect.left;
1251 infoPtr->items[iItm].rect.left = 0;
1253 TRACE("r=%ld, cl=%d, cl.r=%ld, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n",
1254 infoPtr->items[iItm].rect.right,
1255 curItemLeftPos, clientRect.right,
1256 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow);
1258 /* if we have reached the maximum number of tabs on this row */
1259 /* move to the next row, reset our current item left position and */
1260 /* the count of items on this row */
1262 /* ************ FIXME FIXME FIXME *************** */
1266 /* if item n and n+1 are in the same row, */
1267 /* then the display has n+1 lower (toward the */
1268 /* bottom) than n. We do it just the */
1271 /* ************ FIXME FIXME FIXME *************** */
1273 if (lStyle & TCS_VERTICAL) {
1274 /* Vert: Add the remaining tabs in the *last* remainder rows */
1275 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) {
1281 /* Horz: Add the remaining tabs in the *first* remainder rows */
1282 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) {
1289 /* shift the item to the right to place it as the next item in this row */
1290 infoPtr->items[iItm].rect.left += curItemLeftPos;
1291 infoPtr->items[iItm].rect.right += curItemLeftPos;
1292 infoPtr->items[iItm].rect.top = iRow;
1293 if (lStyle & TCS_BUTTONS)
1295 curItemLeftPos = infoPtr->items[iItm].rect.right + 1;
1296 if (lStyle & TCS_FLATBUTTONS)
1297 curItemLeftPos += FLAT_BTN_SPACINGX;
1300 curItemLeftPos = infoPtr->items[iItm].rect.right;
1302 TRACE("arranging <%s>, l,r=%ld,%ld, row=%ld\n",
1303 debugstr_w(infoPtr->items[iItm].pszText),
1304 infoPtr->items[iItm].rect.left,
1305 infoPtr->items[iItm].rect.right,
1306 infoPtr->items[iItm].rect.top);
1313 while(iIndexStart < infoPtr->uNumItem)
1316 * find the indexs of the row
1318 /* find the first item on the next row */
1319 for (iIndexEnd=iIndexStart;
1320 (iIndexEnd < infoPtr->uNumItem) &&
1321 (infoPtr->items[iIndexEnd].rect.top ==
1322 infoPtr->items[iIndexStart].rect.top) ;
1324 /* intentionally blank */;
1327 * we need to justify these tabs so they fill the whole given
1331 /* find the amount of space remaining on this row */
1332 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1333 infoPtr->items[iIndexEnd - 1].rect.right;
1335 /* iCount is the number of tab items on this row */
1336 iCount = iIndexEnd - iIndexStart;
1341 remainder = widthDiff % iCount;
1342 widthDiff = widthDiff / iCount;
1343 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1344 for (iIndex=iIndexStart,iCount=0; iIndex < iIndexEnd;
1347 infoPtr->items[iIndex].rect.left += iCount * widthDiff;
1348 infoPtr->items[iIndex].rect.right += (iCount + 1) * widthDiff;
1350 TRACE("adjusting 1 <%s>, l,r=%ld,%ld\n",
1351 debugstr_w(infoPtr->items[iIndex].pszText),
1352 infoPtr->items[iIndex].rect.left,
1353 infoPtr->items[iIndex].rect.right);
1356 infoPtr->items[iIndex - 1].rect.right += remainder;
1358 else /* we have only one item on this row, make it take up the entire row */
1360 infoPtr->items[iIndexStart].rect.left = clientRect.left;
1361 infoPtr->items[iIndexStart].rect.right = clientRect.right - 4;
1363 TRACE("adjusting 2 <%s>, l,r=%ld,%ld\n",
1364 debugstr_w(infoPtr->items[iIndexStart].pszText),
1365 infoPtr->items[iIndexStart].rect.left,
1366 infoPtr->items[iIndexStart].rect.right);
1371 iIndexStart = iIndexEnd;
1376 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1377 if(lStyle & TCS_VERTICAL)
1380 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1382 rcItem = &(infoPtr->items[iIndex].rect);
1384 rcOriginal = *rcItem;
1386 /* this is rotating the items by 90 degrees around the center of the control */
1387 rcItem->top = (clientRect.right - (rcOriginal.left - clientRect.left)) - (rcOriginal.right - rcOriginal.left);
1388 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1389 rcItem->left = rcOriginal.top;
1390 rcItem->right = rcOriginal.bottom;
1394 TAB_EnsureSelectionVisible(hwnd,infoPtr);
1395 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1398 SelectObject (hdc, hOldFont);
1399 ReleaseDC (hwnd, hdc);
1402 /******************************************************************************
1403 * TAB_DrawItemInterior
1405 * This method is used to draw the interior (text and icon) of a single tab
1406 * into the tab control.
1409 TAB_DrawItemInterior
1417 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1418 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1426 if (drawRect == NULL)
1433 * Get the rectangle for the item.
1435 isVisible = TAB_InternalGetItemRect(hwnd, infoPtr, iItem, &itemRect, &selectedRect);
1440 * Make sure drawRect points to something valid; simplifies code.
1442 drawRect = &localRect;
1445 * This logic copied from the part of TAB_DrawItem which draws
1446 * the tab background. It's important to keep it in sync. I
1447 * would have liked to avoid code duplication, but couldn't figure
1448 * out how without making spaghetti of TAB_DrawItem.
1450 if (lStyle & TCS_BUTTONS)
1452 *drawRect = itemRect;
1453 if (iItem == infoPtr->iSelected)
1461 if (iItem == infoPtr->iSelected)
1462 *drawRect = selectedRect;
1464 *drawRect = itemRect;
1473 htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) );
1474 holdPen = SelectObject(hdc, htextPen);
1476 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1477 SetTextColor(hdc, (iItem == infoPtr->iHotTracked) ?
1478 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1481 * Deflate the rectangle to acount for the padding
1483 if(lStyle & TCS_VERTICAL)
1484 InflateRect(drawRect, -VERTICAL_ITEM_PADDING, -HORIZONTAL_ITEM_PADDING);
1486 InflateRect(drawRect, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
1490 * if owner draw, tell the owner to draw
1492 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd))
1498 * get the control id
1500 id = GetWindowLongA( hwnd, GWL_ID );
1503 * put together the DRAWITEMSTRUCT
1505 dis.CtlType = ODT_TAB;
1508 dis.itemAction = ODA_DRAWENTIRE;
1509 if ( iItem == infoPtr->iSelected )
1510 dis.itemState = ODS_SELECTED;
1513 dis.hwndItem = hwnd; /* */
1515 dis.rcItem = *drawRect; /* */
1516 dis.itemData = infoPtr->items[iItem].lParam;
1519 * send the draw message
1521 SendMessageA( GetParent(hwnd), WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1532 HFONT hOldFont = 0; /* stop uninitialized warning */
1534 INT nEscapement = 0; /* stop uninitialized warning */
1535 INT nOrientation = 0; /* stop uninitialized warning */
1538 /* used to center the icon and text in the tab */
1542 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1543 rcImage = *drawRect;
1547 rcText.left = rcText.top = rcText.right = rcText.bottom = 0;
1550 * Setup for text output
1552 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1553 SetTextColor(hdc, (iItem == infoPtr->iHotTracked) ?
1554 comctl32_color.clrHighlight : comctl32_color.clrBtnText);
1556 /* get the rectangle that the text fits in */
1557 DrawTextW(hdc, infoPtr->items[iItem].pszText, -1,
1558 &rcText, DT_CALCRECT);
1561 * If not owner draw, then do the drawing ourselves.
1565 if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE))
1567 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1569 if(lStyle & TCS_VERTICAL)
1570 center_offset = ((drawRect->bottom - drawRect->top) - (cy + HORIZONTAL_ITEM_PADDING + (rcText.right - rcText.left))) / 2;
1572 center_offset = ((drawRect->right - drawRect->left) - (cx + HORIZONTAL_ITEM_PADDING + (rcText.right - rcText.left))) / 2;
1574 TRACE("for <%s>, c_o=%d, draw=(%ld,%ld)-(%ld,%ld), textlen=%ld\n",
1575 debugstr_w(infoPtr->items[iItem].pszText), center_offset,
1576 drawRect->left, drawRect->top, drawRect->right, drawRect->bottom,
1577 (rcText.right-rcText.left));
1579 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1581 rcImage.top = drawRect->top + center_offset;
1582 rcImage.left = drawRect->right - cx; /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1583 /* right side of the tab, but the image still uses the left as its x position */
1584 /* this keeps the image always drawn off of the same side of the tab */
1585 drawRect->top = rcImage.top + (cx + HORIZONTAL_ITEM_PADDING);
1587 else if(lStyle & TCS_VERTICAL)
1589 rcImage.top = drawRect->bottom - cy - center_offset;
1591 drawRect->bottom = rcImage.top - HORIZONTAL_ITEM_PADDING;
1593 else /* normal style, whether TCS_BOTTOM or not */
1595 rcImage.left = drawRect->left + center_offset + 3;
1596 drawRect->left = rcImage.left + cx + HORIZONTAL_ITEM_PADDING;
1597 rcImage.top -= (lStyle & TCS_BOTTOM) ? 2 : 1;
1600 TRACE("drawing image=%d, left=%ld, top=%ld\n",
1601 infoPtr->items[iItem].iImage, rcImage.left, rcImage.top-1);
1605 infoPtr->items[iItem].iImage,
1611 } else /* no image, so just shift the drawRect borders around */
1613 if(lStyle & TCS_VERTICAL)
1617 currently the rcText rect is flawed because the rotated font does not
1618 often match the horizontal font. So leave this as 0
1619 ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1621 if(lStyle & TCS_BOTTOM)
1622 drawRect->top+=center_offset;
1624 drawRect->bottom-=center_offset;
1628 center_offset = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1629 drawRect->left+=center_offset;
1634 if (lStyle & TCS_RIGHTJUSTIFY)
1635 uHorizAlign = DT_CENTER;
1637 uHorizAlign = DT_LEFT;
1639 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1641 if(lStyle & TCS_BOTTOM)
1644 nOrientation = -900;
1653 /* to get a font with the escapement and orientation we are looking for, we need to */
1654 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1655 if(lStyle & TCS_VERTICAL)
1657 if (!GetObjectA((infoPtr->hFont) ?
1658 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1659 sizeof(LOGFONTA),&logfont))
1663 lstrcpyA(logfont.lfFaceName, "Arial");
1664 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1666 logfont.lfWeight = FW_NORMAL;
1667 logfont.lfItalic = 0;
1668 logfont.lfUnderline = 0;
1669 logfont.lfStrikeOut = 0;
1672 logfont.lfEscapement = nEscapement;
1673 logfont.lfOrientation = nOrientation;
1674 hFont = CreateFontIndirectA(&logfont);
1675 hOldFont = SelectObject(hdc, hFont);
1678 if (lStyle & TCS_VERTICAL)
1681 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1682 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1685 infoPtr->items[iItem].pszText,
1686 lstrlenW(infoPtr->items[iItem].pszText),
1694 infoPtr->items[iItem].pszText,
1695 lstrlenW(infoPtr->items[iItem].pszText),
1697 uHorizAlign | DT_SINGLELINE
1701 /* clean things up */
1702 *drawRect = rcTemp; /* restore drawRect */
1704 if(lStyle & TCS_VERTICAL)
1706 SelectObject(hdc, hOldFont); /* restore the original font */
1708 DeleteObject(hFont);
1715 SetBkMode(hdc, oldBkMode);
1716 SelectObject(hdc, holdPen);
1717 DeleteObject( htextPen );
1720 /******************************************************************************
1723 * This method is used to draw a single tab into the tab control.
1725 static void TAB_DrawItem(
1730 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1731 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1735 RECT r, fillRect, r1;
1738 COLORREF bkgnd, corner;
1741 * Get the rectangle for the item.
1743 isVisible = TAB_InternalGetItemRect(hwnd,
1751 /* If you need to see what the control is doing,
1752 * then override these variables. They will change what
1753 * fill colors are used for filling the tabs, and the
1754 * corners when drawing the edge.
1756 bkgnd = comctl32_color.clrBtnFace;
1757 corner = comctl32_color.clrBtnFace;
1759 if (lStyle & TCS_BUTTONS)
1761 HBRUSH hbr = CreateSolidBrush (bkgnd);
1762 BOOL deleteBrush = TRUE;
1764 /* Get item rectangle */
1767 /* Separators between flat buttons */
1768 if (lStyle & TCS_FLATBUTTONS)
1771 r1.right += (FLAT_BTN_SPACINGX -2);
1772 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT);
1775 if (iItem == infoPtr->iSelected)
1777 /* Background color */
1778 if (!((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
1781 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1783 SetTextColor(hdc, comctl32_color.clr3dFace);
1784 SetBkColor(hdc, comctl32_color.clr3dHilight);
1786 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1787 * we better use 0x55aa bitmap brush to make scrollbar's background
1788 * look different from the window background.
1790 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow)
1791 hbr = COMCTL32_hPattern55AABrush;
1793 deleteBrush = FALSE;
1796 /* Clear interior */
1797 FillRect(hdc, &r, hbr);
1799 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT);
1801 else /* ! selected */
1803 if (!(lStyle & TCS_FLATBUTTONS))
1805 /* Clear interior */
1806 FillRect(hdc, &r, hbr);
1808 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT);
1813 if (deleteBrush) DeleteObject(hbr);
1815 else /* !TCS_BUTTONS */
1817 /* We draw a rectangle of different sizes depending on the selection
1819 if (iItem == infoPtr->iSelected) {
1821 GetClientRect (hwnd, &rect);
1822 clRight = rect.right;
1823 clBottom = rect.bottom;
1830 * Erase the background. (Delay it but setup rectangle.)
1831 * This is necessary when drawing the selected item since it is larger
1832 * than the others, it might overlap with stuff already drawn by the
1837 if(lStyle & TCS_VERTICAL)
1839 /* These are for adjusting the drawing of a Selected tab */
1840 /* The initial values are for the normal case of non-Selected */
1841 int ZZ = 1; /* Do not strech if selected */
1842 if (iItem == infoPtr->iSelected) {
1845 /* if leftmost draw the line longer */
1846 if(selectedRect.top == 0)
1848 /* if rightmost draw the line longer */
1849 if(selectedRect.bottom == clBottom)
1850 fillRect.bottom -= 2;
1853 if (lStyle & TCS_BOTTOM)
1855 /* Adjust both rectangles to match native */
1858 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1860 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1861 r.left,r.top,r.right,r.bottom);
1863 /* Clear interior */
1864 SetBkColor(hdc, bkgnd);
1865 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1867 /* Draw rectangular edge around tab */
1868 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM);
1870 /* Now erase the top corner and draw diagonal edge */
1871 SetBkColor(hdc, corner);
1872 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1875 r1.bottom = r1.top + ROUND_CORNER_SIZE;
1876 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1878 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
1880 /* Now erase the bottom corner and draw diagonal edge */
1881 r1.left = r.right - ROUND_CORNER_SIZE - 1;
1882 r1.bottom = r.bottom;
1884 r1.top = r1.bottom - ROUND_CORNER_SIZE;
1885 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1887 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
1889 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) {
1893 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP);
1899 /* Adjust both rectangles to match native */
1900 fillRect.right += (1-ZZ);
1902 TRACE("<left> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1904 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1905 r.left,r.top,r.right,r.bottom);
1907 /* Clear interior */
1908 SetBkColor(hdc, bkgnd);
1909 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1911 /* Draw rectangular edge around tab */
1912 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM);
1914 /* Now erase the top corner and draw diagonal edge */
1915 SetBkColor(hdc, corner);
1918 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
1919 r1.bottom = r1.top + ROUND_CORNER_SIZE;
1920 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1922 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
1924 /* Now erase the bottom corner and draw diagonal edge */
1926 r1.bottom = r.bottom;
1927 r1.right = r1.left + ROUND_CORNER_SIZE + 1;
1928 r1.top = r1.bottom - ROUND_CORNER_SIZE;
1929 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1931 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT);
1934 else /* ! TCS_VERTICAL */
1936 /* These are for adjusting the drawing of a Selected tab */
1937 /* The initial values are for the normal case of non-Selected */
1938 int ZZ = 1; /* Do not strech if selected */
1939 if (iItem == infoPtr->iSelected) {
1942 /* if leftmost draw the line longer */
1943 if(selectedRect.left == 0)
1945 /* if rightmost draw the line longer */
1946 if(selectedRect.right == clRight)
1947 fillRect.right -= 2;
1950 if (lStyle & TCS_BOTTOM)
1953 /* Adjust both rectangles to match native */
1959 TRACE("<bottom> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
1961 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
1962 r.left,r.top,r.right,r.bottom);
1964 /* Clear interior */
1965 SetBkColor(hdc, bkgnd);
1966 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
1968 /* Draw rectangular edge around tab */
1969 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT);
1971 /* Now erase the righthand corner and draw diagonal edge */
1972 SetBkColor(hdc, corner);
1973 r1.left = r.right - ROUND_CORNER_SIZE;
1974 r1.bottom = r.bottom;
1976 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
1977 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1979 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT);
1981 /* Now erase the lefthand corner and draw diagonal edge */
1983 r1.bottom = r.bottom;
1984 r1.right = r1.left + ROUND_CORNER_SIZE;
1985 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1;
1986 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
1988 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT);
1990 if ((iItem == infoPtr->iSelected) && (selectedRect.left == 0)) {
1994 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT);
2001 /* Adjust both rectangles to match native */
2002 fillRect.bottom += (1-ZZ);
2004 TRACE("<top> item=%d, fill=(%ld,%ld)-(%ld,%ld), edge=(%ld,%ld)-(%ld,%ld)\n",
2006 fillRect.left,fillRect.top,fillRect.right,fillRect.bottom,
2007 r.left,r.top,r.right,r.bottom);
2009 /* Clear interior */
2010 SetBkColor(hdc, bkgnd);
2011 ExtTextOutA(hdc, 0, 0, 2, &fillRect, NULL, 0, 0);
2013 /* Draw rectangular edge around tab */
2014 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT);
2016 /* Now erase the righthand corner and draw diagonal edge */
2017 SetBkColor(hdc, corner);
2018 r1.left = r.right - ROUND_CORNER_SIZE;
2021 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2022 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2024 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT);
2026 /* Now erase the lefthand corner and draw diagonal edge */
2029 r1.right = r1.left + ROUND_CORNER_SIZE;
2030 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1;
2031 ExtTextOutA(hdc, 0, 0, 2, &r1, NULL, 0, 0);
2033 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT);
2039 TAB_DumpItemInternal(infoPtr, iItem);
2041 /* This modifies r to be the text rectangle. */
2043 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
2044 TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
2045 SelectObject(hdc,hOldFont);
2048 /* Draw the focus rectangle */
2049 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
2050 (GetFocus() == hwnd) &&
2051 (iItem == infoPtr->uFocus) )
2054 InflateRect(&r, -1, -1);
2056 DrawFocusRect(hdc, &r);
2061 /******************************************************************************
2064 * This method is used to draw the raised border around the tab control
2067 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
2069 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2071 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2073 GetClientRect (hwnd, &rect);
2076 * Adjust for the style
2079 if (infoPtr->uNumItem)
2081 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2083 rect.bottom -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 3;
2085 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2087 rect.right -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2089 else if(lStyle & TCS_VERTICAL)
2091 rect.left += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2093 else /* not TCS_VERTICAL and not TCS_BOTTOM */
2095 rect.top += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
2099 TRACE("border=(%ld,%ld)-(%ld,%ld)\n",
2100 rect.left, rect.top, rect.right, rect.bottom);
2102 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT);
2105 /******************************************************************************
2108 * This method repaints the tab control..
2110 static void TAB_Refresh (HWND hwnd, HDC hdc)
2112 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2116 if (!infoPtr->DoRedraw)
2119 hOldFont = SelectObject (hdc, infoPtr->hFont);
2121 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
2123 for (i = 0; i < infoPtr->uNumItem; i++)
2124 TAB_DrawItem (hwnd, hdc, i);
2128 /* Draw all the non selected item first */
2129 for (i = 0; i < infoPtr->uNumItem; i++)
2131 if (i != infoPtr->iSelected)
2132 TAB_DrawItem (hwnd, hdc, i);
2135 /* Now, draw the border, draw it before the selected item
2136 * since the selected item overwrites part of the border. */
2137 TAB_DrawBorder (hwnd, hdc);
2139 /* Then, draw the selected item */
2140 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
2142 /* If we haven't set the current focus yet, set it now.
2143 * Only happens when we first paint the tab controls */
2144 if (infoPtr->uFocus == -1)
2145 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
2148 SelectObject (hdc, hOldFont);
2152 TAB_GetRowCount (HWND hwnd )
2154 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2156 return infoPtr->uNumRows;
2160 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
2162 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2164 infoPtr->DoRedraw=(BOOL) wParam;
2168 static LRESULT TAB_EraseBackground(
2175 HBRUSH brush = CreateSolidBrush(comctl32_color.clrBtnFace);
2177 hdc = givenDC ? givenDC : GetDC(hwnd);
2179 GetClientRect(hwnd, &clientRect);
2181 FillRect(hdc, &clientRect, brush);
2184 ReleaseDC(hwnd, hdc);
2186 DeleteObject(brush);
2191 /******************************************************************************
2192 * TAB_EnsureSelectionVisible
2194 * This method will make sure that the current selection is completely
2195 * visible by scrolling until it is.
2197 static void TAB_EnsureSelectionVisible(
2201 INT iSelected = infoPtr->iSelected;
2202 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2203 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2205 /* set the items row to the bottommost row or topmost row depending on
2207 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2212 if(lStyle & TCS_VERTICAL)
2213 newselected = infoPtr->items[iSelected].rect.left;
2215 newselected = infoPtr->items[iSelected].rect.top;
2217 /* the target row is always (number of rows - 1)
2218 as row 0 is furthest from the clientRect */
2219 iTargetRow = infoPtr->uNumRows - 1;
2221 if (newselected != iTargetRow)
2224 if(lStyle & TCS_VERTICAL)
2226 for (i=0; i < infoPtr->uNumItem; i++)
2228 /* move everything in the row of the selected item to the iTargetRow */
2229 if (infoPtr->items[i].rect.left == newselected )
2230 infoPtr->items[i].rect.left = iTargetRow;
2233 if (infoPtr->items[i].rect.left > newselected)
2234 infoPtr->items[i].rect.left-=1;
2240 for (i=0; i < infoPtr->uNumItem; i++)
2242 if (infoPtr->items[i].rect.top == newselected )
2243 infoPtr->items[i].rect.top = iTargetRow;
2246 if (infoPtr->items[i].rect.top > newselected)
2247 infoPtr->items[i].rect.top-=1;
2251 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2256 * Do the trivial cases first.
2258 if ( (!infoPtr->needsScrolling) ||
2259 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2262 if (infoPtr->leftmostVisible >= iSelected)
2264 infoPtr->leftmostVisible = iSelected;
2271 /* Calculate the part of the client area that is visible */
2272 GetClientRect(hwnd, &r);
2275 GetClientRect(infoPtr->hwndUpDown, &r);
2278 if ((infoPtr->items[iSelected].rect.right -
2279 infoPtr->items[iSelected].rect.left) >= width )
2281 /* Special case: width of selected item is greater than visible
2284 infoPtr->leftmostVisible = iSelected;
2288 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2290 if ((infoPtr->items[iSelected].rect.right -
2291 infoPtr->items[i].rect.left) < width)
2294 infoPtr->leftmostVisible = i;
2298 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2299 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2301 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2302 MAKELONG(infoPtr->leftmostVisible, 0));
2305 /******************************************************************************
2306 * TAB_InvalidateTabArea
2308 * This method will invalidate the portion of the control that contains the
2309 * tabs. It is called when the state of the control changes and needs
2312 static void TAB_InvalidateTabArea(
2317 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2318 INT lastRow = infoPtr->uNumRows - 1;
2320 if (lastRow < 0) return;
2322 GetClientRect(hwnd, &clientRect);
2324 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2326 clientRect.top = clientRect.bottom -
2327 infoPtr->tabHeight -
2328 lastRow * (infoPtr->tabHeight - 2) -
2329 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 3;
2331 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2333 clientRect.left = clientRect.right - infoPtr->tabHeight -
2334 lastRow * (infoPtr->tabHeight - 2) -
2335 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 2;
2337 else if(lStyle & TCS_VERTICAL)
2339 clientRect.right = clientRect.left + infoPtr->tabHeight +
2340 lastRow * (infoPtr->tabHeight - 2) -
2341 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 2;
2346 clientRect.bottom = clientRect.top + infoPtr->tabHeight +
2347 lastRow * (infoPtr->tabHeight - 2) +
2348 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 2;
2351 /* Punch out the updown control */
2352 if (infoPtr->needsScrolling && (clientRect.right > 0)) {
2353 GetClientRect(infoPtr->hwndUpDown, &r);
2354 clientRect.right = clientRect.right - (r.right - r.left);
2357 TRACE("invalidate (%ld,%ld)-(%ld,%ld)\n",
2358 clientRect.left,clientRect.top,
2359 clientRect.right,clientRect.bottom);
2361 InvalidateRect(hwnd, &clientRect, TRUE);
2365 TAB_Paint (HWND hwnd, WPARAM wParam)
2370 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2372 TRACE("erase %d, rect=(%ld,%ld)-(%ld,%ld)\n",
2374 ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
2377 TAB_EraseBackground (hwnd, hdc);
2379 TAB_Refresh (hwnd, hdc);
2382 EndPaint (hwnd, &ps);
2388 TAB_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2390 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2395 GetClientRect (hwnd, &rect);
2396 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2397 rect.top, rect.left, rect.bottom, rect.right);
2399 pti = (TCITEMA *)lParam;
2400 iItem = (INT)wParam;
2402 if (iItem < 0) return -1;
2403 if (iItem > infoPtr->uNumItem)
2404 iItem = infoPtr->uNumItem;
2406 TAB_DumpItemExternalA(pti, iItem);
2409 if (infoPtr->uNumItem == 0) {
2410 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
2411 infoPtr->uNumItem++;
2412 infoPtr->iSelected = 0;
2415 TAB_ITEM *oldItems = infoPtr->items;
2417 infoPtr->uNumItem++;
2418 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2420 /* pre insert copy */
2422 memcpy (&infoPtr->items[0], &oldItems[0],
2423 iItem * sizeof(TAB_ITEM));
2426 /* post insert copy */
2427 if (iItem < infoPtr->uNumItem - 1) {
2428 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2429 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2433 if (iItem <= infoPtr->iSelected)
2434 infoPtr->iSelected++;
2436 COMCTL32_Free (oldItems);
2439 infoPtr->items[iItem].mask = pti->mask;
2440 if (pti->mask & TCIF_TEXT)
2441 Str_SetPtrAtoW (&infoPtr->items[iItem].pszText, pti->pszText);
2443 if (pti->mask & TCIF_IMAGE)
2444 infoPtr->items[iItem].iImage = pti->iImage;
2446 if (pti->mask & TCIF_PARAM)
2447 infoPtr->items[iItem].lParam = pti->lParam;
2449 TAB_SetItemBounds(hwnd);
2450 if (infoPtr->uNumItem > 1)
2451 TAB_InvalidateTabArea(hwnd, infoPtr);
2453 InvalidateRect(hwnd, NULL, TRUE);
2455 TRACE("[%p]: added item %d %s\n",
2456 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2463 TAB_InsertItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2465 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2470 GetClientRect (hwnd, &rect);
2471 TRACE("Rect: %p T %li, L %li, B %li, R %li\n", hwnd,
2472 rect.top, rect.left, rect.bottom, rect.right);
2474 pti = (TCITEMW *)lParam;
2475 iItem = (INT)wParam;
2477 if (iItem < 0) return -1;
2478 if (iItem > infoPtr->uNumItem)
2479 iItem = infoPtr->uNumItem;
2481 TAB_DumpItemExternalW(pti, iItem);
2483 if (infoPtr->uNumItem == 0) {
2484 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
2485 infoPtr->uNumItem++;
2486 infoPtr->iSelected = 0;
2489 TAB_ITEM *oldItems = infoPtr->items;
2491 infoPtr->uNumItem++;
2492 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2494 /* pre insert copy */
2496 memcpy (&infoPtr->items[0], &oldItems[0],
2497 iItem * sizeof(TAB_ITEM));
2500 /* post insert copy */
2501 if (iItem < infoPtr->uNumItem - 1) {
2502 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2503 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2507 if (iItem <= infoPtr->iSelected)
2508 infoPtr->iSelected++;
2510 COMCTL32_Free (oldItems);
2513 infoPtr->items[iItem].mask = pti->mask;
2514 if (pti->mask & TCIF_TEXT)
2515 Str_SetPtrW (&infoPtr->items[iItem].pszText, pti->pszText);
2517 if (pti->mask & TCIF_IMAGE)
2518 infoPtr->items[iItem].iImage = pti->iImage;
2520 if (pti->mask & TCIF_PARAM)
2521 infoPtr->items[iItem].lParam = pti->lParam;
2523 TAB_SetItemBounds(hwnd);
2524 if (infoPtr->uNumItem > 1)
2525 TAB_InvalidateTabArea(hwnd, infoPtr);
2527 InvalidateRect(hwnd, NULL, TRUE);
2529 TRACE("[%p]: added item %d %s\n",
2530 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2537 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2539 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2540 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2544 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
2546 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2547 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use default. */
2548 if (LOWORD(lParam)) infoPtr->tabWidth = (INT)LOWORD(lParam);
2549 if (HIWORD(lParam)) infoPtr->tabHeight = (INT)HIWORD(lParam);
2550 TRACE("was h=%d,w=%d, now h=%d,w=%d\n",
2551 HIWORD(lResult), LOWORD(lResult),
2552 infoPtr->tabHeight, infoPtr->tabWidth);
2554 infoPtr->fSizeSet = TRUE;
2560 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2562 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2567 iItem = (INT)wParam;
2568 tabItem = (LPTCITEMA)lParam;
2570 TRACE("%d %p\n", iItem, tabItem);
2571 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2573 TAB_DumpItemExternalA(tabItem, iItem);
2575 wineItem = &infoPtr->items[iItem];
2577 if (tabItem->mask & TCIF_IMAGE)
2578 wineItem->iImage = tabItem->iImage;
2580 if (tabItem->mask & TCIF_PARAM)
2581 wineItem->lParam = tabItem->lParam;
2583 if (tabItem->mask & TCIF_RTLREADING)
2584 FIXME("TCIF_RTLREADING\n");
2586 if (tabItem->mask & TCIF_STATE)
2587 wineItem->dwState = tabItem->dwState;
2589 if (tabItem->mask & TCIF_TEXT)
2590 Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2592 /* Update and repaint tabs */
2593 TAB_SetItemBounds(hwnd);
2594 TAB_InvalidateTabArea(hwnd,infoPtr);
2601 TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2603 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2608 iItem = (INT)wParam;
2609 tabItem = (LPTCITEMW)lParam;
2611 TRACE("%d %p\n", iItem, tabItem);
2612 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2614 TAB_DumpItemExternalW(tabItem, iItem);
2616 wineItem = &infoPtr->items[iItem];
2618 if (tabItem->mask & TCIF_IMAGE)
2619 wineItem->iImage = tabItem->iImage;
2621 if (tabItem->mask & TCIF_PARAM)
2622 wineItem->lParam = tabItem->lParam;
2624 if (tabItem->mask & TCIF_RTLREADING)
2625 FIXME("TCIF_RTLREADING\n");
2627 if (tabItem->mask & TCIF_STATE)
2628 wineItem->dwState = tabItem->dwState;
2630 if (tabItem->mask & TCIF_TEXT)
2631 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2633 /* Update and repaint tabs */
2634 TAB_SetItemBounds(hwnd);
2635 TAB_InvalidateTabArea(hwnd,infoPtr);
2642 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2644 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2646 return infoPtr->uNumItem;
2651 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2653 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2658 iItem = (INT)wParam;
2659 tabItem = (LPTCITEMA)lParam;
2661 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2664 wineItem = &infoPtr->items[iItem];
2666 if (tabItem->mask & TCIF_IMAGE)
2667 tabItem->iImage = wineItem->iImage;
2669 if (tabItem->mask & TCIF_PARAM)
2670 tabItem->lParam = wineItem->lParam;
2672 if (tabItem->mask & TCIF_RTLREADING)
2673 FIXME("TCIF_RTLREADING\n");
2675 if (tabItem->mask & TCIF_STATE)
2676 tabItem->dwState = wineItem->dwState;
2678 if (tabItem->mask & TCIF_TEXT)
2679 Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2681 TAB_DumpItemExternalA(tabItem, iItem);
2688 TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2690 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2695 iItem = (INT)wParam;
2696 tabItem = (LPTCITEMW)lParam;
2698 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2701 wineItem=& infoPtr->items[iItem];
2703 if (tabItem->mask & TCIF_IMAGE)
2704 tabItem->iImage = wineItem->iImage;
2706 if (tabItem->mask & TCIF_PARAM)
2707 tabItem->lParam = wineItem->lParam;
2709 if (tabItem->mask & TCIF_RTLREADING)
2710 FIXME("TCIF_RTLREADING\n");
2712 if (tabItem->mask & TCIF_STATE)
2713 tabItem->dwState = wineItem->dwState;
2715 if (tabItem->mask & TCIF_TEXT)
2716 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2718 TAB_DumpItemExternalW(tabItem, iItem);
2725 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2727 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2728 INT iItem = (INT) wParam;
2729 BOOL bResult = FALSE;
2731 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2733 TAB_ITEM *oldItems = infoPtr->items;
2735 infoPtr->uNumItem--;
2736 infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
2739 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
2741 if (iItem < infoPtr->uNumItem)
2742 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
2743 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
2745 COMCTL32_Free(oldItems);
2747 /* Readjust the selected index */
2748 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2749 infoPtr->iSelected--;
2751 if (iItem < infoPtr->iSelected)
2752 infoPtr->iSelected--;
2754 if (infoPtr->uNumItem == 0)
2755 infoPtr->iSelected = -1;
2757 /* Reposition and repaint tabs */
2758 TAB_SetItemBounds(hwnd);
2759 TAB_InvalidateTabArea(hwnd,infoPtr);
2768 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2770 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2772 COMCTL32_Free (infoPtr->items);
2773 infoPtr->uNumItem = 0;
2774 infoPtr->iSelected = -1;
2775 if (infoPtr->iHotTracked >= 0)
2776 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2777 infoPtr->iHotTracked = -1;
2779 TAB_SetItemBounds(hwnd);
2780 TAB_InvalidateTabArea(hwnd,infoPtr);
2786 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2788 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2791 return (LRESULT)infoPtr->hFont;
2795 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2798 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2800 TRACE("%x %lx\n",wParam, lParam);
2802 infoPtr->hFont = (HFONT)wParam;
2804 TAB_SetItemBounds(hwnd);
2806 TAB_InvalidateTabArea(hwnd, infoPtr);
2813 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2815 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2818 return (LRESULT)infoPtr->himl;
2822 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2824 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2825 HIMAGELIST himlPrev;
2828 himlPrev = infoPtr->himl;
2829 infoPtr->himl= (HIMAGELIST)lParam;
2830 return (LRESULT)himlPrev;
2834 TAB_GetUnicodeFormat (HWND hwnd)
2836 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2837 return infoPtr->bUnicode;
2841 TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
2843 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2844 BOOL bTemp = infoPtr->bUnicode;
2846 infoPtr->bUnicode = (BOOL)wParam;
2852 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2855 /* I'm not really sure what the following code was meant to do.
2856 This is what it is doing:
2857 When WM_SIZE is sent with SIZE_RESTORED, the control
2858 gets positioned in the top left corner.
2862 UINT uPosFlags,cx,cy;
2866 parent = GetParent (hwnd);
2867 GetClientRect(parent, &parent_rect);
2870 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2871 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2873 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2874 cx, cy, uPosFlags | SWP_NOZORDER);
2876 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2879 /* Recompute the size/position of the tabs. */
2880 TAB_SetItemBounds (hwnd);
2882 /* Force a repaint of the control. */
2883 InvalidateRect(hwnd, NULL, TRUE);
2890 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2893 TEXTMETRICA fontMetrics;
2898 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
2900 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
2902 infoPtr->uNumItem = 0;
2903 infoPtr->uNumRows = 0;
2906 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
2907 infoPtr->iSelected = -1;
2908 infoPtr->iHotTracked = -1;
2909 infoPtr->uFocus = -1;
2910 infoPtr->hwndToolTip = 0;
2911 infoPtr->DoRedraw = TRUE;
2912 infoPtr->needsScrolling = FALSE;
2913 infoPtr->hwndUpDown = 0;
2914 infoPtr->leftmostVisible = 0;
2915 infoPtr->fSizeSet = FALSE;
2916 infoPtr->bUnicode = IsWindowUnicode (hwnd);
2918 TRACE("Created tab control, hwnd [%p]\n", hwnd);
2920 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2921 if you don't specify it in CreateWindow. This is necessary in
2922 order for paint to work correctly. This follows windows behaviour. */
2923 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
2924 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2926 if (dwStyle & TCS_TOOLTIPS) {
2927 /* Create tooltip control */
2928 infoPtr->hwndToolTip =
2929 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2930 CW_USEDEFAULT, CW_USEDEFAULT,
2931 CW_USEDEFAULT, CW_USEDEFAULT,
2934 /* Send NM_TOOLTIPSCREATED notification */
2935 if (infoPtr->hwndToolTip) {
2936 NMTOOLTIPSCREATED nmttc;
2938 nmttc.hdr.hwndFrom = hwnd;
2939 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
2940 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2941 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2943 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2944 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
2949 * We need to get text information so we need a DC and we need to select
2953 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
2955 /* Use the system font to determine the initial height of a tab. */
2956 GetTextMetricsA(hdc, &fontMetrics);
2959 * Make sure there is enough space for the letters + growing the
2960 * selected item + extra space for the selected item.
2962 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET +
2963 ((dwStyle & TCS_BUTTONS) ? 2 : 1) *
2964 VERTICAL_ITEM_PADDING;
2966 /* Initialize the width of a tab. */
2967 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
2969 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth);
2971 SelectObject (hdc, hOldFont);
2972 ReleaseDC(hwnd, hdc);
2978 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
2980 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2986 if (infoPtr->items) {
2987 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
2988 if (infoPtr->items[iItem].pszText)
2989 COMCTL32_Free (infoPtr->items[iItem].pszText);
2991 COMCTL32_Free (infoPtr->items);
2994 if (infoPtr->hwndToolTip)
2995 DestroyWindow (infoPtr->hwndToolTip);
2997 if (infoPtr->hwndUpDown)
2998 DestroyWindow(infoPtr->hwndUpDown);
3000 if (infoPtr->iHotTracked >= 0)
3001 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
3003 COMCTL32_Free (infoPtr);
3004 SetWindowLongA(hwnd, 0, 0);
3008 static LRESULT WINAPI
3009 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3012 TRACE("hwnd=%p msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
3013 if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
3014 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3018 case TCM_GETIMAGELIST:
3019 return TAB_GetImageList (hwnd, wParam, lParam);
3021 case TCM_SETIMAGELIST:
3022 return TAB_SetImageList (hwnd, wParam, lParam);
3024 case TCM_GETITEMCOUNT:
3025 return TAB_GetItemCount (hwnd, wParam, lParam);
3028 return TAB_GetItemA (hwnd, wParam, lParam);
3031 return TAB_GetItemW (hwnd, wParam, lParam);
3034 return TAB_SetItemA (hwnd, wParam, lParam);
3037 return TAB_SetItemW (hwnd, wParam, lParam);
3039 case TCM_DELETEITEM:
3040 return TAB_DeleteItem (hwnd, wParam, lParam);
3042 case TCM_DELETEALLITEMS:
3043 return TAB_DeleteAllItems (hwnd, wParam, lParam);
3045 case TCM_GETITEMRECT:
3046 return TAB_GetItemRect (hwnd, wParam, lParam);
3049 return TAB_GetCurSel (hwnd);
3052 return TAB_HitTest (hwnd, wParam, lParam);
3055 return TAB_SetCurSel (hwnd, wParam);
3057 case TCM_INSERTITEMA:
3058 return TAB_InsertItemA (hwnd, wParam, lParam);
3060 case TCM_INSERTITEMW:
3061 return TAB_InsertItemW (hwnd, wParam, lParam);
3063 case TCM_SETITEMEXTRA:
3064 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
3067 case TCM_ADJUSTRECT:
3068 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
3070 case TCM_SETITEMSIZE:
3071 return TAB_SetItemSize (hwnd, wParam, lParam);
3073 case TCM_REMOVEIMAGE:
3074 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
3077 case TCM_SETPADDING:
3078 FIXME("Unimplemented msg TCM_SETPADDING\n");
3081 case TCM_GETROWCOUNT:
3082 return TAB_GetRowCount(hwnd);
3084 case TCM_GETUNICODEFORMAT:
3085 return TAB_GetUnicodeFormat (hwnd);
3087 case TCM_SETUNICODEFORMAT:
3088 return TAB_SetUnicodeFormat (hwnd, wParam);
3090 case TCM_HIGHLIGHTITEM:
3091 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
3094 case TCM_GETTOOLTIPS:
3095 return TAB_GetToolTips (hwnd, wParam, lParam);
3097 case TCM_SETTOOLTIPS:
3098 return TAB_SetToolTips (hwnd, wParam, lParam);
3100 case TCM_GETCURFOCUS:
3101 return TAB_GetCurFocus (hwnd);
3103 case TCM_SETCURFOCUS:
3104 return TAB_SetCurFocus (hwnd, wParam);
3106 case TCM_SETMINTABWIDTH:
3107 FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
3110 case TCM_DESELECTALL:
3111 FIXME("Unimplemented msg TCM_DESELECTALL\n");
3114 case TCM_GETEXTENDEDSTYLE:
3115 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
3118 case TCM_SETEXTENDEDSTYLE:
3119 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
3123 return TAB_GetFont (hwnd, wParam, lParam);
3126 return TAB_SetFont (hwnd, wParam, lParam);
3129 return TAB_Create (hwnd, wParam, lParam);
3132 return TAB_Destroy (hwnd, wParam, lParam);
3135 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3137 case WM_LBUTTONDOWN:
3138 return TAB_LButtonDown (hwnd, wParam, lParam);
3141 return TAB_LButtonUp (hwnd, wParam, lParam);
3144 return SendMessageA(GetParent(hwnd), WM_NOTIFY, wParam, lParam);
3146 case WM_RBUTTONDOWN:
3147 return TAB_RButtonDown (hwnd, wParam, lParam);
3150 return TAB_MouseMove (hwnd, wParam, lParam);
3153 return TAB_EraseBackground (hwnd, (HDC)wParam);
3156 return TAB_Paint (hwnd, wParam);
3159 return TAB_Size (hwnd, wParam, lParam);
3162 return TAB_SetRedraw (hwnd, wParam);
3165 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
3167 case WM_STYLECHANGED:
3168 TAB_SetItemBounds (hwnd);
3169 InvalidateRect(hwnd, NULL, TRUE);
3172 case WM_SYSCOLORCHANGE:
3173 COMCTL32_RefreshSysColors();
3178 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
3181 return TAB_KeyUp(hwnd, wParam);
3183 return TAB_NCHitTest(hwnd, lParam);
3186 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
3187 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
3188 uMsg, wParam, lParam);
3189 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
3201 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3202 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
3203 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
3204 wndClass.cbClsExtra = 0;
3205 wndClass.cbWndExtra = sizeof(TAB_INFO *);
3206 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
3207 wndClass.hbrBackground = NULL;
3208 wndClass.lpszClassName = WC_TABCONTROLA;
3210 RegisterClassA (&wndClass);
3215 TAB_Unregister (void)
3217 UnregisterClassA (WC_TABCONTROLA, NULL);