4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
10 * Unicode support (under construction)
13 * UpDown control not displayed until after a tab is clicked on
21 #include "debugtools.h"
24 DEFAULT_DEBUG_CHANNEL(tab
);
33 RECT rect
; /* bounding rectangle of the item relative to the
34 * leftmost item (the leftmost item, 0, would have a
35 * "left" member of 0 in this rectangle)
37 * additionally the top member hold the row number
38 * and bottom is unused and should be 0 */
43 UINT uNumItem
; /* number of tab items */
44 UINT uNumRows
; /* number of tab rows */
45 INT tabHeight
; /* height of the tab row */
46 INT tabWidth
; /* width of tabs */
47 HFONT hFont
; /* handle to the current font */
48 HCURSOR hcurArrow
; /* handle to the current cursor */
49 HIMAGELIST himl
; /* handle to a image list (may be 0) */
50 HWND hwndToolTip
; /* handle to tab's tooltip */
51 INT leftmostVisible
; /* Used for scrolling, this member contains
52 * the index of the first visible item */
53 INT iSelected
; /* the currently selected item */
54 INT iHotTracked
; /* the highlighted item under the mouse */
55 INT uFocus
; /* item which has the focus */
56 TAB_ITEM
* items
; /* pointer to an array of TAB_ITEM's */
57 BOOL DoRedraw
; /* flag for redrawing when tab contents is changed*/
58 BOOL needsScrolling
; /* TRUE if the size of the tabs is greater than
59 * the size of the control */
60 BOOL fSizeSet
; /* was the size of the tabs explicitly set? */
61 BOOL bUnicode
; /* Unicode control? */
62 HWND hwndUpDown
; /* Updown control used for scrolling */
65 /******************************************************************************
66 * Positioning constants
68 #define SELECTED_TAB_OFFSET 2
69 #define HORIZONTAL_ITEM_PADDING 5
70 #define VERTICAL_ITEM_PADDING 3
71 #define ROUND_CORNER_SIZE 2
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 4
77 #define BUTTON_SPACINGY 4
78 #define FLAT_BTN_SPACINGX 8
79 #define DEFAULT_TAB_WIDTH 96
81 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
83 /******************************************************************************
84 * Hot-tracking timer constants
86 #define TAB_HOTTRACK_TIMER 1
87 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
89 /******************************************************************************
92 static void TAB_Refresh (HWND hwnd
, HDC hdc
);
93 static void TAB_InvalidateTabArea(HWND hwnd
, TAB_INFO
* infoPtr
);
94 static void TAB_EnsureSelectionVisible(HWND hwnd
, TAB_INFO
* infoPtr
);
95 static void TAB_DrawItem(HWND hwnd
, HDC hdc
, INT iItem
);
96 static void TAB_DrawItemInterior(HWND hwnd
, HDC hdc
, INT iItem
, RECT
* drawRect
);
99 TAB_SendSimpleNotify (HWND hwnd
, UINT code
)
103 nmhdr
.hwndFrom
= hwnd
;
104 nmhdr
.idFrom
= GetWindowLongA(hwnd
, GWL_ID
);
107 return (BOOL
) SendMessageA (GetParent (hwnd
), WM_NOTIFY
,
108 (WPARAM
) nmhdr
.idFrom
, (LPARAM
) &nmhdr
);
112 TAB_RelayEvent (HWND hwndTip
, HWND hwndMsg
, UINT uMsg
,
113 WPARAM wParam
, LPARAM lParam
)
121 msg
.time
= GetMessageTime ();
122 msg
.pt
.x
= LOWORD(GetMessagePos ());
123 msg
.pt
.y
= HIWORD(GetMessagePos ());
125 SendMessageA (hwndTip
, TTM_RELAYEVENT
, 0, (LPARAM
)&msg
);
129 TAB_GetCurSel (HWND hwnd
)
131 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
133 return infoPtr
->iSelected
;
137 TAB_GetCurFocus (HWND hwnd
)
139 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
141 return infoPtr
->uFocus
;
145 TAB_GetToolTips (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
147 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
149 if (infoPtr
== NULL
) return 0;
150 return infoPtr
->hwndToolTip
;
154 TAB_SetCurSel (HWND hwnd
,WPARAM wParam
)
156 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
157 INT iItem
= (INT
)wParam
;
161 if ((iItem
>= 0) && (iItem
< infoPtr
->uNumItem
)) {
162 prevItem
=infoPtr
->iSelected
;
163 infoPtr
->iSelected
=iItem
;
164 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
165 TAB_InvalidateTabArea(hwnd
, infoPtr
);
171 TAB_SetCurFocus (HWND hwnd
,WPARAM wParam
)
173 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
174 INT iItem
=(INT
) wParam
;
176 if ((iItem
< 0) || (iItem
>= infoPtr
->uNumItem
)) return 0;
178 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BUTTONS
) {
179 FIXME("Should set input focus\n");
181 if (infoPtr
->iSelected
!= iItem
|| infoPtr
->uFocus
== -1 ) {
182 infoPtr
->uFocus
= iItem
;
183 if (TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
)!=TRUE
) {
184 infoPtr
->iSelected
= iItem
;
185 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
187 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
188 TAB_InvalidateTabArea(hwnd
, infoPtr
);
196 TAB_SetToolTips (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
198 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
200 if (infoPtr
== NULL
) return 0;
201 infoPtr
->hwndToolTip
= (HWND
)wParam
;
205 /******************************************************************************
206 * TAB_InternalGetItemRect
208 * This method will calculate the rectangle representing a given tab item in
209 * client coordinates. This method takes scrolling into account.
211 * This method returns TRUE if the item is visible in the window and FALSE
212 * if it is completely outside the client area.
214 static BOOL
TAB_InternalGetItemRect(
221 RECT tmpItemRect
,clientRect
;
222 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
224 /* Perform a sanity check and a trivial visibility check. */
225 if ( (infoPtr
->uNumItem
<= 0) ||
226 (itemIndex
>= infoPtr
->uNumItem
) ||
227 (!((lStyle
& TCS_MULTILINE
) || (lStyle
& TCS_VERTICAL
)) && (itemIndex
< infoPtr
->leftmostVisible
)) )
231 * Avoid special cases in this procedure by assigning the "out"
232 * parameters if the caller didn't supply them
234 if (itemRect
== NULL
)
235 itemRect
= &tmpItemRect
;
237 /* Retrieve the unmodified item rect. */
238 *itemRect
= infoPtr
->items
[itemIndex
].rect
;
240 /* calculate the times bottom and top based on the row */
241 GetClientRect(hwnd
, &clientRect
);
243 if ((lStyle
& TCS_BOTTOM
) && !(lStyle
& TCS_VERTICAL
))
245 itemRect
->bottom
= clientRect
.bottom
-
246 SELECTED_TAB_OFFSET
-
247 itemRect
->top
* (infoPtr
->tabHeight
- 2) -
248 ((lStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: 0);
250 itemRect
->top
= clientRect
.bottom
-
252 itemRect
->top
* (infoPtr
->tabHeight
- 2) -
253 ((lStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: 0);
255 else if((lStyle
& TCS_BOTTOM
) && (lStyle
& TCS_VERTICAL
))
257 itemRect
->right
= clientRect
.right
- SELECTED_TAB_OFFSET
- itemRect
->left
* (infoPtr
->tabHeight
- 2) -
258 ((lStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGY
: 0);
259 itemRect
->left
= clientRect
.right
- infoPtr
->tabHeight
- itemRect
->left
* (infoPtr
->tabHeight
- 2) -
260 ((lStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGY
: 0);
262 else if((lStyle
& TCS_VERTICAL
) && !(lStyle
& TCS_BOTTOM
))
264 itemRect
->right
= clientRect
.left
+ infoPtr
->tabHeight
+ itemRect
->left
* (infoPtr
->tabHeight
- 2) +
265 ((lStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGY
: 0);
266 itemRect
->left
= clientRect
.left
+ SELECTED_TAB_OFFSET
+ itemRect
->left
* (infoPtr
->tabHeight
- 2) +
267 ((lStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGY
: 0);
269 else if(!(lStyle
& TCS_VERTICAL
) && !(lStyle
& TCS_BOTTOM
)) /* not TCS_BOTTOM and not TCS_VERTICAL */
271 itemRect
->bottom
= clientRect
.top
+
273 itemRect
->top
* (infoPtr
->tabHeight
- 2) +
274 ((lStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: 0);
275 itemRect
->top
= clientRect
.top
+
276 SELECTED_TAB_OFFSET
+
277 itemRect
->top
* (infoPtr
->tabHeight
- 2) +
278 ((lStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: 0);
282 * "scroll" it to make sure the item at the very left of the
283 * tab control is the leftmost visible tab.
285 if(lStyle
& TCS_VERTICAL
)
289 -(clientRect
.bottom
- infoPtr
->items
[infoPtr
->leftmostVisible
].rect
.bottom
));
292 * Move the rectangle so the first item is slightly offset from
293 * the bottom of the tab control.
297 -SELECTED_TAB_OFFSET
);
302 -infoPtr
->items
[infoPtr
->leftmostVisible
].rect
.left
,
306 * Move the rectangle so the first item is slightly offset from
307 * the left of the tab control.
314 /* Now, calculate the position of the item as if it were selected. */
315 if (selectedRect
!=NULL
)
317 CopyRect(selectedRect
, itemRect
);
319 /* The rectangle of a selected item is a bit wider. */
320 if(lStyle
& TCS_VERTICAL
)
321 InflateRect(selectedRect
, 0, SELECTED_TAB_OFFSET
);
323 InflateRect(selectedRect
, SELECTED_TAB_OFFSET
, 0);
325 /* If it also a bit higher. */
326 if ((lStyle
& TCS_BOTTOM
) && !(lStyle
& TCS_VERTICAL
))
328 selectedRect
->top
-= 2; /* the border is thicker on the bottom */
329 selectedRect
->bottom
+= SELECTED_TAB_OFFSET
;
331 else if((lStyle
& TCS_BOTTOM
) && (lStyle
& TCS_VERTICAL
))
333 selectedRect
->left
-= 2; /* the border is thicker on the right */
334 selectedRect
->right
+= SELECTED_TAB_OFFSET
;
336 else if(lStyle
& TCS_VERTICAL
)
338 selectedRect
->left
-= SELECTED_TAB_OFFSET
;
339 selectedRect
->right
+= 1;
343 selectedRect
->top
-= SELECTED_TAB_OFFSET
;
344 selectedRect
->bottom
+= 1;
351 static BOOL
TAB_GetItemRect(HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
353 return TAB_InternalGetItemRect(hwnd
, TAB_GetInfoPtr(hwnd
), (INT
)wParam
,
354 (LPRECT
)lParam
, (LPRECT
)NULL
);
357 /******************************************************************************
360 * This method is called to handle keyboard input
362 static LRESULT
TAB_KeyUp(
366 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
372 newItem
= infoPtr
->uFocus
- 1;
375 newItem
= infoPtr
->uFocus
+ 1;
380 * If we changed to a valid item, change the selection
382 if ((newItem
>= 0) &&
383 (newItem
< infoPtr
->uNumItem
) &&
384 (infoPtr
->uFocus
!= newItem
))
386 if (!TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
))
388 infoPtr
->iSelected
= newItem
;
389 infoPtr
->uFocus
= newItem
;
390 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
392 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
393 TAB_InvalidateTabArea(hwnd
, infoPtr
);
400 /******************************************************************************
403 * This method is called whenever the focus goes in or out of this control
404 * it is used to update the visual state of the control.
406 static LRESULT
TAB_FocusChanging(
412 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
417 * Get the rectangle for the item.
419 isVisible
= TAB_InternalGetItemRect(hwnd
,
426 * If the rectangle is not completely invisible, invalidate that
427 * portion of the window.
431 InvalidateRect(hwnd
, &selectedRect
, TRUE
);
435 * Don't otherwise disturb normal behavior.
437 return DefWindowProcA (hwnd
, uMsg
, wParam
, lParam
);
440 static HWND
TAB_InternalHitTest (
450 for (iCount
= 0; iCount
< infoPtr
->uNumItem
; iCount
++)
452 TAB_InternalGetItemRect(hwnd
, infoPtr
, iCount
, &rect
, NULL
);
454 if (PtInRect(&rect
, pt
))
456 *flags
= TCHT_ONITEM
;
461 *flags
= TCHT_NOWHERE
;
466 TAB_HitTest (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
468 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
469 LPTCHITTESTINFO lptest
= (LPTCHITTESTINFO
) lParam
;
471 return TAB_InternalHitTest (hwnd
, infoPtr
, lptest
->pt
, &lptest
->flags
);
474 /******************************************************************************
477 * Napster v2b5 has a tab control for its main navigation which has a client
478 * area that covers the whole area of the dialog pages.
479 * That's why it receives all msgs for that area and the underlying dialog ctrls
481 * So I decided that we should handle WM_NCHITTEST here and return
482 * HTTRANSPARENT if we don't hit the tab control buttons.
483 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
484 * doesn't do it that way. Maybe depends on tab control styles ?
487 TAB_NCHitTest (HWND hwnd
, LPARAM lParam
)
489 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
493 pt
.x
= LOWORD(lParam
);
494 pt
.y
= HIWORD(lParam
);
495 ScreenToClient(hwnd
, &pt
);
497 if (TAB_InternalHitTest(hwnd
, infoPtr
, pt
, &dummyflag
) == -1)
498 return HTTRANSPARENT
;
504 TAB_LButtonDown (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
506 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
510 if (infoPtr
->hwndToolTip
)
511 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
512 WM_LBUTTONDOWN
, wParam
, lParam
);
514 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_FOCUSONBUTTONDOWN
) {
518 if (infoPtr
->hwndToolTip
)
519 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
520 WM_LBUTTONDOWN
, wParam
, lParam
);
522 pt
.x
= (INT
)LOWORD(lParam
);
523 pt
.y
= (INT
)HIWORD(lParam
);
525 newItem
= TAB_InternalHitTest (hwnd
, infoPtr
, pt
, &dummy
);
527 TRACE("On Tab, item %d\n", newItem
);
529 if ((newItem
!= -1) && (infoPtr
->iSelected
!= newItem
))
531 if (TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
) != TRUE
)
533 infoPtr
->iSelected
= newItem
;
534 infoPtr
->uFocus
= newItem
;
535 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
537 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
539 TAB_InvalidateTabArea(hwnd
, infoPtr
);
546 TAB_LButtonUp (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
548 TAB_SendSimpleNotify(hwnd
, NM_CLICK
);
554 TAB_RButtonDown (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
556 TAB_SendSimpleNotify(hwnd
, NM_RCLICK
);
560 /******************************************************************************
561 * TAB_DrawLoneItemInterior
563 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
564 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
565 * up the device context and font. This routine does the same setup but
566 * only calls TAB_DrawItemInterior for the single specified item.
569 TAB_DrawLoneItemInterior(HWND hwnd
, TAB_INFO
* infoPtr
, int iItem
)
571 HDC hdc
= GetDC(hwnd
);
572 HFONT hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
573 TAB_DrawItemInterior(hwnd
, hdc
, iItem
, NULL
);
574 SelectObject(hdc
, hOldFont
);
575 ReleaseDC(hwnd
, hdc
);
578 /******************************************************************************
579 * TAB_HotTrackTimerProc
581 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
582 * timer is setup so we can check if the mouse is moved out of our window.
583 * (We don't get an event when the mouse leaves, the mouse-move events just
584 * stop being delivered to our window and just start being delivered to
585 * another window.) This function is called when the timer triggers so
586 * we can check if the mouse has left our window. If so, we un-highlight
587 * the hot-tracked tab.
590 TAB_HotTrackTimerProc
592 HWND hwnd
, /* handle of window for timer messages */
593 UINT uMsg
, /* WM_TIMER message */
594 UINT idEvent
, /* timer identifier */
595 DWORD dwTime
/* current system time */
598 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
600 if (infoPtr
!= NULL
&& infoPtr
->iHotTracked
>= 0)
605 ** If we can't get the cursor position, or if the cursor is outside our
606 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
607 ** "outside" even if it is within our bounding rect if another window
608 ** overlaps. Note also that the case where the cursor stayed within our
609 ** window but has moved off the hot-tracked tab will be handled by the
610 ** WM_MOUSEMOVE event.
612 if (!GetCursorPos(&pt
) || WindowFromPoint(pt
) != hwnd
)
614 /* Redraw iHotTracked to look normal */
615 INT iRedraw
= infoPtr
->iHotTracked
;
616 infoPtr
->iHotTracked
= -1;
617 TAB_DrawLoneItemInterior(hwnd
, infoPtr
, iRedraw
);
619 /* Kill this timer */
620 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
625 /******************************************************************************
628 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
629 * should be highlighted. This function determines which tab in a tab control,
630 * if any, is under the mouse and records that information. The caller may
631 * supply output parameters to receive the item number of the tab item which
632 * was highlighted but isn't any longer and of the tab item which is now
633 * highlighted but wasn't previously. The caller can use this information to
634 * selectively redraw those tab items.
636 * If the caller has a mouse position, it can supply it through the pos
637 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
638 * supplies NULL and this function determines the current mouse position
646 int* out_redrawLeave
,
650 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
655 if (out_redrawLeave
!= NULL
)
656 *out_redrawLeave
= -1;
657 if (out_redrawEnter
!= NULL
)
658 *out_redrawEnter
= -1;
660 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_HOTTRACK
)
668 ScreenToClient(hwnd
, &pt
);
676 item
= TAB_InternalHitTest(hwnd
, infoPtr
, pt
, &flags
);
679 if (item
!= infoPtr
->iHotTracked
)
681 if (infoPtr
->iHotTracked
>= 0)
683 /* Mark currently hot-tracked to be redrawn to look normal */
684 if (out_redrawLeave
!= NULL
)
685 *out_redrawLeave
= infoPtr
->iHotTracked
;
689 /* Kill timer which forces recheck of mouse pos */
690 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
695 /* Start timer so we recheck mouse pos */
696 UINT timerID
= SetTimer
700 TAB_HOTTRACK_TIMER_INTERVAL
,
701 TAB_HotTrackTimerProc
705 return; /* Hot tracking not available */
708 infoPtr
->iHotTracked
= item
;
712 /* Mark new hot-tracked to be redrawn to look highlighted */
713 if (out_redrawEnter
!= NULL
)
714 *out_redrawEnter
= item
;
719 /******************************************************************************
722 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
725 TAB_MouseMove (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
730 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
732 if (infoPtr
->hwndToolTip
)
733 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
734 WM_LBUTTONDOWN
, wParam
, lParam
);
736 /* Determine which tab to highlight. Redraw tabs which change highlight
738 TAB_RecalcHotTrack(hwnd
, &lParam
, &redrawLeave
, &redrawEnter
);
740 if (redrawLeave
!= -1)
741 TAB_DrawLoneItemInterior(hwnd
, infoPtr
, redrawLeave
);
742 if (redrawEnter
!= -1)
743 TAB_DrawLoneItemInterior(hwnd
, infoPtr
, redrawEnter
);
748 /******************************************************************************
751 * Calculates the tab control's display area given the window rectangle or
752 * the window rectangle given the requested display rectangle.
754 static LRESULT
TAB_AdjustRect(
759 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
760 DWORD lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
762 if(lStyle
& TCS_VERTICAL
)
764 if (fLarger
) /* Go from display rectangle */
766 /* Add the height of the tabs. */
767 if (lStyle
& TCS_BOTTOM
)
768 prc
->right
+= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
770 prc
->left
-= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
772 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
773 /* Inflate the rectangle for the padding */
774 InflateRect(prc
, DISPLAY_AREA_PADDINGX
, DISPLAY_AREA_PADDINGY
);
776 /* Inflate for the border */
777 InflateRect(prc
, CONTROL_BORDER_SIZEX
, CONTROL_BORDER_SIZEX
);
779 else /* Go from window rectangle. */
781 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
782 /* Deflate the rectangle for the border */
783 InflateRect(prc
, -CONTROL_BORDER_SIZEX
, -CONTROL_BORDER_SIZEX
);
785 /* Deflate the rectangle for the padding */
786 InflateRect(prc
, -DISPLAY_AREA_PADDINGX
, -DISPLAY_AREA_PADDINGY
);
788 /* Remove the height of the tabs. */
789 if (lStyle
& TCS_BOTTOM
)
790 prc
->right
-= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
792 prc
->left
+= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
796 if (fLarger
) /* Go from display rectangle */
798 /* Add the height of the tabs. */
799 if (lStyle
& TCS_BOTTOM
)
800 prc
->bottom
+= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
802 prc
->top
-= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
804 /* Inflate the rectangle for the padding */
805 InflateRect(prc
, DISPLAY_AREA_PADDINGX
, DISPLAY_AREA_PADDINGY
);
807 /* Inflate for the border */
808 InflateRect(prc
, CONTROL_BORDER_SIZEX
, CONTROL_BORDER_SIZEX
);
810 else /* Go from window rectangle. */
812 /* Deflate the rectangle for the border */
813 InflateRect(prc
, -CONTROL_BORDER_SIZEX
, -CONTROL_BORDER_SIZEX
);
815 /* Deflate the rectangle for the padding */
816 InflateRect(prc
, -DISPLAY_AREA_PADDINGX
, -DISPLAY_AREA_PADDINGY
);
818 /* Remove the height of the tabs. */
819 if (lStyle
& TCS_BOTTOM
)
820 prc
->bottom
-= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
822 prc
->top
+= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
829 /******************************************************************************
832 * This method will handle the notification from the scroll control and
833 * perform the scrolling operation on the tab control.
835 static LRESULT
TAB_OnHScroll(
841 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
843 if(nScrollCode
== SB_THUMBPOSITION
&& nPos
!= infoPtr
->leftmostVisible
)
845 if(nPos
< infoPtr
->leftmostVisible
)
846 infoPtr
->leftmostVisible
--;
848 infoPtr
->leftmostVisible
++;
850 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
851 TAB_InvalidateTabArea(hwnd
, infoPtr
);
852 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
853 MAKELONG(infoPtr
->leftmostVisible
, 0));
859 /******************************************************************************
862 * This method will check the current scrolling state and make sure the
863 * scrolling control is displayed (or not).
865 static void TAB_SetupScrolling(
868 const RECT
* clientRect
)
871 DWORD lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
873 if (infoPtr
->needsScrolling
)
879 * Calculate the position of the scroll control.
881 if(lStyle
& TCS_VERTICAL
)
883 controlPos
.right
= clientRect
->right
;
884 controlPos
.left
= controlPos
.right
- 2 * GetSystemMetrics(SM_CXHSCROLL
);
886 if (lStyle
& TCS_BOTTOM
)
888 controlPos
.top
= clientRect
->bottom
- infoPtr
->tabHeight
;
889 controlPos
.bottom
= controlPos
.top
+ GetSystemMetrics(SM_CYHSCROLL
);
893 controlPos
.bottom
= clientRect
->top
+ infoPtr
->tabHeight
;
894 controlPos
.top
= controlPos
.bottom
- GetSystemMetrics(SM_CYHSCROLL
);
899 controlPos
.right
= clientRect
->right
;
900 controlPos
.left
= controlPos
.right
- 2 * GetSystemMetrics(SM_CXHSCROLL
);
902 if (lStyle
& TCS_BOTTOM
)
904 controlPos
.top
= clientRect
->bottom
- infoPtr
->tabHeight
;
905 controlPos
.bottom
= controlPos
.top
+ GetSystemMetrics(SM_CYHSCROLL
);
909 controlPos
.bottom
= clientRect
->top
+ infoPtr
->tabHeight
;
910 controlPos
.top
= controlPos
.bottom
- GetSystemMetrics(SM_CYHSCROLL
);
915 * If we don't have a scroll control yet, we want to create one.
916 * If we have one, we want to make sure it's positioned properly.
918 if (infoPtr
->hwndUpDown
==0)
920 infoPtr
->hwndUpDown
= CreateWindowA("msctls_updown32",
922 WS_VISIBLE
| WS_CHILD
| UDS_HORZ
,
923 controlPos
.left
, controlPos
.top
,
924 controlPos
.right
- controlPos
.left
,
925 controlPos
.bottom
- controlPos
.top
,
933 SetWindowPos(infoPtr
->hwndUpDown
,
935 controlPos
.left
, controlPos
.top
,
936 controlPos
.right
- controlPos
.left
,
937 controlPos
.bottom
- controlPos
.top
,
938 SWP_SHOWWINDOW
| SWP_NOZORDER
);
941 /* Now calculate upper limit of the updown control range.
942 * We do this by calculating how many tabs will be offscreen when the
943 * last tab is visible.
945 if(infoPtr
->uNumItem
)
947 vsize
= clientRect
->right
- (controlPos
.right
- controlPos
.left
+ 1);
948 maxRange
= infoPtr
->uNumItem
;
949 tabwidth
= infoPtr
->items
[maxRange
- 1].rect
.right
;
951 for(; maxRange
> 0; maxRange
--)
953 if(tabwidth
- infoPtr
->items
[maxRange
- 1].rect
.left
> vsize
)
957 if(maxRange
== infoPtr
->uNumItem
)
963 /* If we once had a scroll control... hide it */
964 if (infoPtr
->hwndUpDown
!=0)
965 ShowWindow(infoPtr
->hwndUpDown
, SW_HIDE
);
967 if (infoPtr
->hwndUpDown
)
968 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETRANGE32
, 0, maxRange
);
971 /******************************************************************************
974 * This method will calculate the position rectangles of all the items in the
975 * control. The rectangle calculated starts at 0 for the first item in the
976 * list and ignores scrolling and selection.
977 * It also uses the current font to determine the height of the tab row and
978 * it checks if all the tabs fit in the client area of the window. If they
979 * dont, a scrolling control is added.
981 static void TAB_SetItemBounds (HWND hwnd
)
983 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
984 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
985 TEXTMETRICA fontMetrics
;
989 HFONT hFont
, hOldFont
;
998 * We need to get text information so we need a DC and we need to select
1003 hFont
= infoPtr
->hFont
? infoPtr
->hFont
: GetStockObject (SYSTEM_FONT
);
1004 hOldFont
= SelectObject (hdc
, hFont
);
1007 * We will base the rectangle calculations on the client rectangle
1010 GetClientRect(hwnd
, &clientRect
);
1012 /* if TCS_VERTICAL then swap the height and width so this code places the tabs along the top of the rectangle */
1013 /* and we can just rotate them after rather than duplicate all of the below code */
1014 if(lStyle
& TCS_VERTICAL
)
1016 iTemp
= clientRect
.bottom
;
1017 clientRect
.bottom
= clientRect
.right
;
1018 clientRect
.right
= iTemp
;
1021 /* The leftmost item will be "0" aligned */
1023 curItemRowCount
= 0;
1025 if (!(lStyle
& TCS_FIXEDWIDTH
) && !((lStyle
& TCS_OWNERDRAWFIXED
) && infoPtr
->fSizeSet
) )
1028 int icon_height
= 0;
1030 /* Use the current font to determine the height of a tab. */
1031 GetTextMetricsA(hdc
, &fontMetrics
);
1033 /* Get the icon height */
1035 ImageList_GetIconSize(infoPtr
->himl
, 0, &icon_height
);
1037 /* Take the highest between font or icon */
1038 if (fontMetrics
.tmHeight
> icon_height
)
1039 item_height
= fontMetrics
.tmHeight
;
1041 item_height
= icon_height
;
1044 * Make sure there is enough space for the letters + icon + growing the
1045 * selected item + extra space for the selected item.
1047 infoPtr
->tabHeight
= item_height
+ 2 * VERTICAL_ITEM_PADDING
+
1048 SELECTED_TAB_OFFSET
;
1052 for (curItem
= 0; curItem
< infoPtr
->uNumItem
; curItem
++)
1054 /* Set the leftmost position of the tab. */
1055 infoPtr
->items
[curItem
].rect
.left
= curItemLeftPos
;
1057 if ( (lStyle
& TCS_FIXEDWIDTH
) || ((lStyle
& TCS_OWNERDRAWFIXED
) && infoPtr
->fSizeSet
))
1059 infoPtr
->items
[curItem
].rect
.right
= infoPtr
->items
[curItem
].rect
.left
+
1061 2 * HORIZONTAL_ITEM_PADDING
;
1068 /* Calculate how wide the tab is depending on the text it contains */
1069 GetTextExtentPoint32W(hdc
, infoPtr
->items
[curItem
].pszText
,
1070 lstrlenW(infoPtr
->items
[curItem
].pszText
), &size
);
1072 /* under Windows, there seems to be a minimum width of 2x the height
1073 * for button style tabs */
1074 if (lStyle
& TCS_BUTTONS
)
1075 size
.cx
= max(size
.cx
, 2 * (infoPtr
->tabHeight
- 2));
1077 /* Add the icon width */
1080 ImageList_GetIconSize(infoPtr
->himl
, &icon_width
, 0);
1084 infoPtr
->items
[curItem
].rect
.right
= infoPtr
->items
[curItem
].rect
.left
+
1085 size
.cx
+ icon_width
+
1086 num
* HORIZONTAL_ITEM_PADDING
;
1090 * Check if this is a multiline tab control and if so
1091 * check to see if we should wrap the tabs
1093 * Because we are going to arange all these tabs evenly
1094 * really we are basically just counting rows at this point
1098 if (((lStyle
& TCS_MULTILINE
) || (lStyle
& TCS_VERTICAL
)) &&
1099 (infoPtr
->items
[curItem
].rect
.right
> clientRect
.right
))
1101 infoPtr
->items
[curItem
].rect
.right
-=
1102 infoPtr
->items
[curItem
].rect
.left
;
1104 infoPtr
->items
[curItem
].rect
.left
= 0;
1108 infoPtr
->items
[curItem
].rect
.bottom
= 0;
1109 infoPtr
->items
[curItem
].rect
.top
= curItemRowCount
;
1111 TRACE("TextSize: %li\n", size
.cx
);
1112 TRACE("Rect: T %i, L %i, B %i, R %i\n",
1113 infoPtr
->items
[curItem
].rect
.top
,
1114 infoPtr
->items
[curItem
].rect
.left
,
1115 infoPtr
->items
[curItem
].rect
.bottom
,
1116 infoPtr
->items
[curItem
].rect
.right
);
1119 * The leftmost position of the next item is the rightmost position
1122 if (lStyle
& TCS_BUTTONS
)
1124 curItemLeftPos
= infoPtr
->items
[curItem
].rect
.right
+ 1;
1125 if (lStyle
& TCS_FLATBUTTONS
)
1126 curItemLeftPos
+= FLAT_BTN_SPACINGX
;
1129 curItemLeftPos
= infoPtr
->items
[curItem
].rect
.right
;
1132 if (!((lStyle
& TCS_MULTILINE
) || (lStyle
& TCS_VERTICAL
)))
1135 * Check if we need a scrolling control.
1137 infoPtr
->needsScrolling
= (curItemLeftPos
+ (2 * SELECTED_TAB_OFFSET
) >
1140 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1141 if(!infoPtr
->needsScrolling
)
1142 infoPtr
->leftmostVisible
= 0;
1144 TAB_SetupScrolling(hwnd
, infoPtr
, &clientRect
);
1147 /* Set the number of rows */
1148 infoPtr
->uNumRows
= curItemRowCount
;
1150 if (((lStyle
& TCS_MULTILINE
) || (lStyle
& TCS_VERTICAL
)) && (infoPtr
->uNumItem
> 0))
1152 INT widthDiff
, remainder
;
1153 INT tabPerRow
,remTab
;
1155 INT iIndexStart
=0,iIndexEnd
=0, iCount
=0;
1158 * Ok Microsoft trys to even out the rows. place the same
1159 * number of tabs in each row. So lets give that a shot
1163 tabPerRow
= infoPtr
->uNumItem
/ (infoPtr
->uNumRows
+ 1);
1164 remTab
= infoPtr
->uNumItem
% (infoPtr
->uNumRows
+ 1);
1166 for (iItm
=0,iRow
=0,iCount
=0,curItemLeftPos
=0;
1167 iItm
<infoPtr
->uNumItem
;
1170 /* if we have reached the maximum number of tabs on this row */
1171 /* move to the next row, reset our current item left position and */
1172 /* the count of items on this row */
1173 if (iCount
>= ((iRow
<remTab
)?tabPerRow
+ 1:tabPerRow
))
1180 /* normalize the current rect */
1182 /* shift the item to the left side of the clientRect */
1183 infoPtr
->items
[iItm
].rect
.right
-=
1184 infoPtr
->items
[iItm
].rect
.left
;
1185 infoPtr
->items
[iItm
].rect
.left
= 0;
1187 /* shift the item to the right to place it as the next item in this row */
1188 infoPtr
->items
[iItm
].rect
.left
+= curItemLeftPos
;
1189 infoPtr
->items
[iItm
].rect
.right
+= curItemLeftPos
;
1190 infoPtr
->items
[iItm
].rect
.top
= iRow
;
1191 if (lStyle
& TCS_BUTTONS
)
1193 curItemLeftPos
= infoPtr
->items
[iItm
].rect
.right
+ 1;
1194 if (lStyle
& TCS_FLATBUTTONS
)
1195 curItemLeftPos
+= FLAT_BTN_SPACINGX
;
1198 curItemLeftPos
= infoPtr
->items
[iItm
].rect
.right
;
1205 while(iIndexStart
< infoPtr
->uNumItem
)
1208 * find the indexs of the row
1210 /* find the first item on the next row */
1211 for (iIndexEnd
=iIndexStart
;
1212 (iIndexEnd
< infoPtr
->uNumItem
) &&
1213 (infoPtr
->items
[iIndexEnd
].rect
.top
==
1214 infoPtr
->items
[iIndexStart
].rect
.top
) ;
1216 /* intentionaly blank */;
1219 * we need to justify these tabs so they fill the whole given
1223 /* find the amount of space remaining on this row */
1224 widthDiff
= clientRect
.right
- (2 * SELECTED_TAB_OFFSET
) -
1225 infoPtr
->items
[iIndexEnd
- 1].rect
.right
;
1227 /* iCount is the number of tab items on this row */
1228 iCount
= iIndexEnd
- iIndexStart
;
1233 remainder
= widthDiff
% iCount
;
1234 widthDiff
= widthDiff
/ iCount
;
1235 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1236 for (iIndex
=iIndexStart
,iCount
=0; iIndex
< iIndexEnd
;
1239 infoPtr
->items
[iIndex
].rect
.left
+= iCount
* widthDiff
;
1240 infoPtr
->items
[iIndex
].rect
.right
+= (iCount
+ 1) * widthDiff
;
1242 infoPtr
->items
[iIndex
- 1].rect
.right
+= remainder
;
1244 else /* we have only one item on this row, make it take up the entire row */
1246 infoPtr
->items
[iIndexStart
].rect
.left
= clientRect
.left
;
1247 infoPtr
->items
[iIndexStart
].rect
.right
= clientRect
.right
- 4;
1251 iIndexStart
= iIndexEnd
;
1256 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1257 if(lStyle
& TCS_VERTICAL
)
1260 for(iIndex
= 0; iIndex
< infoPtr
->uNumItem
; iIndex
++)
1262 rcItem
= &(infoPtr
->items
[iIndex
].rect
);
1264 rcOriginal
= *rcItem
;
1266 /* this is rotating the items by 90 degrees around the center of the control */
1267 rcItem
->top
= (clientRect
.right
- (rcOriginal
.left
- clientRect
.left
)) - (rcOriginal
.right
- rcOriginal
.left
);
1268 rcItem
->bottom
= rcItem
->top
+ (rcOriginal
.right
- rcOriginal
.left
);
1269 rcItem
->left
= rcOriginal
.top
;
1270 rcItem
->right
= rcOriginal
.bottom
;
1274 TAB_EnsureSelectionVisible(hwnd
,infoPtr
);
1275 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
1278 SelectObject (hdc
, hOldFont
);
1279 ReleaseDC (hwnd
, hdc
);
1282 /******************************************************************************
1283 * TAB_DrawItemInterior
1285 * This method is used to draw the interior (text and icon) of a single tab
1286 * into the tab control.
1289 TAB_DrawItemInterior
1297 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
1298 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1302 HPEN htextPen
= GetSysColorPen (COLOR_BTNTEXT
);
1306 if (drawRect
== NULL
)
1313 * Get the rectangle for the item.
1315 isVisible
= TAB_InternalGetItemRect(hwnd
, infoPtr
, iItem
, &itemRect
, &selectedRect
);
1320 * Make sure drawRect points to something valid; simplifies code.
1322 drawRect
= &localRect
;
1325 * This logic copied from the part of TAB_DrawItem which draws
1326 * the tab background. It's important to keep it in sync. I
1327 * would have liked to avoid code duplication, but couldn't figure
1328 * out how without making spaghetti of TAB_DrawItem.
1330 if (lStyle
& TCS_BUTTONS
)
1332 *drawRect
= itemRect
;
1333 if (iItem
== infoPtr
->iSelected
)
1341 if (iItem
== infoPtr
->iSelected
)
1342 *drawRect
= selectedRect
;
1344 *drawRect
= itemRect
;
1353 holdPen
= SelectObject(hdc
, htextPen
);
1355 oldBkMode
= SetBkMode(hdc
, TRANSPARENT
);
1356 SetTextColor(hdc
, GetSysColor((iItem
== infoPtr
->iHotTracked
) ? COLOR_HIGHLIGHT
: COLOR_BTNTEXT
));
1359 * Deflate the rectangle to acount for the padding
1361 if(lStyle
& TCS_VERTICAL
)
1362 InflateRect(drawRect
, -VERTICAL_ITEM_PADDING
, -HORIZONTAL_ITEM_PADDING
);
1364 InflateRect(drawRect
, -HORIZONTAL_ITEM_PADDING
, -VERTICAL_ITEM_PADDING
);
1368 * if owner draw, tell the owner to draw
1370 if ((lStyle
& TCS_OWNERDRAWFIXED
) && GetParent(hwnd
))
1376 * get the control id
1378 id
= GetWindowLongA( hwnd
, GWL_ID
);
1381 * put together the DRAWITEMSTRUCT
1383 dis
.CtlType
= ODT_TAB
;
1386 dis
.itemAction
= ODA_DRAWENTIRE
;
1387 if ( iItem
== infoPtr
->iSelected
)
1388 dis
.itemState
= ODS_SELECTED
;
1391 dis
.hwndItem
= hwnd
; /* */
1393 dis
.rcItem
= *drawRect
; /* */
1394 dis
.itemData
= infoPtr
->items
[iItem
].lParam
;
1397 * send the draw message
1399 SendMessageA( GetParent(hwnd
), WM_DRAWITEM
, (WPARAM
)id
, (LPARAM
)&dis
);
1410 HFONT hOldFont
= 0; /* stop uninitialized warning */
1412 INT nEscapement
= 0; /* stop uninitialized warning */
1413 INT nOrientation
= 0; /* stop uninitialized warning */
1416 /* used to center the icon and text in the tab */
1420 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1421 rcImage
= *drawRect
;
1426 * Setup for text output
1428 oldBkMode
= SetBkMode(hdc
, TRANSPARENT
);
1429 SetTextColor(hdc
, GetSysColor((iItem
== infoPtr
->iHotTracked
) ? COLOR_HIGHLIGHT
: COLOR_BTNTEXT
));
1431 /* get the rectangle that the text fits in */
1432 DrawTextW(hdc
, infoPtr
->items
[iItem
].pszText
, -1,
1433 &rcText
, DT_CALCRECT
);
1436 * If not owner draw, then do the drawing ourselves.
1440 if (infoPtr
->himl
&& (infoPtr
->items
[iItem
].mask
& TCIF_IMAGE
))
1442 ImageList_GetIconSize(infoPtr
->himl
, &cx
, &cy
);
1444 if(lStyle
& TCS_VERTICAL
)
1445 center_offset
= ((drawRect
->bottom
- drawRect
->top
) - (cy
+ VERTICAL_ITEM_PADDING
+ (rcText
.right
- rcText
.left
))) / 2;
1447 center_offset
= ((drawRect
->right
- drawRect
->left
) - (cx
+ HORIZONTAL_ITEM_PADDING
+ (rcText
.right
- rcText
.left
))) / 2;
1449 if((lStyle
& TCS_VERTICAL
) && (lStyle
& TCS_BOTTOM
))
1451 /* rcImage.left = drawRect->left; */ /* explicit from above rcImage = *drawRect */
1452 rcImage
.top
= drawRect
->top
+ center_offset
;
1453 rcImage
.left
= drawRect
->right
- cx
; /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1454 /* right side of the tab, but the image still uses the left as its x position */
1455 /* this keeps the image always drawn off of the same side of the tab */
1456 drawRect
->top
= rcImage
.top
+ (cx
+ VERTICAL_ITEM_PADDING
);
1458 else if(lStyle
& TCS_VERTICAL
)
1460 /* rcImage.left = drawRect->left; */ /* explicit from above rcImage = *drawRect */
1461 rcImage
.top
= drawRect
->bottom
- cy
- center_offset
;
1463 drawRect
->bottom
= rcImage
.top
- VERTICAL_ITEM_PADDING
;
1465 else /* normal style, whether TCS_BOTTOM or not */
1467 rcImage
.left
= drawRect
->left
+ center_offset
;
1468 /* rcImage.top = drawRect->top; */ /* explicit from above rcImage = *drawRect */
1470 drawRect
->left
= rcImage
.left
+ cx
+ HORIZONTAL_ITEM_PADDING
;
1476 infoPtr
->items
[iItem
].iImage
,
1482 } else /* no image, so just shift the drawRect borders around */
1484 if(lStyle
& TCS_VERTICAL
)
1486 center_offset
= ((drawRect
->bottom
- drawRect
->top
) - (rcText
.right
- rcText
.left
)) / 2;
1488 if(lStyle
& TCS_BOTTOM
)
1489 drawRect
->top
+=center_offset
;
1491 drawRect
->bottom
-=center_offset
;
1495 center_offset
= ((drawRect
->right
- drawRect
->left
) - (rcText
.right
- rcText
.left
)) / 2;
1496 drawRect
->left
+=center_offset
;
1501 if (lStyle
& TCS_RIGHTJUSTIFY
)
1502 uHorizAlign
= DT_CENTER
;
1504 uHorizAlign
= DT_LEFT
;
1506 if(lStyle
& TCS_VERTICAL
) /* if we are vertical rotate the text and each character */
1508 if(lStyle
& TCS_BOTTOM
)
1511 nOrientation
= -900;
1520 /* to get a font with the escapement and orientation we are looking for, we need to */
1521 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1522 if(lStyle
& TCS_VERTICAL
)
1526 lstrcpyA(logfont
.lfFaceName
, "Arial");
1527 logfont
.lfHeight
= -MulDiv(iPointSize
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
1528 logfont
.lfWeight
= FW_NORMAL
;
1529 logfont
.lfItalic
= 0;
1530 logfont
.lfUnderline
= 0;
1531 logfont
.lfStrikeOut
= 0;
1533 logfont
.lfEscapement
= nEscapement
;
1534 logfont
.lfOrientation
= nOrientation
;
1535 hFont
= CreateFontIndirectA(&logfont
);
1536 hOldFont
= SelectObject(hdc
, hFont
);
1540 ((lStyle
& TCS_VERTICAL
) && (lStyle
& TCS_BOTTOM
)) ? drawRect
->right
: drawRect
->left
,
1541 ((lStyle
& TCS_VERTICAL
) && !(lStyle
& TCS_BOTTOM
)) ? drawRect
->bottom
: drawRect
->top
,
1544 infoPtr
->items
[iItem
].pszText
,
1545 lstrlenW(infoPtr
->items
[iItem
].pszText
),
1549 /* clean things up */
1550 *drawRect
= rcTemp
; /* restore drawRect */
1552 if(lStyle
& TCS_VERTICAL
)
1553 SelectObject(hdc
, hOldFont
); /* restore the original font */
1559 SetBkMode(hdc
, oldBkMode
);
1560 SelectObject(hdc
, holdPen
);
1563 /******************************************************************************
1566 * This method is used to draw a single tab into the tab control.
1568 static void TAB_DrawItem(
1573 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1574 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1581 * Get the rectangle for the item.
1583 isVisible
= TAB_InternalGetItemRect(hwnd
,
1591 HBRUSH hbr
= CreateSolidBrush (GetSysColor(COLOR_BTNFACE
));
1592 HPEN hwPen
= GetSysColorPen (COLOR_3DHILIGHT
);
1593 HPEN hbPen
= GetSysColorPen (COLOR_3DDKSHADOW
);
1594 HPEN hShade
= GetSysColorPen (COLOR_BTNSHADOW
);
1597 BOOL deleteBrush
= TRUE
;
1599 if (lStyle
& TCS_BUTTONS
)
1601 /* Get item rectangle */
1604 holdPen
= SelectObject (hdc
, hwPen
);
1606 /* Separators between flat buttons */
1607 /* FIXME: test and correct this if necessary for TCS_FLATBUTTONS style */
1608 if (lStyle
& TCS_FLATBUTTONS
)
1610 int x
= r
.right
+ FLAT_BTN_SPACINGX
- 2;
1613 MoveToEx (hdc
, x
, r
.bottom
- 1, NULL
);
1614 LineTo (hdc
, x
, r
.top
- 1);
1618 SelectObject(hdc
, hbPen
);
1619 MoveToEx (hdc
, x
, r
.bottom
- 1, NULL
);
1620 LineTo (hdc
, x
, r
.top
- 1);
1623 SelectObject (hdc
, hShade
);
1624 MoveToEx (hdc
, x
- 1, r
.bottom
- 1, NULL
);
1625 LineTo (hdc
, x
- 1, r
.top
- 1);
1628 if (iItem
== infoPtr
->iSelected
)
1630 /* Background color */
1631 if (!((lStyle
& TCS_OWNERDRAWFIXED
) && infoPtr
->fSizeSet
))
1633 COLORREF bk
= GetSysColor(COLOR_3DHILIGHT
);
1635 hbr
= GetSysColorBrush(COLOR_SCROLLBAR
);
1637 SetTextColor(hdc
, GetSysColor(COLOR_3DFACE
));
1638 SetBkColor(hdc
, bk
);
1640 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1641 * we better use 0x55aa bitmap brush to make scrollbar's background
1642 * look different from the window background.
1644 if (bk
== GetSysColor(COLOR_WINDOW
))
1645 hbr
= COMCTL32_hPattern55AABrush
;
1647 deleteBrush
= FALSE
;
1650 /* Erase the background */
1651 FillRect(hdc
, &r
, hbr
);
1655 * The rectangles calculated exclude the right and bottom
1656 * borders of the rectangle. To simplify the following code, those
1657 * borders are shaved-off beforehand.
1663 SelectObject(hdc
, hwPen
);
1664 MoveToEx (hdc
, r
.left
, r
.bottom
, NULL
);
1665 LineTo (hdc
, r
.right
, r
.bottom
);
1666 LineTo (hdc
, r
.right
, r
.top
+ 1);
1669 SelectObject(hdc
, hbPen
);
1670 LineTo (hdc
, r
.left
+ 1, r
.top
+ 1);
1671 LineTo (hdc
, r
.left
+ 1, r
.bottom
);
1674 SelectObject (hdc
, hShade
);
1675 MoveToEx (hdc
, r
.right
, r
.top
, NULL
);
1676 LineTo (hdc
, r
.left
, r
.top
);
1677 LineTo (hdc
, r
.left
, r
.bottom
);
1681 /* Erase the background */
1682 FillRect(hdc
, &r
, hbr
);
1684 if (!(lStyle
& TCS_FLATBUTTONS
))
1687 MoveToEx (hdc
, r
.left
, r
.bottom
, NULL
);
1688 LineTo (hdc
, r
.left
, r
.top
);
1689 LineTo (hdc
, r
.right
, r
.top
);
1692 SelectObject(hdc
, hbPen
);
1693 LineTo (hdc
, r
.right
, r
.bottom
);
1694 LineTo (hdc
, r
.left
, r
.bottom
);
1697 SelectObject (hdc
, hShade
);
1698 MoveToEx (hdc
, r
.right
- 1, r
.top
, NULL
);
1699 LineTo (hdc
, r
.right
- 1, r
.bottom
- 1);
1700 LineTo (hdc
, r
.left
+ 1, r
.bottom
- 1);
1704 else /* !TCS_BUTTONS */
1706 /* Background color */
1708 hbr
= CreateSolidBrush(GetSysColor(COLOR_BTNFACE
));
1710 /* We draw a rectangle of different sizes depending on the selection
1712 if (iItem
== infoPtr
->iSelected
)
1718 * Erase the background.
1719 * This is necessary when drawing the selected item since it is larger
1720 * than the others, it might overlap with stuff already drawn by the
1723 FillRect(hdc
, &r
, hbr
);
1727 * The rectangles calculated exclude the right and bottom
1728 * borders of the rectangle. To simplify the following code, those
1729 * borders are shaved-off beforehand.
1734 holdPen
= SelectObject (hdc
, hwPen
);
1735 if(lStyle
& TCS_VERTICAL
)
1737 if (lStyle
& TCS_BOTTOM
)
1740 MoveToEx (hdc
, r
.left
, r
.top
, NULL
);
1741 LineTo (hdc
, r
.right
- ROUND_CORNER_SIZE
, r
.top
);
1742 LineTo (hdc
, r
.right
, r
.top
+ ROUND_CORNER_SIZE
);
1745 SelectObject(hdc
, hbPen
);
1746 LineTo (hdc
, r
.right
, r
.bottom
- ROUND_CORNER_SIZE
);
1747 LineTo (hdc
, r
.right
- ROUND_CORNER_SIZE
, r
.bottom
);
1748 LineTo (hdc
, r
.left
- 1, r
.bottom
);
1751 SelectObject (hdc
, hShade
);
1752 MoveToEx (hdc
, r
.right
- 1, r
.top
, NULL
);
1753 LineTo (hdc
, r
.right
- 1, r
.bottom
- 1);
1754 LineTo (hdc
, r
.left
- 1, r
.bottom
- 1);
1759 MoveToEx (hdc
, r
.right
, r
.top
, NULL
);
1760 LineTo (hdc
, r
.left
+ ROUND_CORNER_SIZE
, r
.top
);
1761 LineTo (hdc
, r
.left
, r
.top
+ ROUND_CORNER_SIZE
);
1762 LineTo (hdc
, r
.left
, r
.bottom
- ROUND_CORNER_SIZE
);
1765 SelectObject(hdc
, hbPen
);
1766 LineTo (hdc
, r
.left
+ ROUND_CORNER_SIZE
, r
.bottom
);
1767 LineTo (hdc
, r
.right
+ 1, r
.bottom
);
1770 SelectObject (hdc
, hShade
);
1771 MoveToEx (hdc
, r
.left
+ ROUND_CORNER_SIZE
- 1, r
.bottom
- 1, NULL
);
1772 LineTo (hdc
, r
.right
+ 1, r
.bottom
- 1);
1777 if (lStyle
& TCS_BOTTOM
)
1780 MoveToEx (hdc
, r
.left
, r
.top
, NULL
);
1781 LineTo (hdc
, r
.left
, r
.bottom
- ROUND_CORNER_SIZE
);
1782 LineTo (hdc
, r
.left
+ ROUND_CORNER_SIZE
, r
.bottom
);
1785 SelectObject(hdc
, hbPen
);
1786 LineTo (hdc
, r
.right
- ROUND_CORNER_SIZE
, r
.bottom
);
1787 LineTo (hdc
, r
.right
, r
.bottom
- ROUND_CORNER_SIZE
);
1788 LineTo (hdc
, r
.right
, r
.top
- 1);
1791 SelectObject (hdc
, hShade
);
1792 MoveToEx (hdc
, r
.left
, r
.bottom
- 1, NULL
);
1793 LineTo (hdc
, r
.right
- ROUND_CORNER_SIZE
- 1, r
.bottom
- 1);
1794 LineTo (hdc
, r
.right
- 1, r
.bottom
- ROUND_CORNER_SIZE
- 1);
1795 LineTo (hdc
, r
.right
- 1, r
.top
- 1);
1800 if(infoPtr
->items
[iItem
].rect
.left
== 0) /* if leftmost draw the line longer */
1801 MoveToEx (hdc
, r
.left
, r
.bottom
, NULL
);
1803 MoveToEx (hdc
, r
.left
, r
.bottom
- 1, NULL
);
1805 LineTo (hdc
, r
.left
, r
.top
+ ROUND_CORNER_SIZE
);
1806 LineTo (hdc
, r
.left
+ ROUND_CORNER_SIZE
, r
.top
);
1807 LineTo (hdc
, r
.right
- ROUND_CORNER_SIZE
, r
.top
);
1810 SelectObject(hdc
, hbPen
);
1811 LineTo (hdc
, r
.right
, r
.top
+ ROUND_CORNER_SIZE
);
1812 LineTo (hdc
, r
.right
, r
.bottom
+ 1);
1816 SelectObject (hdc
, hShade
);
1817 MoveToEx (hdc
, r
.right
- 1, r
.top
+ ROUND_CORNER_SIZE
, NULL
);
1818 LineTo (hdc
, r
.right
- 1, r
.bottom
+ 1);
1823 /* This modifies r to be the text rectangle. */
1824 TAB_DrawItemInterior(hwnd
, hdc
, iItem
, &r
);
1826 /* Draw the focus rectangle */
1827 if (((lStyle
& TCS_FOCUSNEVER
) == 0) &&
1828 (GetFocus() == hwnd
) &&
1829 (iItem
== infoPtr
->uFocus
) )
1832 InflateRect(&r
, -1, -1);
1834 DrawFocusRect(hdc
, &r
);
1838 SelectObject(hdc
, holdPen
);
1839 if (deleteBrush
) DeleteObject(hbr
);
1843 /******************************************************************************
1846 * This method is used to draw the raised border around the tab control
1849 static void TAB_DrawBorder (HWND hwnd
, HDC hdc
)
1851 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1853 HPEN hwPen
= GetSysColorPen (COLOR_3DHILIGHT
);
1854 HPEN hbPen
= GetSysColorPen (COLOR_3DDKSHADOW
);
1855 HPEN hShade
= GetSysColorPen (COLOR_BTNSHADOW
);
1857 DWORD lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1859 GetClientRect (hwnd
, &rect
);
1862 * Adjust for the style
1864 if ((lStyle
& TCS_BOTTOM
) && !(lStyle
& TCS_VERTICAL
))
1866 rect
.bottom
-= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
1868 else if((lStyle
& TCS_BOTTOM
) && (lStyle
& TCS_VERTICAL
))
1870 rect
.right
-= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
1872 else if(lStyle
& TCS_VERTICAL
)
1874 rect
.left
+= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 2;
1876 else /* not TCS_VERTICAL and not TCS_BOTTOM */
1878 rect
.top
+= (infoPtr
->tabHeight
- 2) * (infoPtr
->uNumRows
+ 1) + 1;
1882 * Shave-off the right and bottom margins (exluded in the
1889 htmPen
= SelectObject (hdc
, hwPen
);
1891 MoveToEx (hdc
, rect
.left
, rect
.bottom
, NULL
);
1892 LineTo (hdc
, rect
.left
, rect
.top
);
1893 LineTo (hdc
, rect
.right
, rect
.top
);
1896 SelectObject (hdc
, hbPen
);
1897 LineTo (hdc
, rect
.right
, rect
.bottom
);
1898 LineTo (hdc
, rect
.left
, rect
.bottom
);
1901 SelectObject (hdc
, hShade
);
1902 MoveToEx (hdc
, rect
.right
- 1, rect
.top
, NULL
);
1903 LineTo (hdc
, rect
.right
- 1, rect
.bottom
- 1);
1904 LineTo (hdc
, rect
.left
, rect
.bottom
- 1);
1906 SelectObject(hdc
, htmPen
);
1909 /******************************************************************************
1912 * This method repaints the tab control..
1914 static void TAB_Refresh (HWND hwnd
, HDC hdc
)
1916 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1920 if (!infoPtr
->DoRedraw
)
1923 hOldFont
= SelectObject (hdc
, infoPtr
->hFont
);
1925 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BUTTONS
)
1927 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
1928 TAB_DrawItem (hwnd
, hdc
, i
);
1932 /* Draw all the non selected item first */
1933 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
1935 if (i
!= infoPtr
->iSelected
)
1936 TAB_DrawItem (hwnd
, hdc
, i
);
1939 /* Now, draw the border, draw it before the selected item
1940 * since the selected item overwrites part of the border. */
1941 TAB_DrawBorder (hwnd
, hdc
);
1943 /* Then, draw the selected item */
1944 TAB_DrawItem (hwnd
, hdc
, infoPtr
->iSelected
);
1946 /* If we haven't set the current focus yet, set it now.
1947 * Only happens when we first paint the tab controls */
1948 if (infoPtr
->uFocus
== -1)
1949 TAB_SetCurFocus(hwnd
, infoPtr
->iSelected
);
1952 SelectObject (hdc
, hOldFont
);
1956 TAB_GetRowCount (HWND hwnd
)
1958 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1960 return infoPtr
->uNumRows
;
1964 TAB_SetRedraw (HWND hwnd
, WPARAM wParam
)
1966 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1968 infoPtr
->DoRedraw
=(BOOL
) wParam
;
1972 static LRESULT
TAB_EraseBackground(
1979 HBRUSH brush
= CreateSolidBrush(GetSysColor(COLOR_BTNFACE
));
1981 hdc
= givenDC
? givenDC
: GetDC(hwnd
);
1983 GetClientRect(hwnd
, &clientRect
);
1985 FillRect(hdc
, &clientRect
, brush
);
1988 ReleaseDC(hwnd
, hdc
);
1990 DeleteObject(brush
);
1995 /******************************************************************************
1996 * TAB_EnsureSelectionVisible
1998 * This method will make sure that the current selection is completely
1999 * visible by scrolling until it is.
2001 static void TAB_EnsureSelectionVisible(
2005 INT iSelected
= infoPtr
->iSelected
;
2006 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
2007 INT iOrigLeftmostVisible
= infoPtr
->leftmostVisible
;
2009 /* set the items row to the bottommost row or topmost row depending on
2011 if ((infoPtr
->uNumRows
> 0) && !(lStyle
& TCS_BUTTONS
))
2016 if(lStyle
& TCS_VERTICAL
)
2017 newselected
= infoPtr
->items
[iSelected
].rect
.left
;
2019 newselected
= infoPtr
->items
[iSelected
].rect
.top
;
2021 /* the target row is always the number of rows as 0 is the row furthest from the clientRect */
2022 iTargetRow
= infoPtr
->uNumRows
;
2024 if (newselected
!= iTargetRow
)
2027 if(lStyle
& TCS_VERTICAL
)
2029 for (i
=0; i
< infoPtr
->uNumItem
; i
++)
2031 /* move everything in the row of the selected item to the iTargetRow */
2032 if (infoPtr
->items
[i
].rect
.left
== newselected
)
2033 infoPtr
->items
[i
].rect
.left
= iTargetRow
;
2036 if (infoPtr
->items
[i
].rect
.left
> newselected
)
2037 infoPtr
->items
[i
].rect
.left
-=1;
2043 for (i
=0; i
< infoPtr
->uNumItem
; i
++)
2045 if (infoPtr
->items
[i
].rect
.top
== newselected
)
2046 infoPtr
->items
[i
].rect
.top
= iTargetRow
;
2049 if (infoPtr
->items
[i
].rect
.top
> newselected
)
2050 infoPtr
->items
[i
].rect
.top
-=1;
2054 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
2059 * Do the trivial cases first.
2061 if ( (!infoPtr
->needsScrolling
) ||
2062 (infoPtr
->hwndUpDown
==0) || (lStyle
& TCS_VERTICAL
))
2065 if (infoPtr
->leftmostVisible
>= iSelected
)
2067 infoPtr
->leftmostVisible
= iSelected
;
2074 /* Calculate the part of the client area that is visible */
2075 GetClientRect(hwnd
, &r
);
2078 GetClientRect(infoPtr
->hwndUpDown
, &r
);
2081 if ((infoPtr
->items
[iSelected
].rect
.right
-
2082 infoPtr
->items
[iSelected
].rect
.left
) >= width
)
2084 /* Special case: width of selected item is greater than visible
2087 infoPtr
->leftmostVisible
= iSelected
;
2091 for (i
= infoPtr
->leftmostVisible
; i
< infoPtr
->uNumItem
; i
++)
2093 if ((infoPtr
->items
[iSelected
].rect
.right
-
2094 infoPtr
->items
[i
].rect
.left
) < width
)
2097 infoPtr
->leftmostVisible
= i
;
2101 if (infoPtr
->leftmostVisible
!= iOrigLeftmostVisible
)
2102 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
2104 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
2105 MAKELONG(infoPtr
->leftmostVisible
, 0));
2108 /******************************************************************************
2109 * TAB_InvalidateTabArea
2111 * This method will invalidate the portion of the control that contains the
2112 * tabs. It is called when the state of the control changes and needs
2115 static void TAB_InvalidateTabArea(
2120 DWORD lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
2122 GetClientRect(hwnd
, &clientRect
);
2124 if ((lStyle
& TCS_BOTTOM
) && !(lStyle
& TCS_VERTICAL
))
2126 clientRect
.top
= clientRect
.bottom
-
2127 infoPtr
->tabHeight
-
2128 (infoPtr
->uNumRows
) * (infoPtr
->tabHeight
- 2) -
2129 ((lStyle
& TCS_BUTTONS
) ? (infoPtr
->uNumRows
) * BUTTON_SPACINGY
: 0) - 2;
2131 else if((lStyle
& TCS_BOTTOM
) && (lStyle
& TCS_VERTICAL
))
2133 clientRect
.left
= clientRect
.right
- infoPtr
->tabHeight
-
2134 (infoPtr
->uNumRows
) * (infoPtr
->tabHeight
- 2) -
2135 ((lStyle
& TCS_BUTTONS
) ? (infoPtr
->uNumRows
) * BUTTON_SPACINGY
: 0) - 2;
2137 else if(lStyle
& TCS_VERTICAL
)
2139 clientRect
.right
= clientRect
.left
+ infoPtr
->tabHeight
+
2140 (infoPtr
->uNumRows
) * (infoPtr
->tabHeight
- 2) -
2141 ((lStyle
& TCS_BUTTONS
) ? (infoPtr
->uNumRows
) * BUTTON_SPACINGY
: 0) + 1;
2146 clientRect
.bottom
= clientRect
.top
+ infoPtr
->tabHeight
+
2147 (infoPtr
->uNumRows
) * (infoPtr
->tabHeight
- 2) +
2148 ((lStyle
& TCS_BUTTONS
) ? (infoPtr
->uNumRows
) * BUTTON_SPACINGY
: 0) + 1;
2151 InvalidateRect(hwnd
, &clientRect
, TRUE
);
2155 TAB_Paint (HWND hwnd
, WPARAM wParam
)
2160 hdc
= wParam
== 0 ? BeginPaint (hwnd
, &ps
) : (HDC
)wParam
;
2161 TAB_Refresh (hwnd
, hdc
);
2164 EndPaint (hwnd
, &ps
);
2170 TAB_InsertItemA (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2172 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2177 GetClientRect (hwnd
, &rect
);
2178 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd
,
2179 rect
.top
, rect
.left
, rect
.bottom
, rect
.right
);
2181 pti
= (TCITEMA
*)lParam
;
2182 iItem
= (INT
)wParam
;
2184 if (iItem
< 0) return -1;
2185 if (iItem
> infoPtr
->uNumItem
)
2186 iItem
= infoPtr
->uNumItem
;
2188 if (infoPtr
->uNumItem
== 0) {
2189 infoPtr
->items
= COMCTL32_Alloc (sizeof (TAB_ITEM
));
2190 infoPtr
->uNumItem
++;
2191 infoPtr
->iSelected
= 0;
2194 TAB_ITEM
*oldItems
= infoPtr
->items
;
2196 infoPtr
->uNumItem
++;
2197 infoPtr
->items
= COMCTL32_Alloc (sizeof (TAB_ITEM
) * infoPtr
->uNumItem
);
2199 /* pre insert copy */
2201 memcpy (&infoPtr
->items
[0], &oldItems
[0],
2202 iItem
* sizeof(TAB_ITEM
));
2205 /* post insert copy */
2206 if (iItem
< infoPtr
->uNumItem
- 1) {
2207 memcpy (&infoPtr
->items
[iItem
+1], &oldItems
[iItem
],
2208 (infoPtr
->uNumItem
- iItem
- 1) * sizeof(TAB_ITEM
));
2212 if (iItem
<= infoPtr
->iSelected
)
2213 infoPtr
->iSelected
++;
2215 COMCTL32_Free (oldItems
);
2218 infoPtr
->items
[iItem
].mask
= pti
->mask
;
2219 if (pti
->mask
& TCIF_TEXT
)
2220 Str_SetPtrAtoW (&infoPtr
->items
[iItem
].pszText
, pti
->pszText
);
2222 if (pti
->mask
& TCIF_IMAGE
)
2223 infoPtr
->items
[iItem
].iImage
= pti
->iImage
;
2225 if (pti
->mask
& TCIF_PARAM
)
2226 infoPtr
->items
[iItem
].lParam
= pti
->lParam
;
2228 TAB_SetItemBounds(hwnd
);
2229 TAB_InvalidateTabArea(hwnd
, infoPtr
);
2231 TRACE("[%04x]: added item %d '%s'\n",
2232 hwnd
, iItem
, debugstr_w(infoPtr
->items
[iItem
].pszText
));
2239 TAB_InsertItemW (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2241 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2246 GetClientRect (hwnd
, &rect
);
2247 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd
,
2248 rect
.top
, rect
.left
, rect
.bottom
, rect
.right
);
2250 pti
= (TCITEMW
*)lParam
;
2251 iItem
= (INT
)wParam
;
2253 if (iItem
< 0) return -1;
2254 if (iItem
> infoPtr
->uNumItem
)
2255 iItem
= infoPtr
->uNumItem
;
2257 if (infoPtr
->uNumItem
== 0) {
2258 infoPtr
->items
= COMCTL32_Alloc (sizeof (TAB_ITEM
));
2259 infoPtr
->uNumItem
++;
2260 infoPtr
->iSelected
= 0;
2263 TAB_ITEM
*oldItems
= infoPtr
->items
;
2265 infoPtr
->uNumItem
++;
2266 infoPtr
->items
= COMCTL32_Alloc (sizeof (TAB_ITEM
) * infoPtr
->uNumItem
);
2268 /* pre insert copy */
2270 memcpy (&infoPtr
->items
[0], &oldItems
[0],
2271 iItem
* sizeof(TAB_ITEM
));
2274 /* post insert copy */
2275 if (iItem
< infoPtr
->uNumItem
- 1) {
2276 memcpy (&infoPtr
->items
[iItem
+1], &oldItems
[iItem
],
2277 (infoPtr
->uNumItem
- iItem
- 1) * sizeof(TAB_ITEM
));
2281 if (iItem
<= infoPtr
->iSelected
)
2282 infoPtr
->iSelected
++;
2284 COMCTL32_Free (oldItems
);
2287 infoPtr
->items
[iItem
].mask
= pti
->mask
;
2288 if (pti
->mask
& TCIF_TEXT
)
2289 Str_SetPtrW (&infoPtr
->items
[iItem
].pszText
, pti
->pszText
);
2291 if (pti
->mask
& TCIF_IMAGE
)
2292 infoPtr
->items
[iItem
].iImage
= pti
->iImage
;
2294 if (pti
->mask
& TCIF_PARAM
)
2295 infoPtr
->items
[iItem
].lParam
= pti
->lParam
;
2297 TAB_SetItemBounds(hwnd
);
2298 TAB_InvalidateTabArea(hwnd
, infoPtr
);
2300 TRACE("[%04x]: added item %d '%s'\n",
2301 hwnd
, iItem
, debugstr_w(infoPtr
->items
[iItem
].pszText
));
2308 TAB_SetItemSize (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2310 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2311 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
2314 if ((lStyle
& TCS_FIXEDWIDTH
) || (lStyle
& TCS_OWNERDRAWFIXED
))
2316 lResult
= MAKELONG(infoPtr
->tabWidth
, infoPtr
->tabHeight
);
2317 infoPtr
->tabWidth
= (INT
)LOWORD(lParam
);
2318 infoPtr
->tabHeight
= (INT
)HIWORD(lParam
);
2320 infoPtr
->fSizeSet
= TRUE
;
2326 TAB_SetItemA (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2328 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2333 iItem
= (INT
)wParam
;
2334 tabItem
= (LPTCITEMA
)lParam
;
2336 TRACE("%d %p\n", iItem
, tabItem
);
2337 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
)) return FALSE
;
2339 wineItem
= &infoPtr
->items
[iItem
];
2341 if (tabItem
->mask
& TCIF_IMAGE
)
2342 wineItem
->iImage
= tabItem
->iImage
;
2344 if (tabItem
->mask
& TCIF_PARAM
)
2345 wineItem
->lParam
= tabItem
->lParam
;
2347 if (tabItem
->mask
& TCIF_RTLREADING
)
2348 FIXME("TCIF_RTLREADING\n");
2350 if (tabItem
->mask
& TCIF_STATE
)
2351 wineItem
->dwState
= tabItem
->dwState
;
2353 if (tabItem
->mask
& TCIF_TEXT
)
2354 Str_SetPtrAtoW(&wineItem
->pszText
, tabItem
->pszText
);
2356 /* Update and repaint tabs */
2357 TAB_SetItemBounds(hwnd
);
2358 TAB_InvalidateTabArea(hwnd
,infoPtr
);
2365 TAB_SetItemW (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2367 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2372 iItem
= (INT
)wParam
;
2373 tabItem
= (LPTCITEMW
)lParam
;
2375 TRACE("%d %p\n", iItem
, tabItem
);
2376 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
)) return FALSE
;
2378 wineItem
= &infoPtr
->items
[iItem
];
2380 if (tabItem
->mask
& TCIF_IMAGE
)
2381 wineItem
->iImage
= tabItem
->iImage
;
2383 if (tabItem
->mask
& TCIF_PARAM
)
2384 wineItem
->lParam
= tabItem
->lParam
;
2386 if (tabItem
->mask
& TCIF_RTLREADING
)
2387 FIXME("TCIF_RTLREADING\n");
2389 if (tabItem
->mask
& TCIF_STATE
)
2390 wineItem
->dwState
= tabItem
->dwState
;
2392 if (tabItem
->mask
& TCIF_TEXT
)
2393 Str_SetPtrW(&wineItem
->pszText
, tabItem
->pszText
);
2395 /* Update and repaint tabs */
2396 TAB_SetItemBounds(hwnd
);
2397 TAB_InvalidateTabArea(hwnd
,infoPtr
);
2404 TAB_GetItemCount (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2406 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2408 return infoPtr
->uNumItem
;
2413 TAB_GetItemA (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2415 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2420 iItem
= (INT
)wParam
;
2421 tabItem
= (LPTCITEMA
)lParam
;
2423 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
))
2426 wineItem
=& infoPtr
->items
[iItem
];
2428 if (tabItem
->mask
& TCIF_IMAGE
)
2429 tabItem
->iImage
= wineItem
->iImage
;
2431 if (tabItem
->mask
& TCIF_PARAM
)
2432 tabItem
->lParam
= wineItem
->lParam
;
2434 if (tabItem
->mask
& TCIF_RTLREADING
)
2435 FIXME("TCIF_RTLREADING\n");
2437 if (tabItem
->mask
& TCIF_STATE
)
2438 tabItem
->dwState
= wineItem
->dwState
;
2440 if (tabItem
->mask
& TCIF_TEXT
)
2441 Str_GetPtrWtoA (wineItem
->pszText
, tabItem
->pszText
, tabItem
->cchTextMax
);
2448 TAB_GetItemW (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2450 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2455 iItem
= (INT
)wParam
;
2456 tabItem
= (LPTCITEMW
)lParam
;
2458 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
))
2461 wineItem
=& infoPtr
->items
[iItem
];
2463 if (tabItem
->mask
& TCIF_IMAGE
)
2464 tabItem
->iImage
= wineItem
->iImage
;
2466 if (tabItem
->mask
& TCIF_PARAM
)
2467 tabItem
->lParam
= wineItem
->lParam
;
2469 if (tabItem
->mask
& TCIF_RTLREADING
)
2470 FIXME("TCIF_RTLREADING\n");
2472 if (tabItem
->mask
& TCIF_STATE
)
2473 tabItem
->dwState
= wineItem
->dwState
;
2475 if (tabItem
->mask
& TCIF_TEXT
)
2476 Str_GetPtrW (wineItem
->pszText
, tabItem
->pszText
, tabItem
->cchTextMax
);
2483 TAB_DeleteItem (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2485 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2486 INT iItem
= (INT
) wParam
;
2487 BOOL bResult
= FALSE
;
2489 if ((iItem
>= 0) && (iItem
< infoPtr
->uNumItem
))
2491 TAB_ITEM
*oldItems
= infoPtr
->items
;
2493 infoPtr
->uNumItem
--;
2494 infoPtr
->items
= COMCTL32_Alloc(sizeof (TAB_ITEM
) * infoPtr
->uNumItem
);
2497 memcpy(&infoPtr
->items
[0], &oldItems
[0], iItem
* sizeof(TAB_ITEM
));
2499 if (iItem
< infoPtr
->uNumItem
)
2500 memcpy(&infoPtr
->items
[iItem
], &oldItems
[iItem
+ 1],
2501 (infoPtr
->uNumItem
- iItem
) * sizeof(TAB_ITEM
));
2503 COMCTL32_Free(oldItems
);
2505 /* Readjust the selected index */
2506 if ((iItem
== infoPtr
->iSelected
) && (iItem
> 0))
2507 infoPtr
->iSelected
--;
2509 if (iItem
< infoPtr
->iSelected
)
2510 infoPtr
->iSelected
--;
2512 if (infoPtr
->uNumItem
== 0)
2513 infoPtr
->iSelected
= -1;
2515 /* Reposition and repaint tabs */
2516 TAB_SetItemBounds(hwnd
);
2517 TAB_InvalidateTabArea(hwnd
,infoPtr
);
2526 TAB_DeleteAllItems (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2528 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2530 COMCTL32_Free (infoPtr
->items
);
2531 infoPtr
->uNumItem
= 0;
2532 infoPtr
->iSelected
= -1;
2533 if (infoPtr
->iHotTracked
>= 0)
2534 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
2535 infoPtr
->iHotTracked
= -1;
2537 TAB_SetItemBounds(hwnd
);
2538 TAB_InvalidateTabArea(hwnd
,infoPtr
);
2544 TAB_GetFont (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2546 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2549 return (LRESULT
)infoPtr
->hFont
;
2553 TAB_SetFont (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2556 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2558 TRACE("%x %lx\n",wParam
, lParam
);
2560 infoPtr
->hFont
= (HFONT
)wParam
;
2562 TAB_SetItemBounds(hwnd
);
2564 TAB_InvalidateTabArea(hwnd
, infoPtr
);
2571 TAB_GetImageList (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2573 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2576 return (LRESULT
)infoPtr
->himl
;
2580 TAB_SetImageList (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2582 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2583 HIMAGELIST himlPrev
;
2586 himlPrev
= infoPtr
->himl
;
2587 infoPtr
->himl
= (HIMAGELIST
)lParam
;
2588 return (LRESULT
)himlPrev
;
2592 TAB_GetUnicodeFormat (HWND hwnd
)
2594 TAB_INFO
*infoPtr
= TAB_GetInfoPtr (hwnd
);
2595 return infoPtr
->bUnicode
;
2599 TAB_SetUnicodeFormat (HWND hwnd
, WPARAM wParam
)
2601 TAB_INFO
*infoPtr
= TAB_GetInfoPtr (hwnd
);
2602 BOOL bTemp
= infoPtr
->bUnicode
;
2604 infoPtr
->bUnicode
= (BOOL
)wParam
;
2610 TAB_Size (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2613 /* I'm not really sure what the following code was meant to do.
2614 This is what it is doing:
2615 When WM_SIZE is sent with SIZE_RESTORED, the control
2616 gets positioned in the top left corner.
2620 UINT uPosFlags,cx,cy;
2624 parent = GetParent (hwnd);
2625 GetClientRect(parent, &parent_rect);
2628 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2629 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2631 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2632 cx, cy, uPosFlags | SWP_NOZORDER);
2634 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2637 /* Recompute the size/position of the tabs. */
2638 TAB_SetItemBounds (hwnd
);
2640 /* Force a repaint of the control. */
2641 InvalidateRect(hwnd
, NULL
, TRUE
);
2648 TAB_Create (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2651 TEXTMETRICA fontMetrics
;
2656 infoPtr
= (TAB_INFO
*)COMCTL32_Alloc (sizeof(TAB_INFO
));
2658 SetWindowLongA(hwnd
, 0, (DWORD
)infoPtr
);
2660 infoPtr
->uNumItem
= 0;
2661 infoPtr
->uNumRows
= 0;
2664 infoPtr
->hcurArrow
= LoadCursorA (0, IDC_ARROWA
);
2665 infoPtr
->iSelected
= -1;
2666 infoPtr
->iHotTracked
= -1;
2667 infoPtr
->uFocus
= -1;
2668 infoPtr
->hwndToolTip
= 0;
2669 infoPtr
->DoRedraw
= TRUE
;
2670 infoPtr
->needsScrolling
= FALSE
;
2671 infoPtr
->hwndUpDown
= 0;
2672 infoPtr
->leftmostVisible
= 0;
2673 infoPtr
->fSizeSet
= FALSE
;
2674 infoPtr
->bUnicode
= IsWindowUnicode (hwnd
);
2676 TRACE("Created tab control, hwnd [%04x]\n", hwnd
);
2678 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2679 if you don't specify it in CreateWindow. This is necessary in
2680 order for paint to work correctly. This follows windows behaviour. */
2681 dwStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
2682 SetWindowLongA(hwnd
, GWL_STYLE
, dwStyle
|WS_CLIPSIBLINGS
);
2684 if (dwStyle
& TCS_TOOLTIPS
) {
2685 /* Create tooltip control */
2686 infoPtr
->hwndToolTip
=
2687 CreateWindowExA (0, TOOLTIPS_CLASSA
, NULL
, 0,
2688 CW_USEDEFAULT
, CW_USEDEFAULT
,
2689 CW_USEDEFAULT
, CW_USEDEFAULT
,
2692 /* Send NM_TOOLTIPSCREATED notification */
2693 if (infoPtr
->hwndToolTip
) {
2694 NMTOOLTIPSCREATED nmttc
;
2696 nmttc
.hdr
.hwndFrom
= hwnd
;
2697 nmttc
.hdr
.idFrom
= GetWindowLongA(hwnd
, GWL_ID
);
2698 nmttc
.hdr
.code
= NM_TOOLTIPSCREATED
;
2699 nmttc
.hwndToolTips
= infoPtr
->hwndToolTip
;
2701 SendMessageA (GetParent (hwnd
), WM_NOTIFY
,
2702 (WPARAM
)GetWindowLongA(hwnd
, GWL_ID
), (LPARAM
)&nmttc
);
2707 * We need to get text information so we need a DC and we need to select
2711 hOldFont
= SelectObject (hdc
, GetStockObject (SYSTEM_FONT
));
2713 /* Use the system font to determine the initial height of a tab. */
2714 GetTextMetricsA(hdc
, &fontMetrics
);
2717 * Make sure there is enough space for the letters + growing the
2718 * selected item + extra space for the selected item.
2720 infoPtr
->tabHeight
= fontMetrics
.tmHeight
+ 2 * VERTICAL_ITEM_PADDING
+
2721 SELECTED_TAB_OFFSET
;
2723 /* Initialize the width of a tab. */
2724 infoPtr
->tabWidth
= DEFAULT_TAB_WIDTH
;
2726 SelectObject (hdc
, hOldFont
);
2727 ReleaseDC(hwnd
, hdc
);
2733 TAB_Destroy (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2735 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2741 if (infoPtr
->items
) {
2742 for (iItem
= 0; iItem
< infoPtr
->uNumItem
; iItem
++) {
2743 if (infoPtr
->items
[iItem
].pszText
)
2744 COMCTL32_Free (infoPtr
->items
[iItem
].pszText
);
2746 COMCTL32_Free (infoPtr
->items
);
2749 if (infoPtr
->hwndToolTip
)
2750 DestroyWindow (infoPtr
->hwndToolTip
);
2752 if (infoPtr
->hwndUpDown
)
2753 DestroyWindow(infoPtr
->hwndUpDown
);
2755 if (infoPtr
->iHotTracked
>= 0)
2756 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
2758 COMCTL32_Free (infoPtr
);
2759 SetWindowLongA(hwnd
, 0, 0);
2763 static LRESULT WINAPI
2764 TAB_WindowProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
2767 TRACE("hwnd=%x msg=%x wParam=%x lParam=%lx\n", hwnd
, uMsg
, wParam
, lParam
);
2768 if (!TAB_GetInfoPtr(hwnd
) && (uMsg
!= WM_CREATE
))
2769 return DefWindowProcA (hwnd
, uMsg
, wParam
, lParam
);
2773 case TCM_GETIMAGELIST
:
2774 return TAB_GetImageList (hwnd
, wParam
, lParam
);
2776 case TCM_SETIMAGELIST
:
2777 return TAB_SetImageList (hwnd
, wParam
, lParam
);
2779 case TCM_GETITEMCOUNT
:
2780 return TAB_GetItemCount (hwnd
, wParam
, lParam
);
2783 return TAB_GetItemA (hwnd
, wParam
, lParam
);
2786 return TAB_GetItemW (hwnd
, wParam
, lParam
);
2789 return TAB_SetItemA (hwnd
, wParam
, lParam
);
2792 return TAB_SetItemW (hwnd
, wParam
, lParam
);
2794 case TCM_DELETEITEM
:
2795 return TAB_DeleteItem (hwnd
, wParam
, lParam
);
2797 case TCM_DELETEALLITEMS
:
2798 return TAB_DeleteAllItems (hwnd
, wParam
, lParam
);
2800 case TCM_GETITEMRECT
:
2801 return TAB_GetItemRect (hwnd
, wParam
, lParam
);
2804 return TAB_GetCurSel (hwnd
);
2807 return TAB_HitTest (hwnd
, wParam
, lParam
);
2810 return TAB_SetCurSel (hwnd
, wParam
);
2812 case TCM_INSERTITEMA
:
2813 return TAB_InsertItemA (hwnd
, wParam
, lParam
);
2815 case TCM_INSERTITEMW
:
2816 return TAB_InsertItemW (hwnd
, wParam
, lParam
);
2818 case TCM_SETITEMEXTRA
:
2819 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
2822 case TCM_ADJUSTRECT
:
2823 return TAB_AdjustRect (hwnd
, (BOOL
)wParam
, (LPRECT
)lParam
);
2825 case TCM_SETITEMSIZE
:
2826 return TAB_SetItemSize (hwnd
, wParam
, lParam
);
2828 case TCM_REMOVEIMAGE
:
2829 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
2832 case TCM_SETPADDING
:
2833 FIXME("Unimplemented msg TCM_SETPADDING\n");
2836 case TCM_GETROWCOUNT
:
2837 return TAB_GetRowCount(hwnd
);
2839 case TCM_GETUNICODEFORMAT
:
2840 return TAB_GetUnicodeFormat (hwnd
);
2842 case TCM_SETUNICODEFORMAT
:
2843 return TAB_SetUnicodeFormat (hwnd
, wParam
);
2845 case TCM_HIGHLIGHTITEM
:
2846 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
2849 case TCM_GETTOOLTIPS
:
2850 return TAB_GetToolTips (hwnd
, wParam
, lParam
);
2852 case TCM_SETTOOLTIPS
:
2853 return TAB_SetToolTips (hwnd
, wParam
, lParam
);
2855 case TCM_GETCURFOCUS
:
2856 return TAB_GetCurFocus (hwnd
);
2858 case TCM_SETCURFOCUS
:
2859 return TAB_SetCurFocus (hwnd
, wParam
);
2861 case TCM_SETMINTABWIDTH
:
2862 FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
2865 case TCM_DESELECTALL
:
2866 FIXME("Unimplemented msg TCM_DESELECTALL\n");
2869 case TCM_GETEXTENDEDSTYLE
:
2870 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
2873 case TCM_SETEXTENDEDSTYLE
:
2874 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
2878 return TAB_GetFont (hwnd
, wParam
, lParam
);
2881 return TAB_SetFont (hwnd
, wParam
, lParam
);
2884 return TAB_Create (hwnd
, wParam
, lParam
);
2887 return TAB_Destroy (hwnd
, wParam
, lParam
);
2890 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2892 case WM_LBUTTONDOWN
:
2893 return TAB_LButtonDown (hwnd
, wParam
, lParam
);
2896 return TAB_LButtonUp (hwnd
, wParam
, lParam
);
2898 case WM_RBUTTONDOWN
:
2899 return TAB_RButtonDown (hwnd
, wParam
, lParam
);
2902 return TAB_MouseMove (hwnd
, wParam
, lParam
);
2905 return TAB_EraseBackground (hwnd
, (HDC
)wParam
);
2908 return TAB_Paint (hwnd
, wParam
);
2911 return TAB_Size (hwnd
, wParam
, lParam
);
2914 return TAB_SetRedraw (hwnd
, wParam
);
2917 return TAB_OnHScroll(hwnd
, (int)LOWORD(wParam
), (int)HIWORD(wParam
), (HWND
)lParam
);
2919 case WM_STYLECHANGED
:
2920 TAB_SetItemBounds (hwnd
);
2921 InvalidateRect(hwnd
, NULL
, TRUE
);
2926 return TAB_FocusChanging(hwnd
, uMsg
, wParam
, lParam
);
2929 return TAB_KeyUp(hwnd
, wParam
);
2931 return TAB_NCHitTest(hwnd
, lParam
);
2934 if (uMsg
>= WM_USER
)
2935 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
2936 uMsg
, wParam
, lParam
);
2937 return DefWindowProcA (hwnd
, uMsg
, wParam
, lParam
);
2949 ZeroMemory (&wndClass
, sizeof(WNDCLASSA
));
2950 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
;
2951 wndClass
.lpfnWndProc
= (WNDPROC
)TAB_WindowProc
;
2952 wndClass
.cbClsExtra
= 0;
2953 wndClass
.cbWndExtra
= sizeof(TAB_INFO
*);
2954 wndClass
.hCursor
= LoadCursorA (0, IDC_ARROWA
);
2955 wndClass
.hbrBackground
= (HBRUSH
)NULL
;
2956 wndClass
.lpszClassName
= WC_TABCONTROLA
;
2958 RegisterClassA (&wndClass
);
2963 TAB_Unregister (void)
2965 UnregisterClassA (WC_TABCONTROLA
, (HINSTANCE
)NULL
);