4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
18 #include "debugtools.h"
21 DEFAULT_DEBUG_CHANNEL(tab
);
31 RECT rect
; /* bounding rectangle of the item relative to the
32 * leftmost item (the leftmost item, 0, would have a
33 * "left" member of 0 in this rectangle)
35 * additionally the top member hold the row number
36 * and bottom is unused and should be 0 */
41 UINT uNumItem
; /* number of tab items */
42 UINT uNumRows
; /* number of tab rows */
43 INT tabHeight
; /* height of the tab row */
44 INT tabWidth
; /* width of tabs */
45 HFONT hFont
; /* handle to the current font */
46 HCURSOR hcurArrow
; /* handle to the current cursor */
47 HIMAGELIST himl
; /* handle to a image list (may be 0) */
48 HWND hwndToolTip
; /* handle to tab's tooltip */
50 INT leftmostVisible
; /* Used for scrolling, this member contains
51 * the index of the first visible item */
52 INT iSelected
; /* the currently selected item */
53 INT iHotTracked
; /* the highlighted item under the mouse */
54 INT uFocus
; /* item which has the focus */
55 TAB_ITEM
* items
; /* pointer to an array of TAB_ITEM's */
56 BOOL DoRedraw
; /* flag for redrawing when tab contents is changed*/
57 BOOL needsScrolling
; /* TRUE if the size of the tabs is greater than
58 * the size of the control */
59 BOOL fSizeSet
; /* was the size of the tabs explicitly set? */
60 HWND hwndUpDown
; /* Updown control used for scrolling */
63 /******************************************************************************
64 * Positioning constants
66 #define SELECTED_TAB_OFFSET 2
67 #define HORIZONTAL_ITEM_PADDING 5
68 #define VERTICAL_ITEM_PADDING 3
69 #define ROUND_CORNER_SIZE 2
70 #define FOCUS_RECT_HOFFSET 2
71 #define FOCUS_RECT_VOFFSET 1
72 #define DISPLAY_AREA_PADDINGX 2
73 #define DISPLAY_AREA_PADDINGY 2
74 #define CONTROL_BORDER_SIZEX 2
75 #define CONTROL_BORDER_SIZEY 2
76 #define BUTTON_SPACINGX 10
77 #define DEFAULT_TAB_WIDTH 96
79 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
81 /******************************************************************************
82 * Hot-tracking timer constants
84 #define TAB_HOTTRACK_TIMER 1
85 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
87 /******************************************************************************
90 static void TAB_Refresh (HWND hwnd
, HDC hdc
);
91 static void TAB_InvalidateTabArea(HWND hwnd
, TAB_INFO
* infoPtr
);
92 static void TAB_EnsureSelectionVisible(HWND hwnd
, TAB_INFO
* infoPtr
);
93 static void TAB_DrawItem(HWND hwnd
, HDC hdc
, INT iItem
);
94 static void TAB_DrawItemInterior(HWND hwnd
, HDC hdc
, INT iItem
, RECT
* drawRect
);
97 TAB_SendSimpleNotify (HWND hwnd
, UINT code
)
101 nmhdr
.hwndFrom
= hwnd
;
102 nmhdr
.idFrom
= GetWindowLongA(hwnd
, GWL_ID
);
105 return (BOOL
) SendMessageA (GetParent (hwnd
), WM_NOTIFY
,
106 (WPARAM
) nmhdr
.idFrom
, (LPARAM
) &nmhdr
);
111 TAB_RelayEvent (HWND hwndTip
, HWND hwndMsg
, UINT uMsg
,
112 WPARAM wParam
, LPARAM lParam
)
120 msg
.time
= GetMessageTime ();
121 msg
.pt
.x
= LOWORD(GetMessagePos ());
122 msg
.pt
.y
= HIWORD(GetMessagePos ());
124 SendMessageA (hwndTip
, TTM_RELAYEVENT
, 0, (LPARAM
)&msg
);
130 TAB_GetCurSel (HWND hwnd
)
132 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
134 return infoPtr
->iSelected
;
138 TAB_GetCurFocus (HWND hwnd
)
140 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
142 return infoPtr
->uFocus
;
146 TAB_GetToolTips (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
148 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
150 if (infoPtr
== NULL
) return 0;
151 return infoPtr
->hwndToolTip
;
156 TAB_SetCurSel (HWND hwnd
,WPARAM wParam
)
158 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
159 INT iItem
=(INT
) wParam
;
163 if ((iItem
>= 0) && (iItem
< infoPtr
->uNumItem
)) {
164 prevItem
=infoPtr
->iSelected
;
165 infoPtr
->iSelected
=iItem
;
166 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
167 TAB_InvalidateTabArea(hwnd
, infoPtr
);
173 TAB_SetCurFocus (HWND hwnd
,WPARAM wParam
)
175 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
176 INT iItem
=(INT
) wParam
;
178 if ((iItem
< 0) || (iItem
>= infoPtr
->uNumItem
)) return 0;
180 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BUTTONS
) {
181 FIXME("Should set input focus\n");
183 if (infoPtr
->iSelected
!= iItem
|| infoPtr
->uFocus
== -1 ) {
184 infoPtr
->uFocus
=iItem
;
185 if (TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
)!=TRUE
) {
186 infoPtr
->iSelected
= iItem
;
187 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
189 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
190 TAB_InvalidateTabArea(hwnd
, infoPtr
);
198 TAB_SetToolTips (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
200 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
202 if (infoPtr
== NULL
) return 0;
203 infoPtr
->hwndToolTip
= (HWND
)wParam
;
207 /******************************************************************************
208 * TAB_InternalGetItemRect
210 * This method will calculate the rectangle representing a given tab item in
211 * client coordinates. This method takes scrolling into account.
213 * This method returns TRUE if the item is visible in the window and FALSE
214 * if it is completely outside the client area.
216 static BOOL
TAB_InternalGetItemRect(
223 RECT tmpItemRect
,clientRect
;
224 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
227 * Perform a sanity check and a trivial visibility check.
229 if ( (infoPtr
->uNumItem
<=0) ||
230 (itemIndex
>= infoPtr
->uNumItem
) ||
231 (!(lStyle
&TCS_MULTILINE
) && (itemIndex
< infoPtr
->leftmostVisible
)) )
235 * Avoid special cases in this procedure by assigning the "out"
236 * parameters if the caller didn't supply them
239 itemRect
= &tmpItemRect
;
242 * Retrieve the unmodified item rect.
244 *itemRect
= infoPtr
->items
[itemIndex
].rect
;
247 * calculate the times bottom and top based on the row
249 GetClientRect(hwnd
, &clientRect
);
251 if (lStyle
& TCS_BOTTOM
)
253 itemRect
->bottom
= clientRect
.bottom
-
254 SELECTED_TAB_OFFSET
-
255 itemRect
->top
* (infoPtr
->tabHeight
- 2);
257 itemRect
->top
= clientRect
.bottom
-
259 itemRect
->top
* ( infoPtr
->tabHeight
- 2);
263 itemRect
->bottom
= clientRect
.top
+
265 itemRect
->top
* (infoPtr
->tabHeight
- 2);
266 itemRect
->top
= clientRect
.top
+
268 itemRect
->top
* (infoPtr
->tabHeight
- 2);
272 * "scroll" it to make sure the item at the very left of the
273 * tab control is the leftmost visible tab.
276 -infoPtr
->items
[infoPtr
->leftmostVisible
].rect
.left
,
280 * Move the rectangle so the first item is slightly offset from
281 * the left of the tab control.
289 * Now, calculate the position of the item as if it were selected.
291 if (selectedRect
!=NULL
)
293 CopyRect(selectedRect
, itemRect
);
296 * The rectangle of a selected item is a bit wider.
298 InflateRect(selectedRect
, SELECTED_TAB_OFFSET
, 0);
301 * If it also a bit higher.
303 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
305 selectedRect
->top
-=2; /* the border is thicker on the bottom */
306 selectedRect
->bottom
+=SELECTED_TAB_OFFSET
;
310 selectedRect
->top
-=SELECTED_TAB_OFFSET
;
311 selectedRect
->bottom
+=1;
318 static BOOL
TAB_GetItemRect(HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
320 return TAB_InternalGetItemRect(hwnd
, TAB_GetInfoPtr(hwnd
), (INT
)wParam
,
321 (LPRECT
)lParam
, (LPRECT
)NULL
);
324 /******************************************************************************
327 * This method is called to handle keyboard input
329 static LRESULT
TAB_KeyUp(
333 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
339 newItem
= infoPtr
->uFocus
-1;
342 newItem
= infoPtr
->uFocus
+1;
347 * If we changed to a valid item, change the selection
349 if ( (newItem
>= 0) &&
350 (newItem
< infoPtr
->uNumItem
) &&
351 (infoPtr
->uFocus
!= newItem
) )
353 if (!TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
))
355 infoPtr
->iSelected
= newItem
;
356 infoPtr
->uFocus
= newItem
;
357 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
359 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
360 TAB_InvalidateTabArea(hwnd
, infoPtr
);
367 /******************************************************************************
370 * This method is called whenever the focus goes in or out of this control
371 * it is used to update the visual state of the control.
373 static LRESULT
TAB_FocusChanging(
379 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
384 * Get the rectangle for the item.
386 isVisible
= TAB_InternalGetItemRect(hwnd
,
393 * If the rectangle is not completely invisible, invalidate that
394 * portion of the window.
398 InvalidateRect(hwnd
, &selectedRect
, TRUE
);
402 * Don't otherwise disturb normal behavior.
404 return DefWindowProcA (hwnd
, uMsg
, wParam
, lParam
);
407 static HWND
TAB_InternalHitTest (
417 for (iCount
= 0; iCount
< infoPtr
->uNumItem
; iCount
++)
419 TAB_InternalGetItemRect(hwnd
,
425 if (PtInRect (&rect
, pt
))
427 *flags
= TCHT_ONITEM
;
437 TAB_HitTest (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
439 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
440 LPTCHITTESTINFO lptest
=(LPTCHITTESTINFO
) lParam
;
442 return TAB_InternalHitTest (hwnd
, infoPtr
,lptest
->pt
,&lptest
->flags
);
447 TAB_LButtonDown (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
449 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
453 if (infoPtr
->hwndToolTip
)
454 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
455 WM_LBUTTONDOWN
, wParam
, lParam
);
457 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_FOCUSONBUTTONDOWN
) {
461 if (infoPtr
->hwndToolTip
)
462 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
463 WM_LBUTTONDOWN
, wParam
, lParam
);
465 pt
.x
= (INT
)LOWORD(lParam
);
466 pt
.y
= (INT
)HIWORD(lParam
);
468 newItem
=TAB_InternalHitTest (hwnd
, infoPtr
,pt
,&dummy
);
470 TRACE("On Tab, item %d\n", newItem
);
472 if ( (newItem
!=-1) &&
473 (infoPtr
->iSelected
!= newItem
) )
475 if (TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
)!=TRUE
)
477 infoPtr
->iSelected
= newItem
;
478 infoPtr
->uFocus
= newItem
;
479 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
481 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
483 TAB_InvalidateTabArea(hwnd
, infoPtr
);
490 TAB_LButtonUp (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
492 TAB_SendSimpleNotify(hwnd
, NM_CLICK
);
498 TAB_RButtonDown (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
500 TAB_SendSimpleNotify(hwnd
, NM_RCLICK
);
504 /******************************************************************************
505 * TAB_DrawLoneItemInterior
507 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
508 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
509 * up the device context and font. This routine does the same setup but
510 * only calls TAB_DrawItemInterior for the single specified item.
513 TAB_DrawLoneItemInterior(HWND hwnd
, TAB_INFO
* infoPtr
, int iItem
)
515 HDC hdc
= GetDC(hwnd
);
516 HFONT hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
517 TAB_DrawItemInterior(hwnd
, hdc
, iItem
, NULL
);
518 SelectObject(hdc
, hOldFont
);
519 ReleaseDC(hwnd
, hdc
);
522 /******************************************************************************
523 * TAB_HotTrackTimerProc
525 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
526 * timer is setup so we can check if the mouse is moved out of our window.
527 * (We don't get an event when the mouse leaves, the mouse-move events just
528 * stop being delivered to our window and just start being delivered to
529 * another window.) This function is called when the timer triggers so
530 * we can check if the mouse has left our window. If so, we un-highlight
531 * the hot-tracked tab.
534 TAB_HotTrackTimerProc
536 HWND hwnd
, /* handle of window for timer messages */
537 UINT uMsg
, /* WM_TIMER message */
538 UINT idEvent
, /* timer identifier */
539 DWORD dwTime
/* current system time */
542 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
544 if (infoPtr
!= NULL
&& infoPtr
->iHotTracked
>= 0)
549 ** If we can't get the cursor position, or if the cursor is outside our
550 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
551 ** "outside" even if it is within our bounding rect if another window
552 ** overlaps. Note also that the case where the cursor stayed within our
553 ** window but has moved off the hot-tracked tab will be handled by the
554 ** WM_MOUSEMOVE event.
556 if (!GetCursorPos(&pt
) || WindowFromPoint(pt
) != hwnd
)
558 /* Redraw iHotTracked to look normal */
559 INT iRedraw
= infoPtr
->iHotTracked
;
560 infoPtr
->iHotTracked
= -1;
561 TAB_DrawLoneItemInterior(hwnd
, infoPtr
, iRedraw
);
563 /* Kill this timer */
564 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
569 /******************************************************************************
572 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
573 * should be highlighted. This function determines which tab in a tab control,
574 * if any, is under the mouse and records that information. The caller may
575 * supply output parameters to receive the item number of the tab item which
576 * was highlighted but isn't any longer and of the tab item which is now
577 * highlighted but wasn't previously. The caller can use this information to
578 * selectively redraw those tab items.
580 * If the caller has a mouse position, it can supply it through the pos
581 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
582 * supplies NULL and this function determines the current mouse position
590 int* out_redrawLeave
,
594 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
599 if (out_redrawLeave
!= NULL
)
600 *out_redrawLeave
= -1;
601 if (out_redrawEnter
!= NULL
)
602 *out_redrawEnter
= -1;
604 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_HOTTRACK
)
612 ScreenToClient(hwnd
, &pt
);
620 item
= TAB_InternalHitTest(hwnd
, infoPtr
, pt
, &flags
);
623 if (item
!= infoPtr
->iHotTracked
)
625 if (infoPtr
->iHotTracked
>= 0)
627 /* Mark currently hot-tracked to be redrawn to look normal */
628 if (out_redrawLeave
!= NULL
)
629 *out_redrawLeave
= infoPtr
->iHotTracked
;
633 /* Kill timer which forces recheck of mouse pos */
634 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
639 /* Start timer so we recheck mouse pos */
640 UINT timerID
= SetTimer
644 TAB_HOTTRACK_TIMER_INTERVAL
,
645 TAB_HotTrackTimerProc
649 return; /* Hot tracking not available */
652 infoPtr
->iHotTracked
= item
;
656 /* Mark new hot-tracked to be redrawn to look highlighted */
657 if (out_redrawEnter
!= NULL
)
658 *out_redrawEnter
= item
;
663 /******************************************************************************
666 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
669 TAB_MouseMove (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
674 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
676 if (infoPtr
->hwndToolTip
)
677 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
678 WM_LBUTTONDOWN
, wParam
, lParam
);
680 /* Determine which tab to highlight. Redraw tabs which change highlight
682 TAB_RecalcHotTrack(hwnd
, &lParam
, &redrawLeave
, &redrawEnter
);
684 if (redrawLeave
!= -1)
685 TAB_DrawLoneItemInterior(hwnd
, infoPtr
, redrawLeave
);
686 if (redrawEnter
!= -1)
687 TAB_DrawLoneItemInterior(hwnd
, infoPtr
, redrawEnter
);
692 /******************************************************************************
695 * Calculates the tab control's display area given the windows rectangle or
696 * the window rectangle given the requested display rectangle.
698 static LRESULT
TAB_AdjustRect(
703 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
708 * Go from display rectangle
712 * Add the height of the tabs.
714 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
715 prc
->bottom
+= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
717 prc
->top
-= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
720 * Inflate the rectangle for the padding
722 InflateRect(prc
, DISPLAY_AREA_PADDINGX
, DISPLAY_AREA_PADDINGY
);
725 * Inflate for the border
727 InflateRect(prc
, CONTROL_BORDER_SIZEX
, CONTROL_BORDER_SIZEX
);
732 * Go from window rectangle.
736 * Deflate the rectangle for the border
738 InflateRect(prc
, -CONTROL_BORDER_SIZEX
, -CONTROL_BORDER_SIZEX
);
741 * Deflate the rectangle for the padding
743 InflateRect(prc
, -DISPLAY_AREA_PADDINGX
, -DISPLAY_AREA_PADDINGY
);
746 * Remove the height of the tabs.
748 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
749 prc
->bottom
-= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
751 prc
->top
+= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
758 /******************************************************************************
761 * This method will handle the notification from the scroll control and
762 * perform the scrolling operation on the tab control.
764 static LRESULT
TAB_OnHScroll(
770 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
772 if(nScrollCode
== SB_THUMBPOSITION
&& nPos
!= infoPtr
->leftmostVisible
)
774 if(nPos
< infoPtr
->leftmostVisible
)
775 infoPtr
->leftmostVisible
--;
777 infoPtr
->leftmostVisible
++;
779 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
780 TAB_InvalidateTabArea(hwnd
, infoPtr
);
781 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
782 MAKELONG(infoPtr
->leftmostVisible
, 0));
788 /******************************************************************************
791 * This method will check the current scrolling state and make sure the
792 * scrolling control is displayed (or not).
794 static void TAB_SetupScrolling(
797 const RECT
* clientRect
)
800 if (infoPtr
->needsScrolling
)
806 * Calculate the position of the scroll control.
808 controlPos
.right
= clientRect
->right
;
809 controlPos
.left
= controlPos
.right
- 2*GetSystemMetrics(SM_CXHSCROLL
);
811 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
813 controlPos
.top
= clientRect
->bottom
- infoPtr
->tabHeight
;
814 controlPos
.bottom
= controlPos
.top
+ GetSystemMetrics(SM_CYHSCROLL
);
818 controlPos
.bottom
= clientRect
->top
+ infoPtr
->tabHeight
;
819 controlPos
.top
= controlPos
.bottom
- GetSystemMetrics(SM_CYHSCROLL
);
823 * If we don't have a scroll control yet, we want to create one.
824 * If we have one, we want to make sure it's positioned right.
826 if (infoPtr
->hwndUpDown
==0)
829 * I use a scrollbar since it seems to be more stable than the Updown
832 infoPtr
->hwndUpDown
= CreateWindowA("msctls_updown32",
834 WS_VISIBLE
| WS_CHILD
| UDS_HORZ
,
835 controlPos
.left
, controlPos
.top
,
836 controlPos
.right
- controlPos
.left
,
837 controlPos
.bottom
- controlPos
.top
,
845 SetWindowPos(infoPtr
->hwndUpDown
,
847 controlPos
.left
, controlPos
.top
,
848 controlPos
.right
- controlPos
.left
,
849 controlPos
.bottom
- controlPos
.top
,
850 SWP_SHOWWINDOW
| SWP_NOZORDER
);
853 /* Now calculate upper limit of the updown control range.
854 * We do this by calculating how many tabs will be offscreen when the
855 * last tab is visible.
857 if(infoPtr
->uNumItem
)
859 vsize
= clientRect
->right
- (controlPos
.right
- controlPos
.left
+ 1);
860 maxRange
= infoPtr
->uNumItem
;
861 tabwidth
= infoPtr
->items
[maxRange
-1].rect
.right
;
863 for(; maxRange
> 0; maxRange
--)
865 if(tabwidth
- infoPtr
->items
[maxRange
- 1].rect
.left
> vsize
)
869 if(maxRange
== infoPtr
->uNumItem
)
876 * If we once had a scroll control... hide it.
878 if (infoPtr
->hwndUpDown
!=0)
880 ShowWindow(infoPtr
->hwndUpDown
, SW_HIDE
);
883 if (infoPtr
->hwndUpDown
)
884 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETRANGE32
, 0, maxRange
);
887 /******************************************************************************
890 * This method will calculate the position rectangles of all the items in the
891 * control. The rectangle calculated starts at 0 for the first item in the
892 * list and ignores scrolling and selection.
893 * It also uses the current font to determine the height of the tab row and
894 * it checks if all the tabs fit in the client area of the window. If they
895 * dont, a scrolling control is added.
897 static void TAB_SetItemBounds (HWND hwnd
)
899 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
900 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
901 TEXTMETRICA fontMetrics
;
905 HFONT hFont
, hOldFont
;
911 * We need to get text information so we need a DC and we need to select
916 hFont
= infoPtr
->hFont
? infoPtr
->hFont
: GetStockObject (SYSTEM_FONT
);
917 hOldFont
= SelectObject (hdc
, hFont
);
920 * We will base the rectangle calculations on the client rectangle
923 GetClientRect(hwnd
, &clientRect
);
926 * The leftmost item will be "0" aligned
931 if ( !(lStyle
& TCS_FIXEDWIDTH
) && !((lStyle
& TCS_OWNERDRAWFIXED
) && infoPtr
->fSizeSet
) )
937 * Use the current font to determine the height of a tab.
939 GetTextMetricsA(hdc
, &fontMetrics
);
942 * Get the icon height
945 ImageList_GetIconSize(infoPtr
->himl
, 0, &icon_height
);
948 * Take the highest between font or icon
950 if (fontMetrics
.tmHeight
> icon_height
)
951 item_height
= fontMetrics
.tmHeight
;
953 item_height
= icon_height
;
956 * Make sure there is enough space for the letters + icon + growing the
957 * selected item + extra space for the selected item.
959 infoPtr
->tabHeight
= item_height
+ 2*VERTICAL_ITEM_PADDING
+
963 for (curItem
= 0; curItem
< infoPtr
->uNumItem
; curItem
++)
966 * Set the leftmost position of the tab.
968 infoPtr
->items
[curItem
].rect
.left
= curItemLeftPos
;
970 if ( (lStyle
& TCS_FIXEDWIDTH
) || ((lStyle
& TCS_OWNERDRAWFIXED
) && infoPtr
->fSizeSet
))
972 infoPtr
->items
[curItem
].rect
.right
= infoPtr
->items
[curItem
].rect
.left
+
974 2*HORIZONTAL_ITEM_PADDING
;
982 * Calculate how wide the tab is depending on the text it contains
984 GetTextExtentPoint32A(hdc
, infoPtr
->items
[curItem
].pszText
,
985 lstrlenA(infoPtr
->items
[curItem
].pszText
), &size
);
992 ImageList_GetIconSize(infoPtr
->himl
, &icon_width
, 0);
996 infoPtr
->items
[curItem
].rect
.right
= infoPtr
->items
[curItem
].rect
.left
+
997 size
.cx
+ icon_width
+
998 num
*HORIZONTAL_ITEM_PADDING
;
1002 * Check if this is a multiline tab control and if so
1003 * check to see if we should wrap the tabs
1005 * Because we are going to arange all these tabs evenly
1006 * really we are basically just counting rows at this point
1010 if ((lStyle
& TCS_MULTILINE
)&&
1011 (infoPtr
->items
[curItem
].rect
.right
> clientRect
.right
))
1013 infoPtr
->items
[curItem
].rect
.right
-=
1014 infoPtr
->items
[curItem
].rect
.left
;
1015 infoPtr
->items
[curItem
].rect
.left
= 0;
1019 infoPtr
->items
[curItem
].rect
.bottom
= 0;
1020 infoPtr
->items
[curItem
].rect
.top
= curItemRowCount
;
1022 TRACE("TextSize: %i\n ", size
.cx
);
1023 TRACE("Rect: T %i, L %i, B %i, R %i\n",
1024 infoPtr
->items
[curItem
].rect
.top
,
1025 infoPtr
->items
[curItem
].rect
.left
,
1026 infoPtr
->items
[curItem
].rect
.bottom
,
1027 infoPtr
->items
[curItem
].rect
.right
);
1030 * The leftmost position of the next item is the rightmost position
1033 if (lStyle
& TCS_BUTTONS
)
1034 curItemLeftPos
= infoPtr
->items
[curItem
].rect
.right
+ BUTTON_SPACINGX
;
1036 curItemLeftPos
= infoPtr
->items
[curItem
].rect
.right
;
1039 if (!(lStyle
& TCS_MULTILINE
))
1042 * Check if we need a scrolling control.
1044 infoPtr
->needsScrolling
= (curItemLeftPos
+ (2*SELECTED_TAB_OFFSET
) >
1047 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1048 if(!infoPtr
->needsScrolling
)
1049 infoPtr
->leftmostVisible
= 0;
1051 TAB_SetupScrolling(hwnd
, infoPtr
, &clientRect
);
1055 * Set the number of rows
1058 infoPtr
->uNumRows
= curItemRowCount
;
1060 if ((lStyle
& TCS_MULTILINE
)&&(infoPtr
->uNumItem
> 0))
1062 INT widthDiff
,remainder
;
1063 INT tabPerRow
,remTab
;
1065 INT iIndexStart
=0,iIndexEnd
=0, iCount
=0;
1068 * Ok Microsoft trys to even out the rows. place the same
1069 * number of tabs in each row. So lets give that a shot
1073 tabPerRow
= infoPtr
->uNumItem
/ (infoPtr
->uNumRows
+ 1);
1074 remTab
= infoPtr
->uNumItem
% (infoPtr
->uNumRows
+ 1);
1076 for (iItm
=0,iRow
=0,iCount
=0,curItemLeftPos
=0;
1077 iItm
<infoPtr
->uNumItem
;
1080 if (iCount
>= ((iRow
<remTab
)?tabPerRow
+1:tabPerRow
))
1087 * normalize the current rect
1089 infoPtr
->items
[iItm
].rect
.right
-=
1090 infoPtr
->items
[iItm
].rect
.left
;
1091 infoPtr
->items
[iItm
].rect
.left
= 0;
1093 infoPtr
->items
[iItm
].rect
.top
= iRow
;
1094 infoPtr
->items
[iItm
].rect
.left
+=curItemLeftPos
;
1095 infoPtr
->items
[iItm
].rect
.right
+=curItemLeftPos
;
1096 if (lStyle
& TCS_BUTTONS
)
1097 curItemLeftPos
= infoPtr
->items
[iItm
].rect
.right
+
1100 curItemLeftPos
= infoPtr
->items
[iItm
].rect
.right
;
1108 while(iIndexStart
< infoPtr
->uNumItem
)
1111 * find the indexs of the row
1113 for (iIndexEnd
=iIndexStart
;
1114 (iIndexEnd
< infoPtr
->uNumItem
) &&
1115 (infoPtr
->items
[iIndexEnd
].rect
.top
==
1116 infoPtr
->items
[iIndexStart
].rect
.top
) ;
1118 /* intentionaly blank */;
1121 * we need to justify these tabs so they fill the whole given
1125 widthDiff
= clientRect
.right
- (2*SELECTED_TAB_OFFSET
) -
1126 infoPtr
->items
[iIndexEnd
-1].rect
.right
;
1128 iCount
= iIndexEnd
-iIndexStart
;
1133 remainder
= widthDiff
% iCount
;
1134 widthDiff
= widthDiff
/ iCount
;
1135 for (iIndex
=iIndexStart
,iCount
=0; iIndex
< iIndexEnd
;
1138 infoPtr
->items
[iIndex
].rect
.left
+=iCount
*widthDiff
;
1139 infoPtr
->items
[iIndex
].rect
.right
+=(iCount
+1)*widthDiff
;
1141 infoPtr
->items
[iIndex
-1].rect
.right
+= remainder
;
1144 iIndexStart
=iIndexEnd
;
1149 TAB_EnsureSelectionVisible(hwnd
,infoPtr
);
1150 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
1154 SelectObject (hdc
, hOldFont
);
1155 ReleaseDC (hwnd
, hdc
);
1158 /******************************************************************************
1159 * TAB_DrawItemInterior
1161 * This method is used to draw the interior (text and icon) of a single tab
1162 * into the tab control.
1165 TAB_DrawItemInterior
1173 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
1174 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1178 HPEN htextPen
= GetSysColorPen (COLOR_BTNTEXT
);
1182 if (drawRect
== NULL
)
1189 * Get the rectangle for the item.
1191 isVisible
= TAB_InternalGetItemRect
1203 * Make sure drawRect points to something valid; simplifies code.
1205 drawRect
= &localRect
;
1208 * This logic copied from the part of TAB_DrawItem which draws
1209 * the tab background. It's important to keep it in sync. I
1210 * would have liked to avoid code duplication, but couldn't figure
1211 * out how without making spaghetti of TAB_DrawItem.
1213 if (lStyle
& TCS_BUTTONS
)
1215 *drawRect
= itemRect
;
1216 if (iItem
== infoPtr
->iSelected
)
1224 if (iItem
== infoPtr
->iSelected
)
1225 *drawRect
= selectedRect
;
1227 *drawRect
= itemRect
;
1236 holdPen
= SelectObject(hdc
, htextPen
);
1238 oldBkMode
= SetBkMode(hdc
, TRANSPARENT
);
1244 (iItem
== infoPtr
->iHotTracked
) ? COLOR_HIGHLIGHT
: COLOR_BTNTEXT
1249 * Deflate the rectangle to acount for the padding
1251 InflateRect(drawRect
, -HORIZONTAL_ITEM_PADDING
, -VERTICAL_ITEM_PADDING
);
1254 * if owner draw, tell the owner to draw
1256 if ( (lStyle
& TCS_OWNERDRAWFIXED
) && GetParent(hwnd
) )
1262 * get the control id
1264 id
= GetWindowLongA( hwnd
, GWL_ID
);
1267 * put together the DRAWITEMSTRUCT
1269 dis
.CtlType
= ODT_TAB
;
1272 dis
.itemAction
= ODA_DRAWENTIRE
;
1273 if ( iItem
== infoPtr
->iSelected
)
1274 dis
.itemState
= ODS_SELECTED
;
1277 dis
.hwndItem
= hwnd
; /* */
1279 dis
.rcItem
= *drawRect
; /* */
1280 dis
.itemData
= infoPtr
->items
[iItem
].lParam
;
1283 * send the draw message
1285 SendMessageA( GetParent(hwnd
), WM_DRAWITEM
, (WPARAM
)id
, (LPARAM
)&dis
);
1292 * If not owner draw, then do the drawing ourselves.
1296 if (infoPtr
->himl
&& (infoPtr
->items
[iItem
].mask
& TCIF_IMAGE
))
1304 infoPtr
->items
[iItem
].iImage
,
1310 ImageList_GetIconSize(infoPtr
->himl
, &cx
, &cy
);
1311 drawRect
->left
+= (cx
+ HORIZONTAL_ITEM_PADDING
);
1317 if (lStyle
& TCS_RIGHTJUSTIFY
)
1318 uHorizAlign
= DT_CENTER
;
1320 uHorizAlign
= DT_LEFT
;
1325 infoPtr
->items
[iItem
].pszText
,
1326 lstrlenA(infoPtr
->items
[iItem
].pszText
),
1328 uHorizAlign
| DT_SINGLELINE
| DT_VCENTER
1335 SetBkMode(hdc
, oldBkMode
);
1336 SelectObject(hdc
, holdPen
);
1339 /******************************************************************************
1342 * This method is used to draw a single tab into the tab control.
1344 static void TAB_DrawItem(
1349 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1350 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1357 * Get the rectangle for the item.
1359 isVisible
= TAB_InternalGetItemRect(hwnd
,
1367 HBRUSH hbr
= CreateSolidBrush (GetSysColor(COLOR_BTNFACE
));
1368 HPEN hwPen
= GetSysColorPen (COLOR_3DHILIGHT
);
1369 HPEN hbPen
= GetSysColorPen (COLOR_BTNSHADOW
);
1370 HPEN hfocusPen
= CreatePen(PS_DOT
, 1, GetSysColor(COLOR_BTNTEXT
));
1373 BOOL deleteBrush
= TRUE
;
1375 if (lStyle
& TCS_BUTTONS
)
1378 * Get item rectangle.
1382 holdPen
= SelectObject (hdc
, hwPen
);
1384 if (iItem
== infoPtr
->iSelected
)
1389 if (!((lStyle
& TCS_OWNERDRAWFIXED
) && infoPtr
->fSizeSet
))
1391 COLORREF bk
= GetSysColor(COLOR_3DHILIGHT
);
1393 hbr
= GetSysColorBrush(COLOR_SCROLLBAR
);
1394 SetTextColor(hdc
, GetSysColor(COLOR_3DFACE
));
1395 SetBkColor(hdc
, bk
);
1397 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1398 * we better use 0x55aa bitmap brush to make scrollbar's background
1399 * look different from the window background.
1401 if (bk
== GetSysColor(COLOR_WINDOW
))
1402 hbr
= CACHE_GetPattern55AABrush();
1404 deleteBrush
= FALSE
;
1408 * Erase the background.
1410 FillRect(hdc
, &r
, hbr
);
1414 * The rectangles calculated exclude the right and bottom
1415 * borders of the rectangle. To simply the following code, those
1416 * borders are shaved-off beforehand.
1422 MoveToEx (hdc
, r
.left
, r
.bottom
, NULL
);
1423 LineTo (hdc
, r
.right
, r
.bottom
);
1424 LineTo (hdc
, r
.right
, r
.top
);
1427 SelectObject(hdc
, hbPen
);
1428 LineTo (hdc
, r
.left
, r
.top
);
1429 LineTo (hdc
, r
.left
, r
.bottom
);
1434 * Erase the background.
1436 FillRect(hdc
, &r
, hbr
);
1439 MoveToEx (hdc
, r
.left
, r
.bottom
, NULL
);
1440 LineTo (hdc
, r
.left
, r
.top
);
1441 LineTo (hdc
, r
.right
, r
.top
);
1444 SelectObject(hdc
, hbPen
);
1445 LineTo (hdc
, r
.right
, r
.bottom
);
1446 LineTo (hdc
, r
.left
, r
.bottom
);
1455 hbr
= CreateSolidBrush(GetSysColor(COLOR_BTNFACE
));
1458 * We draw a rectangle of different sizes depending on the selection
1461 if (iItem
== infoPtr
->iSelected
)
1467 * Erase the background.
1468 * This is necessary when drawing the selected item since it is larger
1469 * than the others, it might overlap with stuff already drawn by the
1472 FillRect(hdc
, &r
, hbr
);
1476 * The rectangles calculated exclude the right and bottom
1477 * borders of the rectangle. To simply the following code, those
1478 * borders are shaved-off beforehand.
1483 holdPen
= SelectObject (hdc
, hwPen
);
1485 if (lStyle
& TCS_BOTTOM
)
1488 MoveToEx (hdc
, r
.left
, r
.top
, NULL
);
1489 LineTo (hdc
, r
.left
, r
.bottom
- ROUND_CORNER_SIZE
);
1490 LineTo (hdc
, r
.left
+ ROUND_CORNER_SIZE
, r
.bottom
);
1493 SelectObject(hdc
, hbPen
);
1494 LineTo (hdc
, r
.right
- ROUND_CORNER_SIZE
, r
.bottom
);
1495 LineTo (hdc
, r
.right
, r
.bottom
- ROUND_CORNER_SIZE
);
1496 LineTo (hdc
, r
.right
, r
.top
);
1501 MoveToEx (hdc
, r
.left
, r
.bottom
, NULL
);
1502 LineTo (hdc
, r
.left
, r
.top
+ ROUND_CORNER_SIZE
);
1503 LineTo (hdc
, r
.left
+ ROUND_CORNER_SIZE
, r
.top
);
1504 LineTo (hdc
, r
.right
- ROUND_CORNER_SIZE
, r
.top
);
1507 SelectObject(hdc
, hbPen
);
1508 LineTo (hdc
, r
.right
, r
.top
+ ROUND_CORNER_SIZE
);
1509 LineTo (hdc
, r
.right
, r
.bottom
);
1513 oldBkMode
= SetBkMode(hdc
, TRANSPARENT
);
1515 /* This modifies r to be the text rectangle. */
1516 TAB_DrawItemInterior(hwnd
, hdc
, iItem
, &r
);
1519 * Draw the focus rectangle
1521 if (((lStyle
& TCS_FOCUSNEVER
) == 0) &&
1522 (GetFocus() == hwnd
) &&
1523 (iItem
== infoPtr
->uFocus
) )
1525 InflateRect(&r
, FOCUS_RECT_HOFFSET
, FOCUS_RECT_VOFFSET
);
1527 SelectObject(hdc
, hfocusPen
);
1529 MoveToEx (hdc
, r
.left
, r
.top
, NULL
);
1530 LineTo (hdc
, r
.right
-1, r
.top
);
1531 LineTo (hdc
, r
.right
-1, r
.bottom
-1);
1532 LineTo (hdc
, r
.left
, r
.bottom
-1);
1533 LineTo (hdc
, r
.left
, r
.top
);
1539 SetBkMode(hdc
, oldBkMode
);
1540 SelectObject(hdc
, holdPen
);
1541 DeleteObject(hfocusPen
);
1542 if (deleteBrush
) DeleteObject(hbr
);
1546 /******************************************************************************
1549 * This method is used to draw the raised border around the tab control
1552 static void TAB_DrawBorder (HWND hwnd
, HDC hdc
)
1554 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1556 HPEN hwPen
= GetSysColorPen (COLOR_3DHILIGHT
);
1557 HPEN hbPen
= GetSysColorPen (COLOR_3DDKSHADOW
);
1558 HPEN hShade
= GetSysColorPen (COLOR_BTNSHADOW
);
1561 GetClientRect (hwnd
, &rect
);
1564 * Adjust for the style
1566 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
1568 rect
.bottom
-= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
1572 rect
.top
+= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
1576 * Shave-off the right and bottom margins (exluded in the
1583 htmPen
= SelectObject (hdc
, hwPen
);
1585 MoveToEx (hdc
, rect
.left
, rect
.bottom
, NULL
);
1586 LineTo (hdc
, rect
.left
, rect
.top
);
1587 LineTo (hdc
, rect
.right
, rect
.top
);
1590 SelectObject (hdc
, hbPen
);
1591 LineTo (hdc
, rect
.right
, rect
.bottom
);
1592 LineTo (hdc
, rect
.left
, rect
.bottom
);
1595 SelectObject (hdc
, hShade
);
1596 MoveToEx (hdc
, rect
.right
-1, rect
.top
, NULL
);
1597 LineTo (hdc
, rect
.right
-1, rect
.bottom
-1);
1598 LineTo (hdc
, rect
.left
, rect
.bottom
-1);
1600 SelectObject(hdc
, htmPen
);
1603 /******************************************************************************
1606 * This method repaints the tab control..
1608 static void TAB_Refresh (HWND hwnd
, HDC hdc
)
1610 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1614 if (!infoPtr
->DoRedraw
)
1617 hOldFont
= SelectObject (hdc
, infoPtr
->hFont
);
1619 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BUTTONS
)
1621 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
1623 TAB_DrawItem (hwnd
, hdc
, i
);
1629 * Draw all the non selected item first.
1631 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
1633 if (i
!= infoPtr
->iSelected
)
1634 TAB_DrawItem (hwnd
, hdc
, i
);
1638 * Now, draw the border, draw it before the selected item
1639 * since the selected item overwrites part of the border.
1641 TAB_DrawBorder (hwnd
, hdc
);
1644 * Then, draw the selected item
1646 TAB_DrawItem (hwnd
, hdc
, infoPtr
->iSelected
);
1649 * If we haven't set the current focus yet, set it now.
1650 * Only happens when we first paint the tab controls.
1652 if (infoPtr
->uFocus
== -1)
1653 TAB_SetCurFocus(hwnd
, infoPtr
->iSelected
);
1656 SelectObject (hdc
, hOldFont
);
1660 TAB_SetRedraw (HWND hwnd
, WPARAM wParam
)
1662 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1664 infoPtr
->DoRedraw
=(BOOL
) wParam
;
1668 static LRESULT
TAB_EraseBackground(
1675 HBRUSH brush
= CreateSolidBrush(GetSysColor(COLOR_BTNFACE
));
1677 hdc
= givenDC
? givenDC
: GetDC(hwnd
);
1679 GetClientRect(hwnd
, &clientRect
);
1681 FillRect(hdc
, &clientRect
, brush
);
1684 ReleaseDC(hwnd
, hdc
);
1686 DeleteObject(brush
);
1691 /******************************************************************************
1692 * TAB_EnsureSelectionVisible
1694 * This method will make sure that the current selection is completely
1695 * visible by scrolling until it is.
1697 static void TAB_EnsureSelectionVisible(
1701 INT iSelected
= infoPtr
->iSelected
;
1703 INT iOrigLeftmostVisible
= infoPtr
->leftmostVisible
;
1706 * set the items row to the bottommost row or topmost row depending on
1710 if (infoPtr
->uNumRows
> 0)
1712 INT newselected
=infoPtr
->items
[iSelected
].rect
.top
;
1714 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1716 if (lStyle
& TCS_BOTTOM
)
1719 iTargetRow
= infoPtr
->uNumRows
;
1721 if (newselected
!= iTargetRow
)
1724 for (i
=0; i
< infoPtr
->uNumItem
; i
++)
1725 if (infoPtr
->items
[i
].rect
.top
== newselected
)
1726 infoPtr
->items
[i
].rect
.top
= iTargetRow
;
1727 else if (lStyle
&TCS_BOTTOM
)
1729 if (infoPtr
->items
[i
].rect
.top
< newselected
)
1730 infoPtr
->items
[i
].rect
.top
+=1;
1734 if (infoPtr
->items
[i
].rect
.top
> newselected
)
1735 infoPtr
->items
[i
].rect
.top
-=1;
1738 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
1743 * Do the trivial cases first.
1745 if ( (!infoPtr
->needsScrolling
) ||
1746 (infoPtr
->hwndUpDown
==0) )
1749 if (infoPtr
->leftmostVisible
>= iSelected
)
1751 infoPtr
->leftmostVisible
= iSelected
;
1758 * Calculate the part of the client area that is visible.
1760 GetClientRect(hwnd
, &r
);
1763 GetClientRect(infoPtr
->hwndUpDown
, &r
);
1766 if ((infoPtr
->items
[iSelected
].rect
.right
-
1767 infoPtr
->items
[iSelected
].rect
.left
) >= width
)
1769 /* Special case: width of selected item is greater than visible
1772 infoPtr
->leftmostVisible
= iSelected
;
1776 for (i
= infoPtr
->leftmostVisible
; i
< infoPtr
->uNumItem
; i
++)
1778 if ((infoPtr
->items
[iSelected
].rect
.right
-
1779 infoPtr
->items
[i
].rect
.left
) < width
)
1782 infoPtr
->leftmostVisible
= i
;
1786 if (infoPtr
->leftmostVisible
!= iOrigLeftmostVisible
)
1787 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
1789 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
1790 MAKELONG(infoPtr
->leftmostVisible
, 0));
1793 /******************************************************************************
1794 * TAB_InvalidateTabArea
1796 * This method will invalidate the portion of the control that contains the
1797 * tabs. It is called when the state of the control changes and needs
1800 static void TAB_InvalidateTabArea(
1806 GetClientRect(hwnd
, &clientRect
);
1808 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BOTTOM
)
1810 clientRect
.top
= clientRect
.bottom
- (infoPtr
->tabHeight
*
1811 (infoPtr
->uNumRows
+ 1) + 3);
1815 clientRect
.bottom
= clientRect
.top
+ (infoPtr
->tabHeight
*
1816 (infoPtr
->uNumRows
+ 1) + 1);
1819 InvalidateRect(hwnd
, &clientRect
, TRUE
);
1823 TAB_Paint (HWND hwnd
, WPARAM wParam
)
1828 hdc
= wParam
== 0 ? BeginPaint (hwnd
, &ps
) : (HDC
)wParam
;
1829 TAB_Refresh (hwnd
, hdc
);
1832 EndPaint (hwnd
, &ps
);
1838 TAB_InsertItem (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1840 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1845 GetClientRect (hwnd
, &rect
);
1846 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd
,
1847 rect
.top
, rect
.left
, rect
.bottom
, rect
.right
);
1849 pti
= (TCITEMA
*)lParam
;
1850 iItem
= (INT
)wParam
;
1852 if (iItem
< 0) return -1;
1853 if (iItem
> infoPtr
->uNumItem
)
1854 iItem
= infoPtr
->uNumItem
;
1856 if (infoPtr
->uNumItem
== 0) {
1857 infoPtr
->items
= COMCTL32_Alloc (sizeof (TAB_ITEM
));
1858 infoPtr
->uNumItem
++;
1859 infoPtr
->iSelected
= 0;
1862 TAB_ITEM
*oldItems
= infoPtr
->items
;
1864 infoPtr
->uNumItem
++;
1865 infoPtr
->items
= COMCTL32_Alloc (sizeof (TAB_ITEM
) * infoPtr
->uNumItem
);
1867 /* pre insert copy */
1869 memcpy (&infoPtr
->items
[0], &oldItems
[0],
1870 iItem
* sizeof(TAB_ITEM
));
1873 /* post insert copy */
1874 if (iItem
< infoPtr
->uNumItem
- 1) {
1875 memcpy (&infoPtr
->items
[iItem
+1], &oldItems
[iItem
],
1876 (infoPtr
->uNumItem
- iItem
- 1) * sizeof(TAB_ITEM
));
1880 if (iItem
<= infoPtr
->iSelected
)
1881 infoPtr
->iSelected
++;
1883 COMCTL32_Free (oldItems
);
1886 infoPtr
->items
[iItem
].mask
= pti
->mask
;
1887 if (pti
->mask
& TCIF_TEXT
) {
1888 len
= lstrlenA (pti
->pszText
);
1889 infoPtr
->items
[iItem
].pszText
= COMCTL32_Alloc (len
+1);
1890 lstrcpyA (infoPtr
->items
[iItem
].pszText
, pti
->pszText
);
1891 infoPtr
->items
[iItem
].cchTextMax
= pti
->cchTextMax
;
1894 if (pti
->mask
& TCIF_IMAGE
)
1895 infoPtr
->items
[iItem
].iImage
= pti
->iImage
;
1897 if (pti
->mask
& TCIF_PARAM
)
1898 infoPtr
->items
[iItem
].lParam
= pti
->lParam
;
1900 TAB_SetItemBounds(hwnd
);
1901 TAB_InvalidateTabArea(hwnd
, infoPtr
);
1903 TRACE("[%04x]: added item %d '%s'\n",
1904 hwnd
, iItem
, infoPtr
->items
[iItem
].pszText
);
1910 TAB_SetItemSize (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1912 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1913 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1916 if ((lStyle
& TCS_FIXEDWIDTH
) || (lStyle
& TCS_OWNERDRAWFIXED
))
1918 lResult
= MAKELONG(infoPtr
->tabWidth
, infoPtr
->tabHeight
);
1919 infoPtr
->tabWidth
= (INT
)LOWORD(lParam
);
1920 infoPtr
->tabHeight
= (INT
)HIWORD(lParam
);
1922 infoPtr
->fSizeSet
= TRUE
;
1928 TAB_SetItemA (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1930 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1936 tabItem
=(LPTCITEMA
) lParam
;
1937 TRACE("%d %p\n",iItem
, tabItem
);
1938 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
)) return FALSE
;
1940 wineItem
=& infoPtr
->items
[iItem
];
1942 if (tabItem
->mask
& TCIF_IMAGE
)
1943 wineItem
->iImage
=tabItem
->iImage
;
1945 if (tabItem
->mask
& TCIF_PARAM
)
1946 wineItem
->lParam
=tabItem
->lParam
;
1948 if (tabItem
->mask
& TCIF_RTLREADING
)
1949 FIXME("TCIF_RTLREADING\n");
1951 if (tabItem
->mask
& TCIF_STATE
)
1952 wineItem
->dwState
=tabItem
->dwState
;
1954 if (tabItem
->mask
& TCIF_TEXT
) {
1955 len
=lstrlenA (tabItem
->pszText
);
1956 if (len
>wineItem
->cchTextMax
)
1957 wineItem
->pszText
= COMCTL32_ReAlloc (wineItem
->pszText
, len
+1);
1958 lstrcpyA (wineItem
->pszText
, tabItem
->pszText
);
1962 * Update and repaint tabs.
1964 TAB_SetItemBounds(hwnd
);
1965 TAB_InvalidateTabArea(hwnd
,infoPtr
);
1971 TAB_GetItemCount (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1973 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1975 return infoPtr
->uNumItem
;
1980 TAB_GetItemA (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1982 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1988 tabItem
=(LPTCITEMA
) lParam
;
1990 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
)) return FALSE
;
1992 wineItem
=& infoPtr
->items
[iItem
];
1994 if (tabItem
->mask
& TCIF_IMAGE
)
1995 tabItem
->iImage
=wineItem
->iImage
;
1997 if (tabItem
->mask
& TCIF_PARAM
)
1998 tabItem
->lParam
=wineItem
->lParam
;
2000 if (tabItem
->mask
& TCIF_RTLREADING
)
2001 FIXME("TCIF_RTLREADING\n");
2003 if (tabItem
->mask
& TCIF_STATE
)
2004 tabItem
->dwState
=wineItem
->dwState
;
2006 if (tabItem
->mask
& TCIF_TEXT
)
2007 lstrcpynA (tabItem
->pszText
, wineItem
->pszText
, tabItem
->cchTextMax
);
2013 TAB_DeleteItem (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2015 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2016 INT iItem
= (INT
) wParam
;
2017 BOOL bResult
= FALSE
;
2019 if ((iItem
>= 0) && (iItem
< infoPtr
->uNumItem
))
2021 TAB_ITEM
*oldItems
= infoPtr
->items
;
2023 infoPtr
->uNumItem
--;
2024 infoPtr
->items
= COMCTL32_Alloc(sizeof (TAB_ITEM
) * infoPtr
->uNumItem
);
2027 memcpy(&infoPtr
->items
[0], &oldItems
[0], iItem
* sizeof(TAB_ITEM
));
2029 if (iItem
< infoPtr
->uNumItem
)
2030 memcpy(&infoPtr
->items
[iItem
], &oldItems
[iItem
+ 1],
2031 (infoPtr
->uNumItem
- iItem
) * sizeof(TAB_ITEM
));
2033 COMCTL32_Free (oldItems
);
2036 * Readjust the selected index.
2038 if ((iItem
== infoPtr
->iSelected
) && (iItem
> 0))
2039 infoPtr
->iSelected
--;
2041 if (iItem
< infoPtr
->iSelected
)
2042 infoPtr
->iSelected
--;
2044 if (infoPtr
->uNumItem
== 0)
2045 infoPtr
->iSelected
= -1;
2048 * Reposition and repaint tabs.
2050 TAB_SetItemBounds(hwnd
);
2051 TAB_InvalidateTabArea(hwnd
,infoPtr
);
2060 TAB_DeleteAllItems (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2062 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2064 COMCTL32_Free (infoPtr
->items
);
2065 infoPtr
->uNumItem
= 0;
2066 infoPtr
->iSelected
= -1;
2067 if (infoPtr
->iHotTracked
>= 0)
2068 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
2069 infoPtr
->iHotTracked
= -1;
2071 TAB_SetItemBounds(hwnd
);
2072 TAB_InvalidateTabArea(hwnd
,infoPtr
);
2078 TAB_GetFont (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2080 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2083 return (LRESULT
)infoPtr
->hFont
;
2087 TAB_SetFont (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2090 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2092 TRACE("%x %lx\n",wParam
, lParam
);
2094 infoPtr
->hFont
= (HFONT
)wParam
;
2096 TAB_SetItemBounds(hwnd
);
2098 TAB_InvalidateTabArea(hwnd
, infoPtr
);
2105 TAB_GetImageList (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2107 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2110 return (LRESULT
)infoPtr
->himl
;
2114 TAB_SetImageList (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2116 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2117 HIMAGELIST himlPrev
;
2120 himlPrev
= infoPtr
->himl
;
2121 infoPtr
->himl
= (HIMAGELIST
)lParam
;
2122 return (LRESULT
)himlPrev
;
2127 TAB_Size (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2130 /* I'm not really sure what the following code was meant to do.
2131 This is what it is doing:
2132 When WM_SIZE is sent with SIZE_RESTORED, the control
2133 gets positioned in the top left corner.
2137 UINT uPosFlags,cx,cy;
2141 parent = GetParent (hwnd);
2142 GetClientRect(parent, &parent_rect);
2145 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2146 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2148 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2149 cx, cy, uPosFlags | SWP_NOZORDER);
2151 FIXME (tab,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2155 * Recompute the size/position of the tabs.
2157 TAB_SetItemBounds (hwnd
);
2160 * Force a repaint of the control.
2162 InvalidateRect(hwnd
, NULL
, TRUE
);
2169 TAB_Create (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2172 TEXTMETRICA fontMetrics
;
2177 infoPtr
= (TAB_INFO
*)COMCTL32_Alloc (sizeof(TAB_INFO
));
2179 SetWindowLongA(hwnd
, 0, (DWORD
)infoPtr
);
2181 infoPtr
->uNumItem
= 0;
2182 infoPtr
->uNumRows
= 0;
2185 infoPtr
->hcurArrow
= LoadCursorA (0, IDC_ARROWA
);
2186 infoPtr
->iSelected
= -1;
2187 infoPtr
->iHotTracked
= -1;
2188 infoPtr
->uFocus
= -1;
2189 infoPtr
->hwndToolTip
= 0;
2190 infoPtr
->DoRedraw
= TRUE
;
2191 infoPtr
->needsScrolling
= FALSE
;
2192 infoPtr
->hwndUpDown
= 0;
2193 infoPtr
->leftmostVisible
= 0;
2194 infoPtr
->fSizeSet
= FALSE
;
2196 TRACE("Created tab control, hwnd [%04x]\n", hwnd
);
2198 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2199 if you don't specify in CreateWindow. This is necesary in
2200 order for paint to work correctly. This follows windows behaviour. */
2201 dwStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
2202 SetWindowLongA(hwnd
, GWL_STYLE
, dwStyle
|WS_CLIPSIBLINGS
);
2204 if (dwStyle
& TCS_TOOLTIPS
) {
2205 /* Create tooltip control */
2206 infoPtr
->hwndToolTip
=
2207 CreateWindowExA (0, TOOLTIPS_CLASSA
, NULL
, 0,
2208 CW_USEDEFAULT
, CW_USEDEFAULT
,
2209 CW_USEDEFAULT
, CW_USEDEFAULT
,
2212 /* Send NM_TOOLTIPSCREATED notification */
2213 if (infoPtr
->hwndToolTip
) {
2214 NMTOOLTIPSCREATED nmttc
;
2216 nmttc
.hdr
.hwndFrom
= hwnd
;
2217 nmttc
.hdr
.idFrom
= GetWindowLongA(hwnd
, GWL_ID
);
2218 nmttc
.hdr
.code
= NM_TOOLTIPSCREATED
;
2219 nmttc
.hwndToolTips
= infoPtr
->hwndToolTip
;
2221 SendMessageA (GetParent (hwnd
), WM_NOTIFY
,
2222 (WPARAM
)GetWindowLongA(hwnd
, GWL_ID
), (LPARAM
)&nmttc
);
2227 * We need to get text information so we need a DC and we need to select
2231 hOldFont
= SelectObject (hdc
, GetStockObject (SYSTEM_FONT
));
2234 * Use the system font to determine the initial height of a tab.
2236 GetTextMetricsA(hdc
, &fontMetrics
);
2239 * Make sure there is enough space for the letters + growing the
2240 * selected item + extra space for the selected item.
2242 infoPtr
->tabHeight
= fontMetrics
.tmHeight
+ 2*VERTICAL_ITEM_PADDING
+
2243 SELECTED_TAB_OFFSET
;
2246 * Initialize the width of a tab.
2248 infoPtr
->tabWidth
= DEFAULT_TAB_WIDTH
;
2250 SelectObject (hdc
, hOldFont
);
2251 ReleaseDC(hwnd
, hdc
);
2257 TAB_Destroy (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2259 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2265 if (infoPtr
->items
) {
2266 for (iItem
= 0; iItem
< infoPtr
->uNumItem
; iItem
++) {
2267 if (infoPtr
->items
[iItem
].pszText
)
2268 COMCTL32_Free (infoPtr
->items
[iItem
].pszText
);
2270 COMCTL32_Free (infoPtr
->items
);
2273 if (infoPtr
->hwndToolTip
)
2274 DestroyWindow (infoPtr
->hwndToolTip
);
2276 if (infoPtr
->hwndUpDown
)
2277 DestroyWindow(infoPtr
->hwndUpDown
);
2279 if (infoPtr
->iHotTracked
>= 0)
2280 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
2282 COMCTL32_Free (infoPtr
);
2283 SetWindowLongA(hwnd
, 0, 0);
2287 static LRESULT WINAPI
2288 TAB_WindowProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
2291 TRACE("hwnd=%x msg=%x wParam=%x lParam=%lx\n", hwnd
, uMsg
, wParam
, lParam
);
2292 if (!TAB_GetInfoPtr(hwnd
) && (uMsg
!= WM_CREATE
))
2293 return DefWindowProcA (hwnd
, uMsg
, wParam
, lParam
);
2297 case TCM_GETIMAGELIST
:
2298 return TAB_GetImageList (hwnd
, wParam
, lParam
);
2300 case TCM_SETIMAGELIST
:
2301 return TAB_SetImageList (hwnd
, wParam
, lParam
);
2303 case TCM_GETITEMCOUNT
:
2304 return TAB_GetItemCount (hwnd
, wParam
, lParam
);
2307 return TAB_GetItemA (hwnd
, wParam
, lParam
);
2310 FIXME("Unimplemented msg TCM_GETITEMW\n");
2314 return TAB_SetItemA (hwnd
, wParam
, lParam
);
2317 FIXME("Unimplemented msg TCM_SETITEMW\n");
2320 case TCM_DELETEITEM
:
2321 return TAB_DeleteItem (hwnd
, wParam
, lParam
);
2323 case TCM_DELETEALLITEMS
:
2324 return TAB_DeleteAllItems (hwnd
, wParam
, lParam
);
2326 case TCM_GETITEMRECT
:
2327 return TAB_GetItemRect (hwnd
, wParam
, lParam
);
2330 return TAB_GetCurSel (hwnd
);
2333 return TAB_HitTest (hwnd
, wParam
, lParam
);
2336 return TAB_SetCurSel (hwnd
, wParam
);
2338 case TCM_INSERTITEMA
:
2339 return TAB_InsertItem (hwnd
, wParam
, lParam
);
2341 case TCM_INSERTITEMW
:
2342 FIXME("Unimplemented msg TCM_INSERTITEMW\n");
2345 case TCM_SETITEMEXTRA
:
2346 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
2349 case TCM_ADJUSTRECT
:
2350 return TAB_AdjustRect (hwnd
, (BOOL
)wParam
, (LPRECT
)lParam
);
2352 case TCM_SETITEMSIZE
:
2353 return TAB_SetItemSize (hwnd
, wParam
, lParam
);
2355 case TCM_REMOVEIMAGE
:
2356 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
2359 case TCM_SETPADDING
:
2360 FIXME("Unimplemented msg TCM_SETPADDING\n");
2363 case TCM_GETROWCOUNT
:
2364 FIXME("Unimplemented msg TCM_GETROWCOUNT\n");
2367 case TCM_GETUNICODEFORMAT
:
2368 FIXME("Unimplemented msg TCM_GETUNICODEFORMAT\n");
2371 case TCM_SETUNICODEFORMAT
:
2372 FIXME("Unimplemented msg TCM_SETUNICODEFORMAT\n");
2375 case TCM_HIGHLIGHTITEM
:
2376 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
2379 case TCM_GETTOOLTIPS
:
2380 return TAB_GetToolTips (hwnd
, wParam
, lParam
);
2382 case TCM_SETTOOLTIPS
:
2383 return TAB_SetToolTips (hwnd
, wParam
, lParam
);
2385 case TCM_GETCURFOCUS
:
2386 return TAB_GetCurFocus (hwnd
);
2388 case TCM_SETCURFOCUS
:
2389 return TAB_SetCurFocus (hwnd
, wParam
);
2391 case TCM_SETMINTABWIDTH
:
2392 FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
2395 case TCM_DESELECTALL
:
2396 FIXME("Unimplemented msg TCM_DESELECTALL\n");
2399 case TCM_GETEXTENDEDSTYLE
:
2400 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
2403 case TCM_SETEXTENDEDSTYLE
:
2404 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
2408 return TAB_GetFont (hwnd
, wParam
, lParam
);
2411 return TAB_SetFont (hwnd
, wParam
, lParam
);
2414 return TAB_Create (hwnd
, wParam
, lParam
);
2417 return TAB_Destroy (hwnd
, wParam
, lParam
);
2420 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2422 case WM_LBUTTONDOWN
:
2423 return TAB_LButtonDown (hwnd
, wParam
, lParam
);
2426 return TAB_LButtonUp (hwnd
, wParam
, lParam
);
2428 case WM_RBUTTONDOWN
:
2429 return TAB_RButtonDown (hwnd
, wParam
, lParam
);
2432 return TAB_MouseMove (hwnd
, wParam
, lParam
);
2435 return TAB_EraseBackground (hwnd
, (HDC
)wParam
);
2438 return TAB_Paint (hwnd
, wParam
);
2441 return TAB_Size (hwnd
, wParam
, lParam
);
2444 return TAB_SetRedraw (hwnd
, wParam
);
2447 return TAB_OnHScroll(hwnd
, (int)LOWORD(wParam
), (int)HIWORD(wParam
), (HWND
)lParam
);
2449 case WM_STYLECHANGED
:
2450 TAB_SetItemBounds (hwnd
);
2451 InvalidateRect(hwnd
, NULL
, TRUE
);
2456 return TAB_FocusChanging(hwnd
, uMsg
, wParam
, lParam
);
2459 return TAB_KeyUp(hwnd
, wParam
);
2462 if (uMsg
>= WM_USER
)
2463 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
2464 uMsg
, wParam
, lParam
);
2465 return DefWindowProcA (hwnd
, uMsg
, wParam
, lParam
);
2477 ZeroMemory (&wndClass
, sizeof(WNDCLASSA
));
2478 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
;
2479 wndClass
.lpfnWndProc
= (WNDPROC
)TAB_WindowProc
;
2480 wndClass
.cbClsExtra
= 0;
2481 wndClass
.cbWndExtra
= sizeof(TAB_INFO
*);
2482 wndClass
.hCursor
= LoadCursorA (0, IDC_ARROWA
);
2483 wndClass
.hbrBackground
= (HBRUSH
)NULL
;
2484 wndClass
.lpszClassName
= WC_TABCONTROLA
;
2486 RegisterClassA (&wndClass
);
2491 TAB_Unregister (void)
2493 UnregisterClassA (WC_TABCONTROLA
, (HINSTANCE
)NULL
);