Protect against use of comctl32 private control memory after it has
[wine.git] / dlls / comctl32 / tab.c
bloba72d443bf5eaa8aad6fd5f07c35a03f057b8e23f
1 /*
2 * Tab control
4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
8 * TODO:
9 * Image list support
10 * Multiline support
11 * Unicode support
14 #include <string.h>
16 #include "winbase.h"
17 #include "commctrl.h"
18 #include "tab.h"
19 #include "debugtools.h"
20 #include "cache.h"
21 #include "win.h"
23 DEFAULT_DEBUG_CHANNEL(tab)
25 /******************************************************************************
26 * Positioning constants
28 #define SELECTED_TAB_OFFSET 2
29 #define HORIZONTAL_ITEM_PADDING 5
30 #define VERTICAL_ITEM_PADDING 3
31 #define ROUND_CORNER_SIZE 2
32 #define FOCUS_RECT_HOFFSET 2
33 #define FOCUS_RECT_VOFFSET 1
34 #define DISPLAY_AREA_PADDINGX 2
35 #define DISPLAY_AREA_PADDINGY 2
36 #define CONTROL_BORDER_SIZEX 2
37 #define CONTROL_BORDER_SIZEY 2
38 #define BUTTON_SPACINGX 10
39 #define DEFAULT_TAB_WIDTH 96
41 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
43 /******************************************************************************
44 * Prototypes
46 static void TAB_Refresh (HWND hwnd, HDC hdc);
47 static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
48 static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
50 static BOOL
51 TAB_SendSimpleNotify (HWND hwnd, UINT code)
53 NMHDR nmhdr;
55 nmhdr.hwndFrom = hwnd;
56 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
57 nmhdr.code = code;
59 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
60 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
64 static VOID
65 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
66 WPARAM wParam, LPARAM lParam)
68 MSG msg;
70 msg.hwnd = hwndMsg;
71 msg.message = uMsg;
72 msg.wParam = wParam;
73 msg.lParam = lParam;
74 msg.time = GetMessageTime ();
75 msg.pt.x = LOWORD(GetMessagePos ());
76 msg.pt.y = HIWORD(GetMessagePos ());
78 SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
83 static LRESULT
84 TAB_GetCurSel (HWND hwnd)
86 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
88 return infoPtr->iSelected;
91 static LRESULT
92 TAB_GetCurFocus (HWND hwnd)
94 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
96 return infoPtr->uFocus;
99 static LRESULT
100 TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
102 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
104 if (infoPtr == NULL) return 0;
105 return infoPtr->hwndToolTip;
109 static LRESULT
110 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
112 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
113 INT iItem=(INT) wParam;
114 INT prevItem;
116 prevItem=-1;
117 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
118 prevItem=infoPtr->iSelected;
119 infoPtr->iSelected=iItem;
120 TAB_EnsureSelectionVisible(hwnd, infoPtr);
121 TAB_InvalidateTabArea(hwnd, infoPtr);
123 return prevItem;
126 static LRESULT
127 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
129 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
130 INT iItem=(INT) wParam;
132 if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
134 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
135 FIXME("Should set input focus\n");
136 } else {
137 if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) {
138 infoPtr->uFocus=iItem;
139 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
140 infoPtr->iSelected = iItem;
141 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
143 TAB_EnsureSelectionVisible(hwnd, infoPtr);
144 TAB_InvalidateTabArea(hwnd, infoPtr);
148 return 0;
151 static LRESULT
152 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
154 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
156 if (infoPtr == NULL) return 0;
157 infoPtr->hwndToolTip = (HWND)wParam;
158 return 0;
161 /******************************************************************************
162 * TAB_InternalGetItemRect
164 * This method will calculate the rectangle representing a given tab item in
165 * client coordinates. This method takes scrolling into account.
167 * This method returns TRUE if the item is visible in the window and FALSE
168 * if it is completely outside the client area.
170 static BOOL TAB_InternalGetItemRect(
171 HWND hwnd,
172 TAB_INFO* infoPtr,
173 INT itemIndex,
174 RECT* itemRect,
175 RECT* selectedRect)
177 RECT tmpItemRect,clientRect;
178 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
181 * Perform a sanity check and a trivial visibility check.
183 if ( (infoPtr->uNumItem <=0) ||
184 (itemIndex >= infoPtr->uNumItem) ||
185 (!(lStyle &TCS_MULTILINE) && (itemIndex < infoPtr->leftmostVisible)) )
186 return FALSE;
189 * Avoid special cases in this procedure by assigning the "out"
190 * parameters if the caller didn't supply them
192 if (itemRect==NULL)
193 itemRect = &tmpItemRect;
196 * Retrieve the unmodified item rect.
198 *itemRect = infoPtr->items[itemIndex].rect;
201 * calculate the times bottom and top based on the row
203 GetClientRect(hwnd, &clientRect);
205 if (lStyle & TCS_BOTTOM)
207 itemRect->bottom = clientRect.bottom -
208 SELECTED_TAB_OFFSET -
209 itemRect->top * (infoPtr->tabHeight - 2);
211 itemRect->top = clientRect.bottom -
212 infoPtr->tabHeight -
213 itemRect->top * ( infoPtr->tabHeight - 2);
215 else
217 itemRect->bottom = clientRect.top +
218 infoPtr->tabHeight +
219 itemRect->top * (infoPtr->tabHeight - 2);
220 itemRect->top = clientRect.top +
221 SELECTED_TAB_OFFSET+
222 itemRect->top * (infoPtr->tabHeight - 2);
226 * "scroll" it to make sure the item at the very left of the
227 * tab control is the leftmost visible tab.
229 OffsetRect(itemRect,
230 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
234 * Move the rectangle so the first item is slightly offset from
235 * the left of the tab control.
237 OffsetRect(itemRect,
238 SELECTED_TAB_OFFSET,
243 * Now, calculate the position of the item as if it were selected.
245 if (selectedRect!=NULL)
247 CopyRect(selectedRect, itemRect);
250 * The rectangle of a selected item is a bit wider.
252 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
255 * If it also a bit higher.
257 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
259 selectedRect->top -=2; /* the border is thicker on the bottom */
260 selectedRect->bottom +=SELECTED_TAB_OFFSET;
262 else
264 selectedRect->top -=SELECTED_TAB_OFFSET;
265 selectedRect->bottom+=1;
269 return TRUE;
272 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
274 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
275 (LPRECT)lParam, (LPRECT)NULL);
278 /******************************************************************************
279 * TAB_KeyUp
281 * This method is called to handle keyboard input
283 static LRESULT TAB_KeyUp(
284 HWND hwnd,
285 WPARAM keyCode)
287 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
288 int newItem = -1;
290 switch (keyCode)
292 case VK_LEFT:
293 newItem = infoPtr->uFocus-1;
294 break;
295 case VK_RIGHT:
296 newItem = infoPtr->uFocus+1;
297 break;
301 * If we changed to a valid item, change the selection
303 if ( (newItem >= 0) &&
304 (newItem < infoPtr->uNumItem) &&
305 (infoPtr->uFocus != newItem) )
307 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
309 infoPtr->iSelected = newItem;
310 infoPtr->uFocus = newItem;
311 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
313 TAB_EnsureSelectionVisible(hwnd, infoPtr);
314 TAB_InvalidateTabArea(hwnd, infoPtr);
318 return 0;
321 /******************************************************************************
322 * TAB_FocusChanging
324 * This method is called whenever the focus goes in or out of this control
325 * it is used to update the visual state of the control.
327 static LRESULT TAB_FocusChanging(
328 HWND hwnd,
329 UINT uMsg,
330 WPARAM wParam,
331 LPARAM lParam)
333 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
334 RECT selectedRect;
335 BOOL isVisible;
338 * Get the rectangle for the item.
340 isVisible = TAB_InternalGetItemRect(hwnd,
341 infoPtr,
342 infoPtr->uFocus,
343 NULL,
344 &selectedRect);
347 * If the rectangle is not completely invisible, invalidate that
348 * portion of the window.
350 if (isVisible)
352 InvalidateRect(hwnd, &selectedRect, TRUE);
356 * Don't otherwise disturb normal behavior.
358 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
361 static HWND TAB_InternalHitTest (
362 HWND hwnd,
363 TAB_INFO* infoPtr,
364 POINT pt,
365 UINT* flags)
368 RECT rect;
369 int iCount;
371 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
373 TAB_InternalGetItemRect(hwnd,
374 infoPtr,
375 iCount,
376 &rect,
377 NULL);
379 if (PtInRect (&rect, pt))
381 *flags = TCHT_ONITEM;
382 return iCount;
386 *flags=TCHT_NOWHERE;
387 return -1;
390 static LRESULT
391 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
393 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
394 LPTCHITTESTINFO lptest=(LPTCHITTESTINFO) lParam;
396 return TAB_InternalHitTest (hwnd, infoPtr,lptest->pt,&lptest->flags);
400 static LRESULT
401 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
403 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
404 POINT pt;
405 INT newItem,dummy;
407 if (infoPtr->hwndToolTip)
408 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
409 WM_LBUTTONDOWN, wParam, lParam);
411 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
412 SetFocus (hwnd);
415 if (infoPtr->hwndToolTip)
416 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
417 WM_LBUTTONDOWN, wParam, lParam);
419 pt.x = (INT)LOWORD(lParam);
420 pt.y = (INT)HIWORD(lParam);
422 newItem=TAB_InternalHitTest (hwnd, infoPtr,pt,&dummy);
424 TRACE("On Tab, item %d\n", newItem);
426 if ( (newItem!=-1) &&
427 (infoPtr->iSelected != newItem) )
429 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE)
431 infoPtr->iSelected = newItem;
432 infoPtr->uFocus = newItem;
433 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
435 TAB_EnsureSelectionVisible(hwnd, infoPtr);
437 TAB_InvalidateTabArea(hwnd, infoPtr);
440 return 0;
443 static LRESULT
444 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
446 TAB_SendSimpleNotify(hwnd, NM_CLICK);
448 return 0;
451 static LRESULT
452 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
454 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
455 return 0;
458 static LRESULT
459 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
461 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
463 if (infoPtr->hwndToolTip)
464 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
465 WM_LBUTTONDOWN, wParam, lParam);
466 return 0;
469 /******************************************************************************
470 * TAB_AdjustRect
472 * Calculates the tab control's display area given the windows rectangle or
473 * the window rectangle given the requested display rectangle.
475 static LRESULT TAB_AdjustRect(
476 HWND hwnd,
477 WPARAM fLarger,
478 LPRECT prc)
480 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
482 if (fLarger)
485 * Go from display rectangle
489 * Add the height of the tabs.
491 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
492 prc->bottom += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
493 else
494 prc->top -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
497 * Inflate the rectangle for the padding
499 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
502 * Inflate for the border
504 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
506 else
509 * Go from window rectangle.
513 * Deflate the rectangle for the border
515 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
518 * Deflate the rectangle for the padding
520 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
523 * Remove the height of the tabs.
525 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
526 prc->bottom -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
527 else
528 prc->top += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
532 return 0;
535 /******************************************************************************
536 * TAB_OnHScroll
538 * This method will handle the notification from the scroll control and
539 * perform the scrolling operation on the tab control.
541 static LRESULT TAB_OnHScroll(
542 HWND hwnd,
543 int nScrollCode,
544 int nPos,
545 HWND hwndScroll)
547 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
549 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
551 if(nPos < infoPtr->leftmostVisible)
552 infoPtr->leftmostVisible--;
553 else
554 infoPtr->leftmostVisible++;
556 TAB_InvalidateTabArea(hwnd, infoPtr);
557 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
558 MAKELONG(infoPtr->leftmostVisible, 0));
561 return 0;
564 /******************************************************************************
565 * TAB_SetupScroling
567 * This method will check the current scrolling state and make sure the
568 * scrolling control is displayed (or not).
570 static void TAB_SetupScrolling(
571 HWND hwnd,
572 TAB_INFO* infoPtr,
573 const RECT* clientRect)
575 INT maxRange = 0;
576 if (infoPtr->needsScrolling)
578 RECT controlPos;
579 INT vsize, tabwidth;
582 * Calculate the position of the scroll control.
584 controlPos.right = clientRect->right;
585 controlPos.left = controlPos.right - 2*GetSystemMetrics(SM_CXHSCROLL);
587 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
589 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
590 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
592 else
594 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
595 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
599 * If we don't have a scroll control yet, we want to create one.
600 * If we have one, we want to make sure it's positioned right.
602 if (infoPtr->hwndUpDown==0)
604 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
606 WS_VISIBLE | WS_CHILD | UDS_HORZ,
607 controlPos.left, controlPos.top,
608 controlPos.right - controlPos.left,
609 controlPos.bottom - controlPos.top,
610 hwnd,
611 (HMENU)NULL,
612 (HINSTANCE)NULL,
613 NULL);
615 else
617 SetWindowPos(infoPtr->hwndUpDown,
618 (HWND)NULL,
619 controlPos.left, controlPos.top,
620 controlPos.right - controlPos.left,
621 controlPos.bottom - controlPos.top,
622 SWP_SHOWWINDOW | SWP_NOZORDER);
625 /* Now calculate upper limit of the updown control range.
626 * We do this by calculating how many tabs will be offscreen when the
627 * last tab is visible.
629 if(infoPtr->uNumItem)
631 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
632 maxRange = infoPtr->uNumItem;
633 tabwidth = infoPtr->items[maxRange-1].rect.right;
635 for(; maxRange > 0; maxRange--)
637 if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
638 break;
641 if(maxRange == infoPtr->uNumItem)
642 maxRange--;
645 else
648 * If we once had a scroll control... hide it.
650 if (infoPtr->hwndUpDown!=0)
652 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
655 if (infoPtr->hwndUpDown)
656 SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
659 /******************************************************************************
660 * TAB_SetItemBounds
662 * This method will calculate the position rectangles of all the items in the
663 * control. The rectangle calculated starts at 0 for the first item in the
664 * list and ignores scrolling and selection.
665 * It also uses the current font to determine the height of the tab row and
666 * it checks if all the tabs fit in the client area of the window. If they
667 * dont, a scrolling control is added.
669 static void TAB_SetItemBounds (HWND hwnd)
671 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
672 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
673 TEXTMETRICA fontMetrics;
674 INT curItem;
675 INT curItemLeftPos;
676 INT curItemRowCount;
677 HFONT hFont, hOldFont;
678 HDC hdc;
679 RECT clientRect;
680 SIZE size;
683 * We need to get text information so we need a DC and we need to select
684 * a font.
686 hdc = GetDC(hwnd);
688 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
689 hOldFont = SelectObject (hdc, hFont);
692 * We will base the rectangle calculations on the client rectangle
693 * of the control.
695 GetClientRect(hwnd, &clientRect);
698 * The leftmost item will be "0" aligned
700 curItemLeftPos = 0;
701 curItemRowCount = 0;
703 if ( !(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet) )
705 int item_height;
706 int icon_height = 0;
709 * Use the current font to determine the height of a tab.
711 GetTextMetricsA(hdc, &fontMetrics);
714 * Get the icon height
716 if (infoPtr->himl)
717 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
720 * Take the highest between font or icon
722 if (fontMetrics.tmHeight > icon_height)
723 item_height = fontMetrics.tmHeight;
724 else
725 item_height = icon_height;
728 * Make sure there is enough space for the letters + icon + growing the
729 * selected item + extra space for the selected item.
731 infoPtr->tabHeight = item_height + 2*VERTICAL_ITEM_PADDING +
732 SELECTED_TAB_OFFSET;
735 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
738 * Set the leftmost position of the tab.
740 infoPtr->items[curItem].rect.left = curItemLeftPos;
742 if ( (lStyle & TCS_FIXEDWIDTH) || ((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
744 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
745 infoPtr->tabWidth +
746 2*HORIZONTAL_ITEM_PADDING;
748 else
750 int icon_width = 0;
751 int num = 2;
754 * Calculate how wide the tab is depending on the text it contains
756 GetTextExtentPoint32A(hdc, infoPtr->items[curItem].pszText,
757 lstrlenA(infoPtr->items[curItem].pszText), &size);
760 * Add the icon width
762 if (infoPtr->himl)
764 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
765 num++;
768 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
769 size.cx + icon_width +
770 num*HORIZONTAL_ITEM_PADDING;
774 * Check if this is a multiline tab control and if so
775 * check to see if we should wrap the tabs
777 * Because we are going to arange all these tabs evenly
778 * really we are basically just counting rows at this point
782 if ((lStyle & TCS_MULTILINE)&&
783 (infoPtr->items[curItem].rect.right > clientRect.right))
785 infoPtr->items[curItem].rect.right -=
786 infoPtr->items[curItem].rect.left;
787 infoPtr->items[curItem].rect.left = 0;
788 curItemRowCount ++;
791 infoPtr->items[curItem].rect.bottom = 0;
792 infoPtr->items[curItem].rect.top = curItemRowCount;
794 TRACE("TextSize: %i\n ", size.cx);
795 TRACE("Rect: T %i, L %i, B %i, R %i\n",
796 infoPtr->items[curItem].rect.top,
797 infoPtr->items[curItem].rect.left,
798 infoPtr->items[curItem].rect.bottom,
799 infoPtr->items[curItem].rect.right);
802 * The leftmost position of the next item is the rightmost position
803 * of this one.
805 if (lStyle & TCS_BUTTONS)
806 curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX;
807 else
808 curItemLeftPos = infoPtr->items[curItem].rect.right;
811 if (!(lStyle & TCS_MULTILINE))
814 * Check if we need a scrolling control.
816 infoPtr->needsScrolling = (curItemLeftPos + (2*SELECTED_TAB_OFFSET) >
817 clientRect.right);
819 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
823 * Set the number of rows
826 infoPtr->uNumRows = curItemRowCount;
828 if ((lStyle & TCS_MULTILINE)&&(infoPtr->uNumItem > 0))
830 INT widthDiff,remainder;
831 INT tabPerRow,remTab;
832 INT iRow,iItm;
833 INT iIndexStart=0,iIndexEnd=0, iCount=0;
836 * Ok Microsoft trys to even out the rows. place the same
837 * number of tabs in each row. So lets give that a shot
841 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows + 1);
842 remTab = infoPtr->uNumItem % (infoPtr->uNumRows + 1);
844 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
845 iItm<infoPtr->uNumItem;
846 iItm++,iCount++)
848 if (iCount >= ((iRow<remTab)?tabPerRow+1:tabPerRow))
850 iRow++;
851 curItemLeftPos = 0;
852 iCount = 0;
855 * normalize the current rect
857 infoPtr->items[iItm].rect.right -=
858 infoPtr->items[iItm].rect.left;
859 infoPtr->items[iItm].rect.left = 0;
861 infoPtr->items[iItm].rect.top = iRow;
862 infoPtr->items[iItm].rect.left +=curItemLeftPos;
863 infoPtr->items[iItm].rect.right +=curItemLeftPos;
864 if (lStyle & TCS_BUTTONS)
865 curItemLeftPos = infoPtr->items[iItm].rect.right +
866 BUTTON_SPACINGX;
867 else
868 curItemLeftPos = infoPtr->items[iItm].rect.right;
872 * Justify the rows
876 while(iIndexStart < infoPtr->uNumItem)
879 * find the indexs of the row
881 for (iIndexEnd=iIndexStart;
882 (iIndexEnd < infoPtr->uNumItem) &&
883 (infoPtr->items[iIndexEnd].rect.top ==
884 infoPtr->items[iIndexStart].rect.top) ;
885 iIndexEnd++)
886 /* intentionaly blank */;
889 * we need to justify these tabs so they fill the whole given
890 * client area
893 widthDiff = clientRect.right - (2*SELECTED_TAB_OFFSET) -
894 infoPtr->items[iIndexEnd-1].rect.right;
896 iCount = iIndexEnd-iIndexStart;
898 if (iCount)
900 INT iIndex;
901 remainder = widthDiff % iCount;
902 widthDiff = widthDiff / iCount;
903 for (iIndex=iIndexStart,iCount=0; iIndex < iIndexEnd;
904 iIndex++,iCount++)
906 infoPtr->items[iIndex].rect.left +=iCount*widthDiff;
907 infoPtr->items[iIndex].rect.right +=(iCount+1)*widthDiff;
909 infoPtr->items[iIndex-1].rect.right += remainder;
912 iIndexStart=iIndexEnd;
917 TAB_EnsureSelectionVisible(hwnd,infoPtr);
919 * Cleanup
921 SelectObject (hdc, hOldFont);
922 ReleaseDC (hwnd, hdc);
925 /******************************************************************************
926 * TAB_DrawItem
928 * This method is used to draw a single tab into the tab control.
930 static void TAB_DrawItem(
931 HWND hwnd,
932 HDC hdc,
933 INT iItem)
935 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
936 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
937 RECT itemRect;
938 RECT selectedRect;
939 BOOL isVisible;
940 RECT r;
943 * Get the rectangle for the item.
945 isVisible = TAB_InternalGetItemRect(hwnd,
946 infoPtr,
947 iItem,
948 &itemRect,
949 &selectedRect);
951 if (isVisible)
953 HBRUSH hbr = CreateSolidBrush (GetSysColor(COLOR_BTNFACE));
954 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
955 HPEN hbPen = GetSysColorPen (COLOR_BTNSHADOW);
956 HPEN hsdPen = GetSysColorPen (COLOR_BTNTEXT);
957 HPEN hfocusPen = CreatePen(PS_DOT, 1, GetSysColor(COLOR_BTNTEXT));
958 HPEN holdPen;
959 INT oldBkMode;
960 INT cx,cy;
961 BOOL deleteBrush = TRUE;
963 if (lStyle & TCS_BUTTONS)
966 * Get item rectangle.
968 r = itemRect;
970 holdPen = SelectObject (hdc, hwPen);
972 if (iItem == infoPtr->iSelected)
975 * Background color.
977 if (!((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
979 COLORREF bk = GetSysColor(COLOR_3DHILIGHT);
980 DeleteObject(hbr);
981 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
982 SetTextColor(hdc, GetSysColor(COLOR_3DFACE));
983 SetBkColor(hdc, bk);
985 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
986 * we better use 0x55aa bitmap brush to make scrollbar's background
987 * look different from the window background.
989 if (bk == GetSysColor(COLOR_WINDOW))
990 hbr = CACHE_GetPattern55AABrush();
992 deleteBrush = FALSE;
996 * Erase the background.
998 FillRect(hdc, &r, hbr);
1001 * Draw the tab now.
1002 * The rectangles calculated exclude the right and bottom
1003 * borders of the rectangle. To simply the following code, those
1004 * borders are shaved-off beforehand.
1006 r.right--;
1007 r.bottom--;
1009 /* highlight */
1010 MoveToEx (hdc, r.left, r.bottom, NULL);
1011 LineTo (hdc, r.right, r.bottom);
1012 LineTo (hdc, r.right, r.top);
1014 /* shadow */
1015 SelectObject(hdc, hbPen);
1016 LineTo (hdc, r.left, r.top);
1017 LineTo (hdc, r.left, r.bottom);
1019 else
1022 * Erase the background.
1024 FillRect(hdc, &r, hbr);
1026 /* highlight */
1027 MoveToEx (hdc, r.left, r.bottom, NULL);
1028 LineTo (hdc, r.left, r.top);
1029 LineTo (hdc, r.right, r.top);
1031 /* shadow */
1032 SelectObject(hdc, hbPen);
1033 LineTo (hdc, r.right, r.bottom);
1034 LineTo (hdc, r.left, r.bottom);
1037 else
1040 * Background color.
1042 DeleteObject(hbr);
1043 hbr = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1046 * We draw a rectangle of different sizes depending on the selection
1047 * state.
1049 if (iItem == infoPtr->iSelected)
1050 r = selectedRect;
1051 else
1052 r = itemRect;
1055 * Erase the background.
1056 * This is necessary when drawing the selected item since it is larger
1057 * than the others, it might overlap with stuff already drawn by the
1058 * other tabs
1060 FillRect(hdc, &r, hbr);
1063 * Draw the tab now.
1064 * The rectangles calculated exclude the right and bottom
1065 * borders of the rectangle. To simply the following code, those
1066 * borders are shaved-off beforehand.
1068 r.right--;
1069 r.bottom--;
1071 holdPen = SelectObject (hdc, hwPen);
1073 if (lStyle & TCS_BOTTOM)
1075 /* highlight */
1076 MoveToEx (hdc, r.left, r.top, NULL);
1077 LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE);
1078 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom);
1080 /* shadow */
1081 SelectObject(hdc, hbPen);
1082 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom);
1083 LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE);
1084 LineTo (hdc, r.right, r.top);
1086 else
1088 /* highlight */
1089 MoveToEx (hdc, r.left, r.bottom, NULL);
1090 LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE);
1091 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top);
1092 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top);
1094 /* shadow */
1095 SelectObject(hdc, hbPen);
1096 LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE);
1097 LineTo (hdc, r.right, r.bottom);
1102 * Text pen
1104 SelectObject(hdc, hsdPen);
1106 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1107 SetTextColor (hdc, COLOR_BTNTEXT);
1110 * Deflate the rectangle to acount for the padding
1112 InflateRect(&r, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
1115 * if owner draw, tell the owner to draw
1117 if ( (lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd) )
1119 DRAWITEMSTRUCT dis;
1120 WND *pwndPtr;
1121 UINT id;
1124 * get the control id
1126 pwndPtr = WIN_FindWndPtr( hwnd );
1127 id = pwndPtr->wIDmenu;
1128 WIN_ReleaseWndPtr(pwndPtr);
1131 * put together the DRAWITEMSTRUCT
1133 dis.CtlType = ODT_TAB;
1134 dis.CtlID = id;
1135 dis.itemID = iItem;
1136 dis.itemAction = ODA_DRAWENTIRE;
1137 if ( iItem == infoPtr->iSelected )
1138 dis.itemState = ODS_SELECTED;
1139 else
1140 dis.itemState = 0;
1141 dis.hwndItem = hwnd; /* */
1142 dis.hDC = hdc;
1143 dis.rcItem = r; /* */
1144 dis.itemData = infoPtr->items[iItem].lParam;
1147 * send the draw message
1149 SendMessageA( GetParent(hwnd), WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1151 else
1154 * If not owner draw, then do the drawing ourselves.
1156 * Draw the icon.
1158 if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE) )
1160 ImageList_Draw (infoPtr->himl, infoPtr->items[iItem].iImage, hdc,
1161 r.left, r.top+1, ILD_NORMAL);
1162 ImageList_GetIconSize (infoPtr->himl, &cx, &cy);
1163 r.left+=(cx + HORIZONTAL_ITEM_PADDING);
1167 * Draw the text;
1169 if (lStyle & TCS_RIGHTJUSTIFY)
1170 DrawTextA(hdc,
1171 infoPtr->items[iItem].pszText,
1172 lstrlenA(infoPtr->items[iItem].pszText),
1173 &r,
1174 DT_CENTER|DT_SINGLELINE|DT_VCENTER);
1175 else
1176 DrawTextA(hdc,
1177 infoPtr->items[iItem].pszText,
1178 lstrlenA(infoPtr->items[iItem].pszText),
1179 &r,
1180 DT_LEFT|DT_SINGLELINE|DT_VCENTER);
1184 * Draw the focus rectangle
1186 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
1187 (GetFocus() == hwnd) &&
1188 (iItem == infoPtr->uFocus) )
1190 InflateRect(&r, FOCUS_RECT_HOFFSET, FOCUS_RECT_VOFFSET);
1192 SelectObject(hdc, hfocusPen);
1194 MoveToEx (hdc, r.left, r.top, NULL);
1195 LineTo (hdc, r.right-1, r.top);
1196 LineTo (hdc, r.right-1, r.bottom -1);
1197 LineTo (hdc, r.left, r.bottom -1);
1198 LineTo (hdc, r.left, r.top);
1202 * Cleanup
1204 SetBkMode(hdc, oldBkMode);
1205 SelectObject(hdc, holdPen);
1206 DeleteObject(hfocusPen);
1207 if (deleteBrush) DeleteObject(hbr);
1211 /******************************************************************************
1212 * TAB_DrawBorder
1214 * This method is used to draw the raised border around the tab control
1215 * "content" area.
1217 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
1219 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1220 HPEN htmPen;
1221 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
1222 HPEN hbPen = GetSysColorPen (COLOR_3DDKSHADOW);
1223 HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW);
1224 RECT rect;
1226 GetClientRect (hwnd, &rect);
1229 * Adjust for the style
1231 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1233 rect.bottom -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
1235 else
1237 rect.top += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
1241 * Shave-off the right and bottom margins (exluded in the
1242 * rect)
1244 rect.right--;
1245 rect.bottom--;
1247 /* highlight */
1248 htmPen = SelectObject (hdc, hwPen);
1250 MoveToEx (hdc, rect.left, rect.bottom, NULL);
1251 LineTo (hdc, rect.left, rect.top);
1252 LineTo (hdc, rect.right, rect.top);
1254 /* Dark Shadow */
1255 SelectObject (hdc, hbPen);
1256 LineTo (hdc, rect.right, rect.bottom );
1257 LineTo (hdc, rect.left, rect.bottom);
1259 /* shade */
1260 SelectObject (hdc, hShade );
1261 MoveToEx (hdc, rect.right-1, rect.top, NULL);
1262 LineTo (hdc, rect.right-1, rect.bottom-1);
1263 LineTo (hdc, rect.left, rect.bottom-1);
1265 SelectObject(hdc, htmPen);
1268 /******************************************************************************
1269 * TAB_Refresh
1271 * This method repaints the tab control..
1273 static void TAB_Refresh (HWND hwnd, HDC hdc)
1275 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1276 HFONT hOldFont;
1277 INT i;
1279 if (!infoPtr->DoRedraw)
1280 return;
1282 hOldFont = SelectObject (hdc, infoPtr->hFont);
1284 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
1286 for (i = 0; i < infoPtr->uNumItem; i++)
1288 TAB_DrawItem (hwnd, hdc, i);
1291 else
1294 * Draw all the non selected item first.
1296 for (i = 0; i < infoPtr->uNumItem; i++)
1298 if (i != infoPtr->iSelected)
1299 TAB_DrawItem (hwnd, hdc, i);
1303 * Now, draw the border, draw it before the selected item
1304 * since the selected item overwrites part of the border.
1306 TAB_DrawBorder (hwnd, hdc);
1309 * Then, draw the selected item
1311 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
1314 * If we haven't set the current focus yet, set it now.
1315 * Only happens when we first paint the tab controls.
1317 if (infoPtr->uFocus == -1)
1318 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
1321 SelectObject (hdc, hOldFont);
1324 static LRESULT
1325 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
1327 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1329 infoPtr->DoRedraw=(BOOL) wParam;
1330 return 0;
1333 static LRESULT TAB_EraseBackground(
1334 HWND hwnd,
1335 HDC givenDC)
1337 HDC hdc;
1338 RECT clientRect;
1340 HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1342 hdc = givenDC ? givenDC : GetDC(hwnd);
1344 GetClientRect(hwnd, &clientRect);
1346 FillRect(hdc, &clientRect, brush);
1348 if (givenDC==0)
1349 ReleaseDC(hwnd, hdc);
1351 DeleteObject(brush);
1353 return 0;
1356 /******************************************************************************
1357 * TAB_EnsureSelectionVisible
1359 * This method will make sure that the current selection is completely
1360 * visible by scrolling until it is.
1362 static void TAB_EnsureSelectionVisible(
1363 HWND hwnd,
1364 TAB_INFO* infoPtr)
1366 INT iSelected = infoPtr->iSelected;
1369 * set the items row to the bottommost row or topmost row depending on
1370 * style
1373 if (infoPtr->uNumRows > 0)
1375 INT newselected=infoPtr->items[iSelected].rect.top;
1376 INT iTargetRow;
1377 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1379 if (lStyle & TCS_BOTTOM)
1380 iTargetRow = 0;
1381 else
1382 iTargetRow = infoPtr->uNumRows;
1384 if (newselected != iTargetRow)
1386 INT i;
1387 for (i=0; i < infoPtr->uNumItem; i++)
1388 if (infoPtr->items[i].rect.top == newselected )
1389 infoPtr->items[i].rect.top = iTargetRow;
1390 else if (lStyle&TCS_BOTTOM)
1392 if (infoPtr->items[i].rect.top < newselected)
1393 infoPtr->items[i].rect.top+=1;
1395 else
1397 if (infoPtr->items[i].rect.top > newselected)
1398 infoPtr->items[i].rect.top-=1;
1404 * Do the trivial cases first.
1406 if ( (!infoPtr->needsScrolling) ||
1407 (infoPtr->hwndUpDown==0) )
1408 return;
1410 if (infoPtr->leftmostVisible >= iSelected)
1412 infoPtr->leftmostVisible = iSelected;
1414 else
1416 RECT r;
1417 INT width, i;
1419 * Calculate the part of the client area that is visible.
1421 GetClientRect(hwnd, &r);
1422 width = r.right;
1424 GetClientRect(infoPtr->hwndUpDown, &r);
1425 width -= r.right;
1427 if ((infoPtr->items[iSelected].rect.right -
1428 infoPtr->items[iSelected].rect.left) >= width )
1430 /* Special case: width of selected item is greater than visible
1431 * part of control.
1433 infoPtr->leftmostVisible = iSelected;
1435 else
1437 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
1439 if ((infoPtr->items[iSelected].rect.right -
1440 infoPtr->items[i].rect.left) < width)
1441 break;
1443 infoPtr->leftmostVisible = i;
1447 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
1448 MAKELONG(infoPtr->leftmostVisible, 0));
1451 /******************************************************************************
1452 * TAB_InvalidateTabArea
1454 * This method will invalidate the portion of the control that contains the
1455 * tabs. It is called when the state of the control changes and needs
1456 * to be redisplayed
1458 static void TAB_InvalidateTabArea(
1459 HWND hwnd,
1460 TAB_INFO* infoPtr)
1462 RECT clientRect;
1464 GetClientRect(hwnd, &clientRect);
1466 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1468 clientRect.top = clientRect.bottom - (infoPtr->tabHeight *
1469 (infoPtr->uNumRows + 1) + 3);
1471 else
1473 clientRect.bottom = clientRect.top + (infoPtr->tabHeight *
1474 (infoPtr->uNumRows + 1) + 1);
1477 InvalidateRect(hwnd, &clientRect, TRUE);
1480 static LRESULT
1481 TAB_Paint (HWND hwnd, WPARAM wParam)
1483 HDC hdc;
1484 PAINTSTRUCT ps;
1486 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
1487 TAB_Refresh (hwnd, hdc);
1489 if(!wParam)
1490 EndPaint (hwnd, &ps);
1492 return 0;
1495 static LRESULT
1496 TAB_InsertItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1498 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1499 TCITEMA *pti;
1500 INT iItem, len;
1501 RECT rect;
1503 GetClientRect (hwnd, &rect);
1504 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
1505 rect.top, rect.left, rect.bottom, rect.right);
1507 pti = (TCITEMA *)lParam;
1508 iItem = (INT)wParam;
1510 if (iItem < 0) return -1;
1511 if (iItem > infoPtr->uNumItem)
1512 iItem = infoPtr->uNumItem;
1514 if (infoPtr->uNumItem == 0) {
1515 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
1516 infoPtr->uNumItem++;
1517 infoPtr->iSelected = 0;
1519 else {
1520 TAB_ITEM *oldItems = infoPtr->items;
1522 infoPtr->uNumItem++;
1523 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
1525 /* pre insert copy */
1526 if (iItem > 0) {
1527 memcpy (&infoPtr->items[0], &oldItems[0],
1528 iItem * sizeof(TAB_ITEM));
1531 /* post insert copy */
1532 if (iItem < infoPtr->uNumItem - 1) {
1533 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
1534 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
1538 if (iItem <= infoPtr->iSelected)
1539 infoPtr->iSelected++;
1541 COMCTL32_Free (oldItems);
1544 infoPtr->items[iItem].mask = pti->mask;
1545 if (pti->mask & TCIF_TEXT) {
1546 len = lstrlenA (pti->pszText);
1547 infoPtr->items[iItem].pszText = COMCTL32_Alloc (len+1);
1548 lstrcpyA (infoPtr->items[iItem].pszText, pti->pszText);
1549 infoPtr->items[iItem].cchTextMax = pti->cchTextMax;
1552 if (pti->mask & TCIF_IMAGE)
1553 infoPtr->items[iItem].iImage = pti->iImage;
1555 if (pti->mask & TCIF_PARAM)
1556 infoPtr->items[iItem].lParam = pti->lParam;
1558 TAB_InvalidateTabArea(hwnd, infoPtr);
1560 TRACE("[%04x]: added item %d '%s'\n",
1561 hwnd, iItem, infoPtr->items[iItem].pszText);
1563 TAB_SetItemBounds(hwnd);
1564 return iItem;
1567 static LRESULT
1568 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
1570 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1571 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1572 LONG lResult = 0;
1574 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
1576 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
1577 infoPtr->tabWidth = (INT)LOWORD(lParam);
1578 infoPtr->tabHeight = (INT)HIWORD(lParam);
1580 infoPtr->fSizeSet = TRUE;
1582 return lResult;
1585 static LRESULT
1586 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1588 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1589 TCITEMA *tabItem;
1590 TAB_ITEM *wineItem;
1591 INT iItem,len;
1593 iItem=(INT) wParam;
1594 tabItem=(LPTCITEMA ) lParam;
1595 TRACE("%d %p\n",iItem, tabItem);
1596 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
1598 wineItem=& infoPtr->items[iItem];
1600 if (tabItem->mask & TCIF_IMAGE)
1601 wineItem->iImage=tabItem->iImage;
1603 if (tabItem->mask & TCIF_PARAM)
1604 wineItem->lParam=tabItem->lParam;
1606 if (tabItem->mask & TCIF_RTLREADING)
1607 FIXME("TCIF_RTLREADING\n");
1609 if (tabItem->mask & TCIF_STATE)
1610 wineItem->dwState=tabItem->dwState;
1612 if (tabItem->mask & TCIF_TEXT) {
1613 len=lstrlenA (tabItem->pszText);
1614 if (len>wineItem->cchTextMax)
1615 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
1616 lstrcpyA (wineItem->pszText, tabItem->pszText);
1620 * Update and repaint tabs.
1622 TAB_SetItemBounds(hwnd);
1623 TAB_InvalidateTabArea(hwnd,infoPtr);
1625 return TRUE;
1628 static LRESULT
1629 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1631 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1633 return infoPtr->uNumItem;
1637 static LRESULT
1638 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1640 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1641 TCITEMA *tabItem;
1642 TAB_ITEM *wineItem;
1643 INT iItem;
1645 iItem=(INT) wParam;
1646 tabItem=(LPTCITEMA) lParam;
1647 TRACE("\n");
1648 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
1650 wineItem=& infoPtr->items[iItem];
1652 if (tabItem->mask & TCIF_IMAGE)
1653 tabItem->iImage=wineItem->iImage;
1655 if (tabItem->mask & TCIF_PARAM)
1656 tabItem->lParam=wineItem->lParam;
1658 if (tabItem->mask & TCIF_RTLREADING)
1659 FIXME("TCIF_RTLREADING\n");
1661 if (tabItem->mask & TCIF_STATE)
1662 tabItem->dwState=wineItem->dwState;
1664 if (tabItem->mask & TCIF_TEXT)
1665 lstrcpynA (tabItem->pszText, wineItem->pszText, tabItem->cchTextMax);
1667 return TRUE;
1670 static LRESULT
1671 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1673 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1674 INT iItem = (INT) wParam;
1675 BOOL bResult = FALSE;
1677 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
1679 TAB_ITEM *oldItems = infoPtr->items;
1681 infoPtr->uNumItem--;
1682 infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
1684 if (iItem > 0)
1685 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
1687 if (iItem < infoPtr->uNumItem)
1688 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
1689 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
1691 COMCTL32_Free (oldItems);
1694 * Readjust the selected index.
1696 if ((iItem == infoPtr->iSelected) && (iItem > 0))
1697 infoPtr->iSelected--;
1699 if (iItem < infoPtr->iSelected)
1700 infoPtr->iSelected--;
1702 if (infoPtr->uNumItem == 0)
1703 infoPtr->iSelected = -1;
1706 * Reposition and repaint tabs.
1708 TAB_SetItemBounds(hwnd);
1709 TAB_InvalidateTabArea(hwnd,infoPtr);
1711 bResult = TRUE;
1714 return bResult;
1717 static LRESULT
1718 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
1720 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1722 COMCTL32_Free (infoPtr->items);
1723 infoPtr->uNumItem = 0;
1724 infoPtr->iSelected = -1;
1726 TAB_SetItemBounds(hwnd);
1727 TAB_InvalidateTabArea(hwnd,infoPtr);
1728 return TRUE;
1732 static LRESULT
1733 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1735 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1737 TRACE("\n");
1738 return (LRESULT)infoPtr->hFont;
1741 static LRESULT
1742 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1745 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1747 TRACE("%x %lx\n",wParam, lParam);
1749 infoPtr->hFont = (HFONT)wParam;
1751 TAB_SetItemBounds(hwnd);
1753 TAB_InvalidateTabArea(hwnd, infoPtr);
1755 return 0;
1759 static LRESULT
1760 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1762 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1764 TRACE("\n");
1765 return (LRESULT)infoPtr->himl;
1768 static LRESULT
1769 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
1771 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1772 HIMAGELIST himlPrev;
1774 TRACE("\n");
1775 himlPrev = infoPtr->himl;
1776 infoPtr->himl= (HIMAGELIST)lParam;
1777 return (LRESULT)himlPrev;
1781 static LRESULT
1782 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1785 /* I'm not really sure what the following code was meant to do.
1786 This is what it is doing:
1787 When WM_SIZE is sent with SIZE_RESTORED, the control
1788 gets positioned in the top left corner.
1790 RECT parent_rect;
1791 HWND parent;
1792 UINT uPosFlags,cx,cy;
1794 uPosFlags=0;
1795 if (!wParam) {
1796 parent = GetParent (hwnd);
1797 GetClientRect(parent, &parent_rect);
1798 cx=LOWORD (lParam);
1799 cy=HIWORD (lParam);
1800 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
1801 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
1803 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
1804 cx, cy, uPosFlags | SWP_NOZORDER);
1805 } else {
1806 FIXME (tab,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
1807 } */
1810 * Recompute the size/position of the tabs.
1812 TAB_SetItemBounds (hwnd);
1815 * Force a repaint of the control.
1817 InvalidateRect(hwnd, NULL, TRUE);
1819 return 0;
1823 static LRESULT
1824 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
1826 TAB_INFO *infoPtr;
1827 TEXTMETRICA fontMetrics;
1828 HDC hdc;
1829 HFONT hOldFont;
1830 DWORD dwStyle;
1832 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
1834 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
1836 infoPtr->uNumItem = 0;
1837 infoPtr->uNumRows = 0;
1838 infoPtr->hFont = 0;
1839 infoPtr->items = 0;
1840 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
1841 infoPtr->iSelected = -1;
1842 infoPtr->uFocus = -1;
1843 infoPtr->hwndToolTip = 0;
1844 infoPtr->DoRedraw = TRUE;
1845 infoPtr->needsScrolling = FALSE;
1846 infoPtr->hwndUpDown = 0;
1847 infoPtr->leftmostVisible = 0;
1848 infoPtr->fSizeSet = FALSE;
1850 TRACE("Created tab control, hwnd [%04x]\n", hwnd);
1852 /* The tab control always has the WS_CLIPSIBLINGS style. Even
1853 if you don't specify in CreateWindow. This is necesary in
1854 order for paint to work correctly. This follows windows behaviour. */
1855 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1856 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
1858 if (dwStyle & TCS_TOOLTIPS) {
1859 /* Create tooltip control */
1860 infoPtr->hwndToolTip =
1861 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
1862 CW_USEDEFAULT, CW_USEDEFAULT,
1863 CW_USEDEFAULT, CW_USEDEFAULT,
1864 hwnd, 0, 0, 0);
1866 /* Send NM_TOOLTIPSCREATED notification */
1867 if (infoPtr->hwndToolTip) {
1868 NMTOOLTIPSCREATED nmttc;
1870 nmttc.hdr.hwndFrom = hwnd;
1871 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1872 nmttc.hdr.code = NM_TOOLTIPSCREATED;
1873 nmttc.hwndToolTips = infoPtr->hwndToolTip;
1875 SendMessageA (GetParent (hwnd), WM_NOTIFY,
1876 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
1881 * We need to get text information so we need a DC and we need to select
1882 * a font.
1884 hdc = GetDC(hwnd);
1885 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
1888 * Use the system font to determine the initial height of a tab.
1890 GetTextMetricsA(hdc, &fontMetrics);
1893 * Make sure there is enough space for the letters + growing the
1894 * selected item + extra space for the selected item.
1896 infoPtr->tabHeight = fontMetrics.tmHeight + 2*VERTICAL_ITEM_PADDING +
1897 SELECTED_TAB_OFFSET;
1900 * Initialize the width of a tab.
1902 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
1904 SelectObject (hdc, hOldFont);
1905 ReleaseDC(hwnd, hdc);
1907 return 0;
1910 static LRESULT
1911 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
1913 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1914 INT iItem;
1916 if (!infoPtr)
1917 return 0;
1919 if (infoPtr->items) {
1920 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
1921 if (infoPtr->items[iItem].pszText)
1922 COMCTL32_Free (infoPtr->items[iItem].pszText);
1924 COMCTL32_Free (infoPtr->items);
1927 if (infoPtr->hwndToolTip)
1928 DestroyWindow (infoPtr->hwndToolTip);
1930 if (infoPtr->hwndUpDown)
1931 DestroyWindow(infoPtr->hwndUpDown);
1933 COMCTL32_Free (infoPtr);
1934 SetWindowLongA(hwnd, 0, 0);
1935 return 0;
1938 static LRESULT WINAPI
1939 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1942 TRACE("hwnd=%x msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
1943 if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
1944 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1946 switch (uMsg)
1948 case TCM_GETIMAGELIST:
1949 return TAB_GetImageList (hwnd, wParam, lParam);
1951 case TCM_SETIMAGELIST:
1952 return TAB_SetImageList (hwnd, wParam, lParam);
1954 case TCM_GETITEMCOUNT:
1955 return TAB_GetItemCount (hwnd, wParam, lParam);
1957 case TCM_GETITEMA:
1958 return TAB_GetItemA (hwnd, wParam, lParam);
1960 case TCM_GETITEMW:
1961 FIXME("Unimplemented msg TCM_GETITEMW\n");
1962 return 0;
1964 case TCM_SETITEMA:
1965 return TAB_SetItemA (hwnd, wParam, lParam);
1967 case TCM_SETITEMW:
1968 FIXME("Unimplemented msg TCM_SETITEMW\n");
1969 return 0;
1971 case TCM_DELETEITEM:
1972 return TAB_DeleteItem (hwnd, wParam, lParam);
1974 case TCM_DELETEALLITEMS:
1975 return TAB_DeleteAllItems (hwnd, wParam, lParam);
1977 case TCM_GETITEMRECT:
1978 return TAB_GetItemRect (hwnd, wParam, lParam);
1980 case TCM_GETCURSEL:
1981 return TAB_GetCurSel (hwnd);
1983 case TCM_HITTEST:
1984 return TAB_HitTest (hwnd, wParam, lParam);
1986 case TCM_SETCURSEL:
1987 return TAB_SetCurSel (hwnd, wParam);
1989 case TCM_INSERTITEMA:
1990 return TAB_InsertItem (hwnd, wParam, lParam);
1992 case TCM_INSERTITEMW:
1993 FIXME("Unimplemented msg TCM_INSERTITEMW\n");
1994 return 0;
1996 case TCM_SETITEMEXTRA:
1997 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
1998 return 0;
2000 case TCM_ADJUSTRECT:
2001 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
2003 case TCM_SETITEMSIZE:
2004 return TAB_SetItemSize (hwnd, wParam, lParam);
2006 case TCM_REMOVEIMAGE:
2007 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
2008 return 0;
2010 case TCM_SETPADDING:
2011 FIXME("Unimplemented msg TCM_SETPADDING\n");
2012 return 0;
2014 case TCM_GETROWCOUNT:
2015 FIXME("Unimplemented msg TCM_GETROWCOUNT\n");
2016 return 0;
2018 case TCM_GETUNICODEFORMAT:
2019 FIXME("Unimplemented msg TCM_GETUNICODEFORMAT\n");
2020 return 0;
2022 case TCM_SETUNICODEFORMAT:
2023 FIXME("Unimplemented msg TCM_SETUNICODEFORMAT\n");
2024 return 0;
2026 case TCM_HIGHLIGHTITEM:
2027 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
2028 return 0;
2030 case TCM_GETTOOLTIPS:
2031 return TAB_GetToolTips (hwnd, wParam, lParam);
2033 case TCM_SETTOOLTIPS:
2034 return TAB_SetToolTips (hwnd, wParam, lParam);
2036 case TCM_GETCURFOCUS:
2037 return TAB_GetCurFocus (hwnd);
2039 case TCM_SETCURFOCUS:
2040 return TAB_SetCurFocus (hwnd, wParam);
2042 case TCM_SETMINTABWIDTH:
2043 FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
2044 return 0;
2046 case TCM_DESELECTALL:
2047 FIXME("Unimplemented msg TCM_DESELECTALL\n");
2048 return 0;
2050 case TCM_GETEXTENDEDSTYLE:
2051 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
2052 return 0;
2054 case TCM_SETEXTENDEDSTYLE:
2055 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
2056 return 0;
2058 case WM_GETFONT:
2059 return TAB_GetFont (hwnd, wParam, lParam);
2061 case WM_SETFONT:
2062 return TAB_SetFont (hwnd, wParam, lParam);
2064 case WM_CREATE:
2065 return TAB_Create (hwnd, wParam, lParam);
2067 case WM_NCDESTROY:
2068 return TAB_Destroy (hwnd, wParam, lParam);
2070 case WM_GETDLGCODE:
2071 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2073 case WM_LBUTTONDOWN:
2074 return TAB_LButtonDown (hwnd, wParam, lParam);
2076 case WM_LBUTTONUP:
2077 return TAB_LButtonUp (hwnd, wParam, lParam);
2079 case WM_RBUTTONDOWN:
2080 return TAB_RButtonDown (hwnd, wParam, lParam);
2082 case WM_MOUSEMOVE:
2083 return TAB_MouseMove (hwnd, wParam, lParam);
2085 case WM_ERASEBKGND:
2086 return TAB_EraseBackground (hwnd, (HDC)wParam);
2088 case WM_PAINT:
2089 return TAB_Paint (hwnd, wParam);
2091 case WM_SIZE:
2092 return TAB_Size (hwnd, wParam, lParam);
2094 case WM_SETREDRAW:
2095 return TAB_SetRedraw (hwnd, wParam);
2097 case WM_HSCROLL:
2098 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
2100 case WM_STYLECHANGED:
2101 TAB_SetItemBounds (hwnd);
2102 InvalidateRect(hwnd, NULL, TRUE);
2103 return 0;
2105 case WM_KILLFOCUS:
2106 case WM_SETFOCUS:
2107 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
2109 case WM_KEYUP:
2110 return TAB_KeyUp(hwnd, wParam);
2112 default:
2113 if (uMsg >= WM_USER)
2114 ERR("unknown msg %04x wp=%08x lp=%08lx\n",
2115 uMsg, wParam, lParam);
2116 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
2119 return 0;
2123 VOID
2124 TAB_Register (void)
2126 WNDCLASSA wndClass;
2128 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
2129 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
2130 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
2131 wndClass.cbClsExtra = 0;
2132 wndClass.cbWndExtra = sizeof(TAB_INFO *);
2133 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
2134 wndClass.hbrBackground = (HBRUSH)NULL;
2135 wndClass.lpszClassName = WC_TABCONTROLA;
2137 RegisterClassA (&wndClass);
2141 VOID
2142 TAB_Unregister (void)
2144 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);