MSDN: "WM_NCHITTEST - Returns HTCLIENT if the control style is
[wine.git] / dlls / comctl32 / tab.c
blob7c4f44ce4703d28b6b98e6d608e18d175864c127
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 * Hot-tracking timer constants
46 #define TAB_HOTTRACK_TIMER 1
47 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
49 /******************************************************************************
50 * Prototypes
52 static void TAB_Refresh (HWND hwnd, HDC hdc);
53 static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
54 static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
55 static void TAB_DrawItem(HWND hwnd, HDC hdc, INT iItem);
56 static void TAB_DrawItemInterior(HWND hwnd, HDC hdc, INT iItem, RECT* drawRect);
58 static BOOL
59 TAB_SendSimpleNotify (HWND hwnd, UINT code)
61 NMHDR nmhdr;
63 nmhdr.hwndFrom = hwnd;
64 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
65 nmhdr.code = code;
67 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
68 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
72 static VOID
73 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
74 WPARAM wParam, LPARAM lParam)
76 MSG msg;
78 msg.hwnd = hwndMsg;
79 msg.message = uMsg;
80 msg.wParam = wParam;
81 msg.lParam = lParam;
82 msg.time = GetMessageTime ();
83 msg.pt.x = LOWORD(GetMessagePos ());
84 msg.pt.y = HIWORD(GetMessagePos ());
86 SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
91 static LRESULT
92 TAB_GetCurSel (HWND hwnd)
94 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
96 return infoPtr->iSelected;
99 static LRESULT
100 TAB_GetCurFocus (HWND hwnd)
102 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
104 return infoPtr->uFocus;
107 static LRESULT
108 TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
110 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
112 if (infoPtr == NULL) return 0;
113 return infoPtr->hwndToolTip;
117 static LRESULT
118 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
120 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
121 INT iItem=(INT) wParam;
122 INT prevItem;
124 prevItem=-1;
125 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
126 prevItem=infoPtr->iSelected;
127 infoPtr->iSelected=iItem;
128 TAB_EnsureSelectionVisible(hwnd, infoPtr);
129 TAB_InvalidateTabArea(hwnd, infoPtr);
131 return prevItem;
134 static LRESULT
135 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
137 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
138 INT iItem=(INT) wParam;
140 if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
142 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
143 FIXME("Should set input focus\n");
144 } else {
145 if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) {
146 infoPtr->uFocus=iItem;
147 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
148 infoPtr->iSelected = iItem;
149 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
151 TAB_EnsureSelectionVisible(hwnd, infoPtr);
152 TAB_InvalidateTabArea(hwnd, infoPtr);
156 return 0;
159 static LRESULT
160 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
162 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
164 if (infoPtr == NULL) return 0;
165 infoPtr->hwndToolTip = (HWND)wParam;
166 return 0;
169 /******************************************************************************
170 * TAB_InternalGetItemRect
172 * This method will calculate the rectangle representing a given tab item in
173 * client coordinates. This method takes scrolling into account.
175 * This method returns TRUE if the item is visible in the window and FALSE
176 * if it is completely outside the client area.
178 static BOOL TAB_InternalGetItemRect(
179 HWND hwnd,
180 TAB_INFO* infoPtr,
181 INT itemIndex,
182 RECT* itemRect,
183 RECT* selectedRect)
185 RECT tmpItemRect,clientRect;
186 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
189 * Perform a sanity check and a trivial visibility check.
191 if ( (infoPtr->uNumItem <=0) ||
192 (itemIndex >= infoPtr->uNumItem) ||
193 (!(lStyle &TCS_MULTILINE) && (itemIndex < infoPtr->leftmostVisible)) )
194 return FALSE;
197 * Avoid special cases in this procedure by assigning the "out"
198 * parameters if the caller didn't supply them
200 if (itemRect==NULL)
201 itemRect = &tmpItemRect;
204 * Retrieve the unmodified item rect.
206 *itemRect = infoPtr->items[itemIndex].rect;
209 * calculate the times bottom and top based on the row
211 GetClientRect(hwnd, &clientRect);
213 if (lStyle & TCS_BOTTOM)
215 itemRect->bottom = clientRect.bottom -
216 SELECTED_TAB_OFFSET -
217 itemRect->top * (infoPtr->tabHeight - 2);
219 itemRect->top = clientRect.bottom -
220 infoPtr->tabHeight -
221 itemRect->top * ( infoPtr->tabHeight - 2);
223 else
225 itemRect->bottom = clientRect.top +
226 infoPtr->tabHeight +
227 itemRect->top * (infoPtr->tabHeight - 2);
228 itemRect->top = clientRect.top +
229 SELECTED_TAB_OFFSET+
230 itemRect->top * (infoPtr->tabHeight - 2);
234 * "scroll" it to make sure the item at the very left of the
235 * tab control is the leftmost visible tab.
237 OffsetRect(itemRect,
238 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
242 * Move the rectangle so the first item is slightly offset from
243 * the left of the tab control.
245 OffsetRect(itemRect,
246 SELECTED_TAB_OFFSET,
251 * Now, calculate the position of the item as if it were selected.
253 if (selectedRect!=NULL)
255 CopyRect(selectedRect, itemRect);
258 * The rectangle of a selected item is a bit wider.
260 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
263 * If it also a bit higher.
265 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
267 selectedRect->top -=2; /* the border is thicker on the bottom */
268 selectedRect->bottom +=SELECTED_TAB_OFFSET;
270 else
272 selectedRect->top -=SELECTED_TAB_OFFSET;
273 selectedRect->bottom+=1;
277 return TRUE;
280 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
282 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
283 (LPRECT)lParam, (LPRECT)NULL);
286 /******************************************************************************
287 * TAB_KeyUp
289 * This method is called to handle keyboard input
291 static LRESULT TAB_KeyUp(
292 HWND hwnd,
293 WPARAM keyCode)
295 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
296 int newItem = -1;
298 switch (keyCode)
300 case VK_LEFT:
301 newItem = infoPtr->uFocus-1;
302 break;
303 case VK_RIGHT:
304 newItem = infoPtr->uFocus+1;
305 break;
309 * If we changed to a valid item, change the selection
311 if ( (newItem >= 0) &&
312 (newItem < infoPtr->uNumItem) &&
313 (infoPtr->uFocus != newItem) )
315 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
317 infoPtr->iSelected = newItem;
318 infoPtr->uFocus = newItem;
319 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
321 TAB_EnsureSelectionVisible(hwnd, infoPtr);
322 TAB_InvalidateTabArea(hwnd, infoPtr);
326 return 0;
329 /******************************************************************************
330 * TAB_FocusChanging
332 * This method is called whenever the focus goes in or out of this control
333 * it is used to update the visual state of the control.
335 static LRESULT TAB_FocusChanging(
336 HWND hwnd,
337 UINT uMsg,
338 WPARAM wParam,
339 LPARAM lParam)
341 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
342 RECT selectedRect;
343 BOOL isVisible;
346 * Get the rectangle for the item.
348 isVisible = TAB_InternalGetItemRect(hwnd,
349 infoPtr,
350 infoPtr->uFocus,
351 NULL,
352 &selectedRect);
355 * If the rectangle is not completely invisible, invalidate that
356 * portion of the window.
358 if (isVisible)
360 InvalidateRect(hwnd, &selectedRect, TRUE);
364 * Don't otherwise disturb normal behavior.
366 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
369 static HWND TAB_InternalHitTest (
370 HWND hwnd,
371 TAB_INFO* infoPtr,
372 POINT pt,
373 UINT* flags)
376 RECT rect;
377 int iCount;
379 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
381 TAB_InternalGetItemRect(hwnd,
382 infoPtr,
383 iCount,
384 &rect,
385 NULL);
387 if (PtInRect (&rect, pt))
389 *flags = TCHT_ONITEM;
390 return iCount;
394 *flags=TCHT_NOWHERE;
395 return -1;
398 static LRESULT
399 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
401 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
402 LPTCHITTESTINFO lptest=(LPTCHITTESTINFO) lParam;
404 return TAB_InternalHitTest (hwnd, infoPtr,lptest->pt,&lptest->flags);
408 static LRESULT
409 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
411 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
412 POINT pt;
413 INT newItem,dummy;
415 if (infoPtr->hwndToolTip)
416 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
417 WM_LBUTTONDOWN, wParam, lParam);
419 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
420 SetFocus (hwnd);
423 if (infoPtr->hwndToolTip)
424 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
425 WM_LBUTTONDOWN, wParam, lParam);
427 pt.x = (INT)LOWORD(lParam);
428 pt.y = (INT)HIWORD(lParam);
430 newItem=TAB_InternalHitTest (hwnd, infoPtr,pt,&dummy);
432 TRACE("On Tab, item %d\n", newItem);
434 if ( (newItem!=-1) &&
435 (infoPtr->iSelected != newItem) )
437 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE)
439 infoPtr->iSelected = newItem;
440 infoPtr->uFocus = newItem;
441 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
443 TAB_EnsureSelectionVisible(hwnd, infoPtr);
445 TAB_InvalidateTabArea(hwnd, infoPtr);
448 return 0;
451 static LRESULT
452 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
454 TAB_SendSimpleNotify(hwnd, NM_CLICK);
456 return 0;
459 static LRESULT
460 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
462 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
463 return 0;
466 /******************************************************************************
467 * TAB_DrawLoneItemInterior
469 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
470 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
471 * up the device context and font. This routine does the same setup but
472 * only calls TAB_DrawItemInterior for the single specified item.
474 static void
475 TAB_DrawLoneItemInterior(HWND hwnd, TAB_INFO* infoPtr, int iItem)
477 HDC hdc = GetDC(hwnd);
478 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
479 TAB_DrawItemInterior(hwnd, hdc, iItem, NULL);
480 SelectObject(hdc, hOldFont);
481 ReleaseDC(hwnd, hdc);
484 /******************************************************************************
485 * TAB_HotTrackTimerProc
487 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
488 * timer is setup so we can check if the mouse is moved out of our window.
489 * (We don't get an event when the mouse leaves, the mouse-move events just
490 * stop being delivered to our window and just start being delivered to
491 * another window.) This function is called when the timer triggers so
492 * we can check if the mouse has left our window. If so, we un-highlight
493 * the hot-tracked tab.
495 static VOID CALLBACK
496 TAB_HotTrackTimerProc
498 HWND hwnd, /* handle of window for timer messages */
499 UINT uMsg, /* WM_TIMER message */
500 UINT idEvent, /* timer identifier */
501 DWORD dwTime /* current system time */
504 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
506 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
508 POINT pt;
511 ** If we can't get the cursor position, or if the cursor is outside our
512 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
513 ** "outside" even if it is within our bounding rect if another window
514 ** overlaps. Note also that the case where the cursor stayed within our
515 ** window but has moved off the hot-tracked tab will be handled by the
516 ** WM_MOUSEMOVE event.
518 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
520 /* Redraw iHotTracked to look normal */
521 INT iRedraw = infoPtr->iHotTracked;
522 infoPtr->iHotTracked = -1;
523 TAB_DrawLoneItemInterior(hwnd, infoPtr, iRedraw);
525 /* Kill this timer */
526 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
531 /******************************************************************************
532 * TAB_RecalcHotTrack
534 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
535 * should be highlighted. This function determines which tab in a tab control,
536 * if any, is under the mouse and records that information. The caller may
537 * supply output parameters to receive the item number of the tab item which
538 * was highlighted but isn't any longer and of the tab item which is now
539 * highlighted but wasn't previously. The caller can use this information to
540 * selectively redraw those tab items.
542 * If the caller has a mouse position, it can supply it through the pos
543 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
544 * supplies NULL and this function determines the current mouse position
545 * itself.
547 static void
548 TAB_RecalcHotTrack
550 HWND hwnd,
551 const LPARAM* pos,
552 int* out_redrawLeave,
553 int* out_redrawEnter
556 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
558 int item = -1;
561 if (out_redrawLeave != NULL)
562 *out_redrawLeave = -1;
563 if (out_redrawEnter != NULL)
564 *out_redrawEnter = -1;
566 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_HOTTRACK)
568 POINT pt;
569 UINT flags;
571 if (pos == NULL)
573 GetCursorPos(&pt);
574 ScreenToClient(hwnd, &pt);
576 else
578 pt.x = LOWORD(*pos);
579 pt.y = HIWORD(*pos);
582 item = TAB_InternalHitTest(hwnd, infoPtr, pt, &flags);
585 if (item != infoPtr->iHotTracked)
587 if (infoPtr->iHotTracked >= 0)
589 /* Mark currently hot-tracked to be redrawn to look normal */
590 if (out_redrawLeave != NULL)
591 *out_redrawLeave = infoPtr->iHotTracked;
593 if (item < 0)
595 /* Kill timer which forces recheck of mouse pos */
596 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
599 else
601 /* Start timer so we recheck mouse pos */
602 UINT timerID = SetTimer
604 hwnd,
605 TAB_HOTTRACK_TIMER,
606 TAB_HOTTRACK_TIMER_INTERVAL,
607 TAB_HotTrackTimerProc
610 if (timerID == 0)
611 return; /* Hot tracking not available */
614 infoPtr->iHotTracked = item;
616 if (item >= 0)
618 /* Mark new hot-tracked to be redrawn to look highlighted */
619 if (out_redrawEnter != NULL)
620 *out_redrawEnter = item;
625 /******************************************************************************
626 * TAB_MouseMove
628 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
630 static LRESULT
631 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
633 int redrawLeave;
634 int redrawEnter;
636 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
638 if (infoPtr->hwndToolTip)
639 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
640 WM_LBUTTONDOWN, wParam, lParam);
642 /* Determine which tab to highlight. Redraw tabs which change highlight
643 ** status. */
644 TAB_RecalcHotTrack(hwnd, &lParam, &redrawLeave, &redrawEnter);
646 if (redrawLeave != -1)
647 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawLeave);
648 if (redrawEnter != -1)
649 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawEnter);
651 return 0;
654 /******************************************************************************
655 * TAB_AdjustRect
657 * Calculates the tab control's display area given the windows rectangle or
658 * the window rectangle given the requested display rectangle.
660 static LRESULT TAB_AdjustRect(
661 HWND hwnd,
662 WPARAM fLarger,
663 LPRECT prc)
665 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
667 if (fLarger)
670 * Go from display rectangle
674 * Add the height of the tabs.
676 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
677 prc->bottom += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
678 else
679 prc->top -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
682 * Inflate the rectangle for the padding
684 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
687 * Inflate for the border
689 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
691 else
694 * Go from window rectangle.
698 * Deflate the rectangle for the border
700 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
703 * Deflate the rectangle for the padding
705 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
708 * Remove the height of the tabs.
710 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
711 prc->bottom -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
712 else
713 prc->top += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
717 return 0;
720 /******************************************************************************
721 * TAB_OnHScroll
723 * This method will handle the notification from the scroll control and
724 * perform the scrolling operation on the tab control.
726 static LRESULT TAB_OnHScroll(
727 HWND hwnd,
728 int nScrollCode,
729 int nPos,
730 HWND hwndScroll)
732 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
734 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
736 if(nPos < infoPtr->leftmostVisible)
737 infoPtr->leftmostVisible--;
738 else
739 infoPtr->leftmostVisible++;
741 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
742 TAB_InvalidateTabArea(hwnd, infoPtr);
743 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
744 MAKELONG(infoPtr->leftmostVisible, 0));
747 return 0;
750 /******************************************************************************
751 * TAB_SetupScroling
753 * This method will check the current scrolling state and make sure the
754 * scrolling control is displayed (or not).
756 static void TAB_SetupScrolling(
757 HWND hwnd,
758 TAB_INFO* infoPtr,
759 const RECT* clientRect)
761 INT maxRange = 0;
762 if (infoPtr->needsScrolling)
764 RECT controlPos;
765 INT vsize, tabwidth;
768 * Calculate the position of the scroll control.
770 controlPos.right = clientRect->right;
771 controlPos.left = controlPos.right - 2*GetSystemMetrics(SM_CXHSCROLL);
773 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
775 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
776 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
778 else
780 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
781 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
785 * If we don't have a scroll control yet, we want to create one.
786 * If we have one, we want to make sure it's positioned right.
788 if (infoPtr->hwndUpDown==0)
791 * I use a scrollbar since it seems to be more stable than the Updown
792 * control.
794 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
796 WS_VISIBLE | WS_CHILD | UDS_HORZ,
797 controlPos.left, controlPos.top,
798 controlPos.right - controlPos.left,
799 controlPos.bottom - controlPos.top,
800 hwnd,
801 (HMENU)NULL,
802 (HINSTANCE)NULL,
803 NULL);
805 else
807 SetWindowPos(infoPtr->hwndUpDown,
808 (HWND)NULL,
809 controlPos.left, controlPos.top,
810 controlPos.right - controlPos.left,
811 controlPos.bottom - controlPos.top,
812 SWP_SHOWWINDOW | SWP_NOZORDER);
815 /* Now calculate upper limit of the updown control range.
816 * We do this by calculating how many tabs will be offscreen when the
817 * last tab is visible.
819 if(infoPtr->uNumItem)
821 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
822 maxRange = infoPtr->uNumItem;
823 tabwidth = infoPtr->items[maxRange-1].rect.right;
825 for(; maxRange > 0; maxRange--)
827 if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
828 break;
831 if(maxRange == infoPtr->uNumItem)
832 maxRange--;
835 else
838 * If we once had a scroll control... hide it.
840 if (infoPtr->hwndUpDown!=0)
842 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
845 if (infoPtr->hwndUpDown)
846 SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
849 /******************************************************************************
850 * TAB_SetItemBounds
852 * This method will calculate the position rectangles of all the items in the
853 * control. The rectangle calculated starts at 0 for the first item in the
854 * list and ignores scrolling and selection.
855 * It also uses the current font to determine the height of the tab row and
856 * it checks if all the tabs fit in the client area of the window. If they
857 * dont, a scrolling control is added.
859 static void TAB_SetItemBounds (HWND hwnd)
861 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
862 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
863 TEXTMETRICA fontMetrics;
864 INT curItem;
865 INT curItemLeftPos;
866 INT curItemRowCount;
867 HFONT hFont, hOldFont;
868 HDC hdc;
869 RECT clientRect;
870 SIZE size;
873 * We need to get text information so we need a DC and we need to select
874 * a font.
876 hdc = GetDC(hwnd);
878 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
879 hOldFont = SelectObject (hdc, hFont);
882 * We will base the rectangle calculations on the client rectangle
883 * of the control.
885 GetClientRect(hwnd, &clientRect);
888 * The leftmost item will be "0" aligned
890 curItemLeftPos = 0;
891 curItemRowCount = 0;
893 if ( !(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet) )
895 int item_height;
896 int icon_height = 0;
899 * Use the current font to determine the height of a tab.
901 GetTextMetricsA(hdc, &fontMetrics);
904 * Get the icon height
906 if (infoPtr->himl)
907 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
910 * Take the highest between font or icon
912 if (fontMetrics.tmHeight > icon_height)
913 item_height = fontMetrics.tmHeight;
914 else
915 item_height = icon_height;
918 * Make sure there is enough space for the letters + icon + growing the
919 * selected item + extra space for the selected item.
921 infoPtr->tabHeight = item_height + 2*VERTICAL_ITEM_PADDING +
922 SELECTED_TAB_OFFSET;
925 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
928 * Set the leftmost position of the tab.
930 infoPtr->items[curItem].rect.left = curItemLeftPos;
932 if ( (lStyle & TCS_FIXEDWIDTH) || ((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
934 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
935 infoPtr->tabWidth +
936 2*HORIZONTAL_ITEM_PADDING;
938 else
940 int icon_width = 0;
941 int num = 2;
944 * Calculate how wide the tab is depending on the text it contains
946 GetTextExtentPoint32A(hdc, infoPtr->items[curItem].pszText,
947 lstrlenA(infoPtr->items[curItem].pszText), &size);
950 * Add the icon width
952 if (infoPtr->himl)
954 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
955 num++;
958 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
959 size.cx + icon_width +
960 num*HORIZONTAL_ITEM_PADDING;
964 * Check if this is a multiline tab control and if so
965 * check to see if we should wrap the tabs
967 * Because we are going to arange all these tabs evenly
968 * really we are basically just counting rows at this point
972 if ((lStyle & TCS_MULTILINE)&&
973 (infoPtr->items[curItem].rect.right > clientRect.right))
975 infoPtr->items[curItem].rect.right -=
976 infoPtr->items[curItem].rect.left;
977 infoPtr->items[curItem].rect.left = 0;
978 curItemRowCount ++;
981 infoPtr->items[curItem].rect.bottom = 0;
982 infoPtr->items[curItem].rect.top = curItemRowCount;
984 TRACE("TextSize: %i\n ", size.cx);
985 TRACE("Rect: T %i, L %i, B %i, R %i\n",
986 infoPtr->items[curItem].rect.top,
987 infoPtr->items[curItem].rect.left,
988 infoPtr->items[curItem].rect.bottom,
989 infoPtr->items[curItem].rect.right);
992 * The leftmost position of the next item is the rightmost position
993 * of this one.
995 if (lStyle & TCS_BUTTONS)
996 curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX;
997 else
998 curItemLeftPos = infoPtr->items[curItem].rect.right;
1001 if (!(lStyle & TCS_MULTILINE))
1004 * Check if we need a scrolling control.
1006 infoPtr->needsScrolling = (curItemLeftPos + (2*SELECTED_TAB_OFFSET) >
1007 clientRect.right);
1009 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1010 if(!infoPtr->needsScrolling)
1011 infoPtr->leftmostVisible = 0;
1013 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
1017 * Set the number of rows
1020 infoPtr->uNumRows = curItemRowCount;
1022 if ((lStyle & TCS_MULTILINE)&&(infoPtr->uNumItem > 0))
1024 INT widthDiff,remainder;
1025 INT tabPerRow,remTab;
1026 INT iRow,iItm;
1027 INT iIndexStart=0,iIndexEnd=0, iCount=0;
1030 * Ok Microsoft trys to even out the rows. place the same
1031 * number of tabs in each row. So lets give that a shot
1035 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows + 1);
1036 remTab = infoPtr->uNumItem % (infoPtr->uNumRows + 1);
1038 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1039 iItm<infoPtr->uNumItem;
1040 iItm++,iCount++)
1042 if (iCount >= ((iRow<remTab)?tabPerRow+1:tabPerRow))
1044 iRow++;
1045 curItemLeftPos = 0;
1046 iCount = 0;
1049 * normalize the current rect
1051 infoPtr->items[iItm].rect.right -=
1052 infoPtr->items[iItm].rect.left;
1053 infoPtr->items[iItm].rect.left = 0;
1055 infoPtr->items[iItm].rect.top = iRow;
1056 infoPtr->items[iItm].rect.left +=curItemLeftPos;
1057 infoPtr->items[iItm].rect.right +=curItemLeftPos;
1058 if (lStyle & TCS_BUTTONS)
1059 curItemLeftPos = infoPtr->items[iItm].rect.right +
1060 BUTTON_SPACINGX;
1061 else
1062 curItemLeftPos = infoPtr->items[iItm].rect.right;
1066 * Justify the rows
1070 while(iIndexStart < infoPtr->uNumItem)
1073 * find the indexs of the row
1075 for (iIndexEnd=iIndexStart;
1076 (iIndexEnd < infoPtr->uNumItem) &&
1077 (infoPtr->items[iIndexEnd].rect.top ==
1078 infoPtr->items[iIndexStart].rect.top) ;
1079 iIndexEnd++)
1080 /* intentionaly blank */;
1083 * we need to justify these tabs so they fill the whole given
1084 * client area
1087 widthDiff = clientRect.right - (2*SELECTED_TAB_OFFSET) -
1088 infoPtr->items[iIndexEnd-1].rect.right;
1090 iCount = iIndexEnd-iIndexStart;
1092 if (iCount>1)
1094 INT iIndex;
1095 remainder = widthDiff % iCount;
1096 widthDiff = widthDiff / iCount;
1097 for (iIndex=iIndexStart,iCount=0; iIndex < iIndexEnd;
1098 iIndex++,iCount++)
1100 infoPtr->items[iIndex].rect.left +=iCount*widthDiff;
1101 infoPtr->items[iIndex].rect.right +=(iCount+1)*widthDiff;
1103 infoPtr->items[iIndex-1].rect.right += remainder;
1106 iIndexStart=iIndexEnd;
1111 TAB_EnsureSelectionVisible(hwnd,infoPtr);
1112 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1114 * Cleanup
1116 SelectObject (hdc, hOldFont);
1117 ReleaseDC (hwnd, hdc);
1120 /******************************************************************************
1121 * TAB_DrawItemInterior
1123 * This method is used to draw the interior (text and icon) of a single tab
1124 * into the tab control.
1126 static void
1127 TAB_DrawItemInterior
1129 HWND hwnd,
1130 HDC hdc,
1131 INT iItem,
1132 RECT* drawRect
1135 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1136 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1138 RECT localRect;
1140 HPEN htextPen = GetSysColorPen (COLOR_BTNTEXT);
1141 HPEN holdPen;
1142 INT oldBkMode;
1144 if (drawRect == NULL)
1146 BOOL isVisible;
1147 RECT itemRect;
1148 RECT selectedRect;
1151 * Get the rectangle for the item.
1153 isVisible = TAB_InternalGetItemRect
1155 hwnd,
1156 infoPtr,
1157 iItem,
1158 &itemRect,
1159 &selectedRect
1161 if (!isVisible)
1162 return;
1165 * Make sure drawRect points to something valid; simplifies code.
1167 drawRect = &localRect;
1170 * This logic copied from the part of TAB_DrawItem which draws
1171 * the tab background. It's important to keep it in sync. I
1172 * would have liked to avoid code duplication, but couldn't figure
1173 * out how without making spaghetti of TAB_DrawItem.
1175 if (lStyle & TCS_BUTTONS)
1177 *drawRect = itemRect;
1178 if (iItem == infoPtr->iSelected)
1180 drawRect->right--;
1181 drawRect->bottom--;
1184 else
1186 if (iItem == infoPtr->iSelected)
1187 *drawRect = selectedRect;
1188 else
1189 *drawRect = itemRect;
1190 drawRect->right--;
1191 drawRect->bottom--;
1196 * Text pen
1198 holdPen = SelectObject(hdc, htextPen);
1200 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1201 SetTextColor
1203 hdc,
1204 GetSysColor
1206 (iItem == infoPtr->iHotTracked) ? COLOR_HIGHLIGHT : COLOR_BTNTEXT
1211 * Deflate the rectangle to acount for the padding
1213 InflateRect(drawRect, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
1216 * if owner draw, tell the owner to draw
1218 if ( (lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd) )
1220 DRAWITEMSTRUCT dis;
1221 WND *pwndPtr;
1222 UINT id;
1225 * get the control id
1227 pwndPtr = WIN_FindWndPtr( hwnd );
1228 id = pwndPtr->wIDmenu;
1229 WIN_ReleaseWndPtr(pwndPtr);
1232 * put together the DRAWITEMSTRUCT
1234 dis.CtlType = ODT_TAB;
1235 dis.CtlID = id;
1236 dis.itemID = iItem;
1237 dis.itemAction = ODA_DRAWENTIRE;
1238 if ( iItem == infoPtr->iSelected )
1239 dis.itemState = ODS_SELECTED;
1240 else
1241 dis.itemState = 0;
1242 dis.hwndItem = hwnd; /* */
1243 dis.hDC = hdc;
1244 dis.rcItem = *drawRect; /* */
1245 dis.itemData = infoPtr->items[iItem].lParam;
1248 * send the draw message
1250 SendMessageA( GetParent(hwnd), WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1252 else
1254 UINT uHorizAlign;
1257 * If not owner draw, then do the drawing ourselves.
1259 * Draw the icon.
1261 if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE))
1263 INT cx;
1264 INT cy;
1266 ImageList_Draw
1268 infoPtr->himl,
1269 infoPtr->items[iItem].iImage,
1270 hdc,
1271 drawRect->left,
1272 drawRect->top + 1,
1273 ILD_NORMAL
1275 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1276 drawRect->left += (cx + HORIZONTAL_ITEM_PADDING);
1280 * Draw the text;
1282 if (lStyle & TCS_RIGHTJUSTIFY)
1283 uHorizAlign = DT_CENTER;
1284 else
1285 uHorizAlign = DT_LEFT;
1287 DrawTextA
1289 hdc,
1290 infoPtr->items[iItem].pszText,
1291 lstrlenA(infoPtr->items[iItem].pszText),
1292 drawRect,
1293 uHorizAlign | DT_SINGLELINE | DT_VCENTER
1298 * Cleanup
1300 SetBkMode(hdc, oldBkMode);
1301 SelectObject(hdc, holdPen);
1304 /******************************************************************************
1305 * TAB_DrawItem
1307 * This method is used to draw a single tab into the tab control.
1309 static void TAB_DrawItem(
1310 HWND hwnd,
1311 HDC hdc,
1312 INT iItem)
1314 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1315 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1316 RECT itemRect;
1317 RECT selectedRect;
1318 BOOL isVisible;
1319 RECT r;
1322 * Get the rectangle for the item.
1324 isVisible = TAB_InternalGetItemRect(hwnd,
1325 infoPtr,
1326 iItem,
1327 &itemRect,
1328 &selectedRect);
1330 if (isVisible)
1332 HBRUSH hbr = CreateSolidBrush (GetSysColor(COLOR_BTNFACE));
1333 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
1334 HPEN hbPen = GetSysColorPen (COLOR_BTNSHADOW);
1335 HPEN hfocusPen = CreatePen(PS_DOT, 1, GetSysColor(COLOR_BTNTEXT));
1336 HPEN holdPen;
1337 INT oldBkMode;
1338 BOOL deleteBrush = TRUE;
1340 if (lStyle & TCS_BUTTONS)
1343 * Get item rectangle.
1345 r = itemRect;
1347 holdPen = SelectObject (hdc, hwPen);
1349 if (iItem == infoPtr->iSelected)
1352 * Background color.
1354 if (!((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
1356 COLORREF bk = GetSysColor(COLOR_3DHILIGHT);
1357 DeleteObject(hbr);
1358 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1359 SetTextColor(hdc, GetSysColor(COLOR_3DFACE));
1360 SetBkColor(hdc, bk);
1362 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1363 * we better use 0x55aa bitmap brush to make scrollbar's background
1364 * look different from the window background.
1366 if (bk == GetSysColor(COLOR_WINDOW))
1367 hbr = CACHE_GetPattern55AABrush();
1369 deleteBrush = FALSE;
1373 * Erase the background.
1375 FillRect(hdc, &r, hbr);
1378 * Draw the tab now.
1379 * The rectangles calculated exclude the right and bottom
1380 * borders of the rectangle. To simply the following code, those
1381 * borders are shaved-off beforehand.
1383 r.right--;
1384 r.bottom--;
1386 /* highlight */
1387 MoveToEx (hdc, r.left, r.bottom, NULL);
1388 LineTo (hdc, r.right, r.bottom);
1389 LineTo (hdc, r.right, r.top);
1391 /* shadow */
1392 SelectObject(hdc, hbPen);
1393 LineTo (hdc, r.left, r.top);
1394 LineTo (hdc, r.left, r.bottom);
1396 else
1399 * Erase the background.
1401 FillRect(hdc, &r, hbr);
1403 /* highlight */
1404 MoveToEx (hdc, r.left, r.bottom, NULL);
1405 LineTo (hdc, r.left, r.top);
1406 LineTo (hdc, r.right, r.top);
1408 /* shadow */
1409 SelectObject(hdc, hbPen);
1410 LineTo (hdc, r.right, r.bottom);
1411 LineTo (hdc, r.left, r.bottom);
1414 else
1417 * Background color.
1419 DeleteObject(hbr);
1420 hbr = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1423 * We draw a rectangle of different sizes depending on the selection
1424 * state.
1426 if (iItem == infoPtr->iSelected)
1427 r = selectedRect;
1428 else
1429 r = itemRect;
1432 * Erase the background.
1433 * This is necessary when drawing the selected item since it is larger
1434 * than the others, it might overlap with stuff already drawn by the
1435 * other tabs
1437 FillRect(hdc, &r, hbr);
1440 * Draw the tab now.
1441 * The rectangles calculated exclude the right and bottom
1442 * borders of the rectangle. To simply the following code, those
1443 * borders are shaved-off beforehand.
1445 r.right--;
1446 r.bottom--;
1448 holdPen = SelectObject (hdc, hwPen);
1450 if (lStyle & TCS_BOTTOM)
1452 /* highlight */
1453 MoveToEx (hdc, r.left, r.top, NULL);
1454 LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE);
1455 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom);
1457 /* shadow */
1458 SelectObject(hdc, hbPen);
1459 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom);
1460 LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE);
1461 LineTo (hdc, r.right, r.top);
1463 else
1465 /* highlight */
1466 MoveToEx (hdc, r.left, r.bottom, NULL);
1467 LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE);
1468 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top);
1469 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top);
1471 /* shadow */
1472 SelectObject(hdc, hbPen);
1473 LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE);
1474 LineTo (hdc, r.right, r.bottom);
1478 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1480 /* This modifies r to be the text rectangle. */
1481 TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
1484 * Draw the focus rectangle
1486 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
1487 (GetFocus() == hwnd) &&
1488 (iItem == infoPtr->uFocus) )
1490 InflateRect(&r, FOCUS_RECT_HOFFSET, FOCUS_RECT_VOFFSET);
1492 SelectObject(hdc, hfocusPen);
1494 MoveToEx (hdc, r.left, r.top, NULL);
1495 LineTo (hdc, r.right-1, r.top);
1496 LineTo (hdc, r.right-1, r.bottom -1);
1497 LineTo (hdc, r.left, r.bottom -1);
1498 LineTo (hdc, r.left, r.top);
1502 * Cleanup
1504 SetBkMode(hdc, oldBkMode);
1505 SelectObject(hdc, holdPen);
1506 DeleteObject(hfocusPen);
1507 if (deleteBrush) DeleteObject(hbr);
1511 /******************************************************************************
1512 * TAB_DrawBorder
1514 * This method is used to draw the raised border around the tab control
1515 * "content" area.
1517 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
1519 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1520 HPEN htmPen;
1521 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
1522 HPEN hbPen = GetSysColorPen (COLOR_3DDKSHADOW);
1523 HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW);
1524 RECT rect;
1526 GetClientRect (hwnd, &rect);
1529 * Adjust for the style
1531 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1533 rect.bottom -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
1535 else
1537 rect.top += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
1541 * Shave-off the right and bottom margins (exluded in the
1542 * rect)
1544 rect.right--;
1545 rect.bottom--;
1547 /* highlight */
1548 htmPen = SelectObject (hdc, hwPen);
1550 MoveToEx (hdc, rect.left, rect.bottom, NULL);
1551 LineTo (hdc, rect.left, rect.top);
1552 LineTo (hdc, rect.right, rect.top);
1554 /* Dark Shadow */
1555 SelectObject (hdc, hbPen);
1556 LineTo (hdc, rect.right, rect.bottom );
1557 LineTo (hdc, rect.left, rect.bottom);
1559 /* shade */
1560 SelectObject (hdc, hShade );
1561 MoveToEx (hdc, rect.right-1, rect.top, NULL);
1562 LineTo (hdc, rect.right-1, rect.bottom-1);
1563 LineTo (hdc, rect.left, rect.bottom-1);
1565 SelectObject(hdc, htmPen);
1568 /******************************************************************************
1569 * TAB_Refresh
1571 * This method repaints the tab control..
1573 static void TAB_Refresh (HWND hwnd, HDC hdc)
1575 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1576 HFONT hOldFont;
1577 INT i;
1579 if (!infoPtr->DoRedraw)
1580 return;
1582 hOldFont = SelectObject (hdc, infoPtr->hFont);
1584 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
1586 for (i = 0; i < infoPtr->uNumItem; i++)
1588 TAB_DrawItem (hwnd, hdc, i);
1591 else
1594 * Draw all the non selected item first.
1596 for (i = 0; i < infoPtr->uNumItem; i++)
1598 if (i != infoPtr->iSelected)
1599 TAB_DrawItem (hwnd, hdc, i);
1603 * Now, draw the border, draw it before the selected item
1604 * since the selected item overwrites part of the border.
1606 TAB_DrawBorder (hwnd, hdc);
1609 * Then, draw the selected item
1611 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
1614 * If we haven't set the current focus yet, set it now.
1615 * Only happens when we first paint the tab controls.
1617 if (infoPtr->uFocus == -1)
1618 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
1621 SelectObject (hdc, hOldFont);
1624 static LRESULT
1625 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
1627 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1629 infoPtr->DoRedraw=(BOOL) wParam;
1630 return 0;
1633 static LRESULT TAB_EraseBackground(
1634 HWND hwnd,
1635 HDC givenDC)
1637 HDC hdc;
1638 RECT clientRect;
1640 HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1642 hdc = givenDC ? givenDC : GetDC(hwnd);
1644 GetClientRect(hwnd, &clientRect);
1646 FillRect(hdc, &clientRect, brush);
1648 if (givenDC==0)
1649 ReleaseDC(hwnd, hdc);
1651 DeleteObject(brush);
1653 return 0;
1656 /******************************************************************************
1657 * TAB_EnsureSelectionVisible
1659 * This method will make sure that the current selection is completely
1660 * visible by scrolling until it is.
1662 static void TAB_EnsureSelectionVisible(
1663 HWND hwnd,
1664 TAB_INFO* infoPtr)
1666 INT iSelected = infoPtr->iSelected;
1668 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
1671 * set the items row to the bottommost row or topmost row depending on
1672 * style
1675 if (infoPtr->uNumRows > 0)
1677 INT newselected=infoPtr->items[iSelected].rect.top;
1678 INT iTargetRow;
1679 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1681 if (lStyle & TCS_BOTTOM)
1682 iTargetRow = 0;
1683 else
1684 iTargetRow = infoPtr->uNumRows;
1686 if (newselected != iTargetRow)
1688 INT i;
1689 for (i=0; i < infoPtr->uNumItem; i++)
1690 if (infoPtr->items[i].rect.top == newselected )
1691 infoPtr->items[i].rect.top = iTargetRow;
1692 else if (lStyle&TCS_BOTTOM)
1694 if (infoPtr->items[i].rect.top < newselected)
1695 infoPtr->items[i].rect.top+=1;
1697 else
1699 if (infoPtr->items[i].rect.top > newselected)
1700 infoPtr->items[i].rect.top-=1;
1703 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1708 * Do the trivial cases first.
1710 if ( (!infoPtr->needsScrolling) ||
1711 (infoPtr->hwndUpDown==0) )
1712 return;
1714 if (infoPtr->leftmostVisible >= iSelected)
1716 infoPtr->leftmostVisible = iSelected;
1718 else
1720 RECT r;
1721 INT width, i;
1723 * Calculate the part of the client area that is visible.
1725 GetClientRect(hwnd, &r);
1726 width = r.right;
1728 GetClientRect(infoPtr->hwndUpDown, &r);
1729 width -= r.right;
1731 if ((infoPtr->items[iSelected].rect.right -
1732 infoPtr->items[iSelected].rect.left) >= width )
1734 /* Special case: width of selected item is greater than visible
1735 * part of control.
1737 infoPtr->leftmostVisible = iSelected;
1739 else
1741 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
1743 if ((infoPtr->items[iSelected].rect.right -
1744 infoPtr->items[i].rect.left) < width)
1745 break;
1747 infoPtr->leftmostVisible = i;
1751 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
1752 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1754 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
1755 MAKELONG(infoPtr->leftmostVisible, 0));
1758 /******************************************************************************
1759 * TAB_InvalidateTabArea
1761 * This method will invalidate the portion of the control that contains the
1762 * tabs. It is called when the state of the control changes and needs
1763 * to be redisplayed
1765 static void TAB_InvalidateTabArea(
1766 HWND hwnd,
1767 TAB_INFO* infoPtr)
1769 RECT clientRect;
1771 GetClientRect(hwnd, &clientRect);
1773 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1775 clientRect.top = clientRect.bottom - (infoPtr->tabHeight *
1776 (infoPtr->uNumRows + 1) + 3);
1778 else
1780 clientRect.bottom = clientRect.top + (infoPtr->tabHeight *
1781 (infoPtr->uNumRows + 1) + 1);
1784 InvalidateRect(hwnd, &clientRect, TRUE);
1787 static LRESULT
1788 TAB_Paint (HWND hwnd, WPARAM wParam)
1790 HDC hdc;
1791 PAINTSTRUCT ps;
1793 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
1794 TAB_Refresh (hwnd, hdc);
1796 if(!wParam)
1797 EndPaint (hwnd, &ps);
1799 return 0;
1802 static LRESULT
1803 TAB_InsertItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1805 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1806 TCITEMA *pti;
1807 INT iItem, len;
1808 RECT rect;
1810 GetClientRect (hwnd, &rect);
1811 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
1812 rect.top, rect.left, rect.bottom, rect.right);
1814 pti = (TCITEMA *)lParam;
1815 iItem = (INT)wParam;
1817 if (iItem < 0) return -1;
1818 if (iItem > infoPtr->uNumItem)
1819 iItem = infoPtr->uNumItem;
1821 if (infoPtr->uNumItem == 0) {
1822 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
1823 infoPtr->uNumItem++;
1824 infoPtr->iSelected = 0;
1826 else {
1827 TAB_ITEM *oldItems = infoPtr->items;
1829 infoPtr->uNumItem++;
1830 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
1832 /* pre insert copy */
1833 if (iItem > 0) {
1834 memcpy (&infoPtr->items[0], &oldItems[0],
1835 iItem * sizeof(TAB_ITEM));
1838 /* post insert copy */
1839 if (iItem < infoPtr->uNumItem - 1) {
1840 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
1841 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
1845 if (iItem <= infoPtr->iSelected)
1846 infoPtr->iSelected++;
1848 COMCTL32_Free (oldItems);
1851 infoPtr->items[iItem].mask = pti->mask;
1852 if (pti->mask & TCIF_TEXT) {
1853 len = lstrlenA (pti->pszText);
1854 infoPtr->items[iItem].pszText = COMCTL32_Alloc (len+1);
1855 lstrcpyA (infoPtr->items[iItem].pszText, pti->pszText);
1856 infoPtr->items[iItem].cchTextMax = pti->cchTextMax;
1859 if (pti->mask & TCIF_IMAGE)
1860 infoPtr->items[iItem].iImage = pti->iImage;
1862 if (pti->mask & TCIF_PARAM)
1863 infoPtr->items[iItem].lParam = pti->lParam;
1865 TAB_SetItemBounds(hwnd);
1866 TAB_InvalidateTabArea(hwnd, infoPtr);
1868 TRACE("[%04x]: added item %d '%s'\n",
1869 hwnd, iItem, infoPtr->items[iItem].pszText);
1871 return iItem;
1874 static LRESULT
1875 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
1877 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1878 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1879 LONG lResult = 0;
1881 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
1883 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
1884 infoPtr->tabWidth = (INT)LOWORD(lParam);
1885 infoPtr->tabHeight = (INT)HIWORD(lParam);
1887 infoPtr->fSizeSet = TRUE;
1889 return lResult;
1892 static LRESULT
1893 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1895 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1896 TCITEMA *tabItem;
1897 TAB_ITEM *wineItem;
1898 INT iItem,len;
1900 iItem=(INT) wParam;
1901 tabItem=(LPTCITEMA ) lParam;
1902 TRACE("%d %p\n",iItem, tabItem);
1903 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
1905 wineItem=& infoPtr->items[iItem];
1907 if (tabItem->mask & TCIF_IMAGE)
1908 wineItem->iImage=tabItem->iImage;
1910 if (tabItem->mask & TCIF_PARAM)
1911 wineItem->lParam=tabItem->lParam;
1913 if (tabItem->mask & TCIF_RTLREADING)
1914 FIXME("TCIF_RTLREADING\n");
1916 if (tabItem->mask & TCIF_STATE)
1917 wineItem->dwState=tabItem->dwState;
1919 if (tabItem->mask & TCIF_TEXT) {
1920 len=lstrlenA (tabItem->pszText);
1921 if (len>wineItem->cchTextMax)
1922 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
1923 lstrcpyA (wineItem->pszText, tabItem->pszText);
1927 * Update and repaint tabs.
1929 TAB_SetItemBounds(hwnd);
1930 TAB_InvalidateTabArea(hwnd,infoPtr);
1932 return TRUE;
1935 static LRESULT
1936 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1938 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1940 return infoPtr->uNumItem;
1944 static LRESULT
1945 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1947 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1948 TCITEMA *tabItem;
1949 TAB_ITEM *wineItem;
1950 INT iItem;
1952 iItem=(INT) wParam;
1953 tabItem=(LPTCITEMA) lParam;
1954 TRACE("\n");
1955 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
1957 wineItem=& infoPtr->items[iItem];
1959 if (tabItem->mask & TCIF_IMAGE)
1960 tabItem->iImage=wineItem->iImage;
1962 if (tabItem->mask & TCIF_PARAM)
1963 tabItem->lParam=wineItem->lParam;
1965 if (tabItem->mask & TCIF_RTLREADING)
1966 FIXME("TCIF_RTLREADING\n");
1968 if (tabItem->mask & TCIF_STATE)
1969 tabItem->dwState=wineItem->dwState;
1971 if (tabItem->mask & TCIF_TEXT)
1972 lstrcpynA (tabItem->pszText, wineItem->pszText, tabItem->cchTextMax);
1974 return TRUE;
1977 static LRESULT
1978 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1980 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1981 INT iItem = (INT) wParam;
1982 BOOL bResult = FALSE;
1984 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
1986 TAB_ITEM *oldItems = infoPtr->items;
1988 infoPtr->uNumItem--;
1989 infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
1991 if (iItem > 0)
1992 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
1994 if (iItem < infoPtr->uNumItem)
1995 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
1996 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
1998 COMCTL32_Free (oldItems);
2001 * Readjust the selected index.
2003 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2004 infoPtr->iSelected--;
2006 if (iItem < infoPtr->iSelected)
2007 infoPtr->iSelected--;
2009 if (infoPtr->uNumItem == 0)
2010 infoPtr->iSelected = -1;
2013 * Reposition and repaint tabs.
2015 TAB_SetItemBounds(hwnd);
2016 TAB_InvalidateTabArea(hwnd,infoPtr);
2018 bResult = TRUE;
2021 return bResult;
2024 static LRESULT
2025 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2027 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2029 COMCTL32_Free (infoPtr->items);
2030 infoPtr->uNumItem = 0;
2031 infoPtr->iSelected = -1;
2032 if (infoPtr->iHotTracked >= 0)
2033 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2034 infoPtr->iHotTracked = -1;
2036 TAB_SetItemBounds(hwnd);
2037 TAB_InvalidateTabArea(hwnd,infoPtr);
2038 return TRUE;
2042 static LRESULT
2043 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2045 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2047 TRACE("\n");
2048 return (LRESULT)infoPtr->hFont;
2051 static LRESULT
2052 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2055 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2057 TRACE("%x %lx\n",wParam, lParam);
2059 infoPtr->hFont = (HFONT)wParam;
2061 TAB_SetItemBounds(hwnd);
2063 TAB_InvalidateTabArea(hwnd, infoPtr);
2065 return 0;
2069 static LRESULT
2070 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2072 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2074 TRACE("\n");
2075 return (LRESULT)infoPtr->himl;
2078 static LRESULT
2079 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2081 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2082 HIMAGELIST himlPrev;
2084 TRACE("\n");
2085 himlPrev = infoPtr->himl;
2086 infoPtr->himl= (HIMAGELIST)lParam;
2087 return (LRESULT)himlPrev;
2091 static LRESULT
2092 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2095 /* I'm not really sure what the following code was meant to do.
2096 This is what it is doing:
2097 When WM_SIZE is sent with SIZE_RESTORED, the control
2098 gets positioned in the top left corner.
2100 RECT parent_rect;
2101 HWND parent;
2102 UINT uPosFlags,cx,cy;
2104 uPosFlags=0;
2105 if (!wParam) {
2106 parent = GetParent (hwnd);
2107 GetClientRect(parent, &parent_rect);
2108 cx=LOWORD (lParam);
2109 cy=HIWORD (lParam);
2110 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2111 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2113 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2114 cx, cy, uPosFlags | SWP_NOZORDER);
2115 } else {
2116 FIXME (tab,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2117 } */
2120 * Recompute the size/position of the tabs.
2122 TAB_SetItemBounds (hwnd);
2125 * Force a repaint of the control.
2127 InvalidateRect(hwnd, NULL, TRUE);
2129 return 0;
2133 static LRESULT
2134 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2136 TAB_INFO *infoPtr;
2137 TEXTMETRICA fontMetrics;
2138 HDC hdc;
2139 HFONT hOldFont;
2140 DWORD dwStyle;
2142 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
2144 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
2146 infoPtr->uNumItem = 0;
2147 infoPtr->uNumRows = 0;
2148 infoPtr->hFont = 0;
2149 infoPtr->items = 0;
2150 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
2151 infoPtr->iSelected = -1;
2152 infoPtr->iHotTracked = -1;
2153 infoPtr->uFocus = -1;
2154 infoPtr->hwndToolTip = 0;
2155 infoPtr->DoRedraw = TRUE;
2156 infoPtr->needsScrolling = FALSE;
2157 infoPtr->hwndUpDown = 0;
2158 infoPtr->leftmostVisible = 0;
2159 infoPtr->fSizeSet = FALSE;
2161 TRACE("Created tab control, hwnd [%04x]\n", hwnd);
2163 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2164 if you don't specify in CreateWindow. This is necesary in
2165 order for paint to work correctly. This follows windows behaviour. */
2166 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
2167 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2169 if (dwStyle & TCS_TOOLTIPS) {
2170 /* Create tooltip control */
2171 infoPtr->hwndToolTip =
2172 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2173 CW_USEDEFAULT, CW_USEDEFAULT,
2174 CW_USEDEFAULT, CW_USEDEFAULT,
2175 hwnd, 0, 0, 0);
2177 /* Send NM_TOOLTIPSCREATED notification */
2178 if (infoPtr->hwndToolTip) {
2179 NMTOOLTIPSCREATED nmttc;
2181 nmttc.hdr.hwndFrom = hwnd;
2182 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
2183 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2184 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2186 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2187 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
2192 * We need to get text information so we need a DC and we need to select
2193 * a font.
2195 hdc = GetDC(hwnd);
2196 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
2199 * Use the system font to determine the initial height of a tab.
2201 GetTextMetricsA(hdc, &fontMetrics);
2204 * Make sure there is enough space for the letters + growing the
2205 * selected item + extra space for the selected item.
2207 infoPtr->tabHeight = fontMetrics.tmHeight + 2*VERTICAL_ITEM_PADDING +
2208 SELECTED_TAB_OFFSET;
2211 * Initialize the width of a tab.
2213 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
2215 SelectObject (hdc, hOldFont);
2216 ReleaseDC(hwnd, hdc);
2218 return 0;
2221 static LRESULT
2222 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
2224 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2225 INT iItem;
2227 if (!infoPtr)
2228 return 0;
2230 if (infoPtr->items) {
2231 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
2232 if (infoPtr->items[iItem].pszText)
2233 COMCTL32_Free (infoPtr->items[iItem].pszText);
2235 COMCTL32_Free (infoPtr->items);
2238 if (infoPtr->hwndToolTip)
2239 DestroyWindow (infoPtr->hwndToolTip);
2241 if (infoPtr->hwndUpDown)
2242 DestroyWindow(infoPtr->hwndUpDown);
2244 if (infoPtr->iHotTracked >= 0)
2245 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2247 COMCTL32_Free (infoPtr);
2248 SetWindowLongA(hwnd, 0, 0);
2249 return 0;
2252 static LRESULT WINAPI
2253 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2256 TRACE("hwnd=%x msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
2257 if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
2258 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
2260 switch (uMsg)
2262 case TCM_GETIMAGELIST:
2263 return TAB_GetImageList (hwnd, wParam, lParam);
2265 case TCM_SETIMAGELIST:
2266 return TAB_SetImageList (hwnd, wParam, lParam);
2268 case TCM_GETITEMCOUNT:
2269 return TAB_GetItemCount (hwnd, wParam, lParam);
2271 case TCM_GETITEMA:
2272 return TAB_GetItemA (hwnd, wParam, lParam);
2274 case TCM_GETITEMW:
2275 FIXME("Unimplemented msg TCM_GETITEMW\n");
2276 return 0;
2278 case TCM_SETITEMA:
2279 return TAB_SetItemA (hwnd, wParam, lParam);
2281 case TCM_SETITEMW:
2282 FIXME("Unimplemented msg TCM_SETITEMW\n");
2283 return 0;
2285 case TCM_DELETEITEM:
2286 return TAB_DeleteItem (hwnd, wParam, lParam);
2288 case TCM_DELETEALLITEMS:
2289 return TAB_DeleteAllItems (hwnd, wParam, lParam);
2291 case TCM_GETITEMRECT:
2292 return TAB_GetItemRect (hwnd, wParam, lParam);
2294 case TCM_GETCURSEL:
2295 return TAB_GetCurSel (hwnd);
2297 case TCM_HITTEST:
2298 return TAB_HitTest (hwnd, wParam, lParam);
2300 case TCM_SETCURSEL:
2301 return TAB_SetCurSel (hwnd, wParam);
2303 case TCM_INSERTITEMA:
2304 return TAB_InsertItem (hwnd, wParam, lParam);
2306 case TCM_INSERTITEMW:
2307 FIXME("Unimplemented msg TCM_INSERTITEMW\n");
2308 return 0;
2310 case TCM_SETITEMEXTRA:
2311 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
2312 return 0;
2314 case TCM_ADJUSTRECT:
2315 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
2317 case TCM_SETITEMSIZE:
2318 return TAB_SetItemSize (hwnd, wParam, lParam);
2320 case TCM_REMOVEIMAGE:
2321 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
2322 return 0;
2324 case TCM_SETPADDING:
2325 FIXME("Unimplemented msg TCM_SETPADDING\n");
2326 return 0;
2328 case TCM_GETROWCOUNT:
2329 FIXME("Unimplemented msg TCM_GETROWCOUNT\n");
2330 return 0;
2332 case TCM_GETUNICODEFORMAT:
2333 FIXME("Unimplemented msg TCM_GETUNICODEFORMAT\n");
2334 return 0;
2336 case TCM_SETUNICODEFORMAT:
2337 FIXME("Unimplemented msg TCM_SETUNICODEFORMAT\n");
2338 return 0;
2340 case TCM_HIGHLIGHTITEM:
2341 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
2342 return 0;
2344 case TCM_GETTOOLTIPS:
2345 return TAB_GetToolTips (hwnd, wParam, lParam);
2347 case TCM_SETTOOLTIPS:
2348 return TAB_SetToolTips (hwnd, wParam, lParam);
2350 case TCM_GETCURFOCUS:
2351 return TAB_GetCurFocus (hwnd);
2353 case TCM_SETCURFOCUS:
2354 return TAB_SetCurFocus (hwnd, wParam);
2356 case TCM_SETMINTABWIDTH:
2357 FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
2358 return 0;
2360 case TCM_DESELECTALL:
2361 FIXME("Unimplemented msg TCM_DESELECTALL\n");
2362 return 0;
2364 case TCM_GETEXTENDEDSTYLE:
2365 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
2366 return 0;
2368 case TCM_SETEXTENDEDSTYLE:
2369 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
2370 return 0;
2372 case WM_GETFONT:
2373 return TAB_GetFont (hwnd, wParam, lParam);
2375 case WM_SETFONT:
2376 return TAB_SetFont (hwnd, wParam, lParam);
2378 case WM_CREATE:
2379 return TAB_Create (hwnd, wParam, lParam);
2381 case WM_NCDESTROY:
2382 return TAB_Destroy (hwnd, wParam, lParam);
2384 case WM_GETDLGCODE:
2385 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2387 case WM_LBUTTONDOWN:
2388 return TAB_LButtonDown (hwnd, wParam, lParam);
2390 case WM_LBUTTONUP:
2391 return TAB_LButtonUp (hwnd, wParam, lParam);
2393 case WM_RBUTTONDOWN:
2394 return TAB_RButtonDown (hwnd, wParam, lParam);
2396 case WM_MOUSEMOVE:
2397 return TAB_MouseMove (hwnd, wParam, lParam);
2399 case WM_ERASEBKGND:
2400 return TAB_EraseBackground (hwnd, (HDC)wParam);
2402 case WM_PAINT:
2403 return TAB_Paint (hwnd, wParam);
2405 case WM_SIZE:
2406 return TAB_Size (hwnd, wParam, lParam);
2408 case WM_SETREDRAW:
2409 return TAB_SetRedraw (hwnd, wParam);
2411 case WM_HSCROLL:
2412 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
2414 case WM_STYLECHANGED:
2415 TAB_SetItemBounds (hwnd);
2416 InvalidateRect(hwnd, NULL, TRUE);
2417 return 0;
2419 case WM_KILLFOCUS:
2420 case WM_SETFOCUS:
2421 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
2423 case WM_KEYUP:
2424 return TAB_KeyUp(hwnd, wParam);
2426 default:
2427 if (uMsg >= WM_USER)
2428 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
2429 uMsg, wParam, lParam);
2430 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
2433 return 0;
2437 VOID
2438 TAB_Register (void)
2440 WNDCLASSA wndClass;
2442 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
2443 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
2444 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
2445 wndClass.cbClsExtra = 0;
2446 wndClass.cbWndExtra = sizeof(TAB_INFO *);
2447 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
2448 wndClass.hbrBackground = (HBRUSH)NULL;
2449 wndClass.lpszClassName = WC_TABCONTROLA;
2451 RegisterClassA (&wndClass);
2455 VOID
2456 TAB_Unregister (void)
2458 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);