4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Unicode support (under construction)
27 * UpDown control not displayed until after a tab is clicked on
35 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(tab
);
47 RECT rect
; /* bounding rectangle of the item relative to the
48 * leftmost item (the leftmost item, 0, would have a
49 * "left" member of 0 in this rectangle)
51 * additionally the top member hold the row number
52 * and bottom is unused and should be 0 */
57 UINT uNumItem
; /* number of tab items */
58 UINT uNumRows
; /* number of tab rows */
59 INT tabHeight
; /* height of the tab row */
60 INT tabWidth
; /* width of tabs */
61 HFONT hFont
; /* handle to the current font */
62 HCURSOR hcurArrow
; /* handle to the current cursor */
63 HIMAGELIST himl
; /* handle to a image list (may be 0) */
64 HWND hwndToolTip
; /* handle to tab's tooltip */
65 INT leftmostVisible
; /* Used for scrolling, this member contains
66 * the index of the first visible item */
67 INT iSelected
; /* the currently selected item */
68 INT iHotTracked
; /* the highlighted item under the mouse */
69 INT uFocus
; /* item which has the focus */
70 TAB_ITEM
* items
; /* pointer to an array of TAB_ITEM's */
71 BOOL DoRedraw
; /* flag for redrawing when tab contents is changed*/
72 BOOL needsScrolling
; /* TRUE if the size of the tabs is greater than
73 * the size of the control */
74 BOOL fSizeSet
; /* was the size of the tabs explicitly set? */
75 BOOL bUnicode
; /* Unicode control? */
76 HWND hwndUpDown
; /* Updown control used for scrolling */
79 /******************************************************************************
80 * Positioning constants
82 #define SELECTED_TAB_OFFSET 2
83 #define HORIZONTAL_ITEM_PADDING 5
84 #define VERTICAL_ITEM_PADDING 3
85 #define ROUND_CORNER_SIZE 2
86 #define DISPLAY_AREA_PADDINGX 2
87 #define DISPLAY_AREA_PADDINGY 2
88 #define CONTROL_BORDER_SIZEX 2
89 #define CONTROL_BORDER_SIZEY 2
90 #define BUTTON_SPACINGX 4
91 #define BUTTON_SPACINGY 4
92 #define FLAT_BTN_SPACINGX 8
93 #define DEFAULT_TAB_WIDTH 96
95 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
97 /******************************************************************************
98 * Hot-tracking timer constants
100 #define TAB_HOTTRACK_TIMER 1
101 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
103 /******************************************************************************
106 static void TAB_Refresh (HWND hwnd
, HDC hdc
);
107 static void TAB_InvalidateTabArea(HWND hwnd
, TAB_INFO
* infoPtr
);
108 static void TAB_EnsureSelectionVisible(HWND hwnd
, TAB_INFO
* infoPtr
);
109 static void TAB_DrawItem(HWND hwnd
, HDC hdc
, INT iItem
);
110 static void TAB_DrawItemInterior(HWND hwnd
, HDC hdc
, INT iItem
, RECT
* drawRect
);
113 TAB_SendSimpleNotify (HWND hwnd
, UINT code
)
117 nmhdr
.hwndFrom
= hwnd
;
118 nmhdr
.idFrom
= GetWindowLongA(hwnd
, GWL_ID
);
121 return (BOOL
) SendMessageA (GetParent (hwnd
), WM_NOTIFY
,
122 (WPARAM
) nmhdr
.idFrom
, (LPARAM
) &nmhdr
);
126 TAB_RelayEvent (HWND hwndTip
, HWND hwndMsg
, UINT uMsg
,
127 WPARAM wParam
, LPARAM lParam
)
135 msg
.time
= GetMessageTime ();
136 msg
.pt
.x
= LOWORD(GetMessagePos ());
137 msg
.pt
.y
= HIWORD(GetMessagePos ());
139 SendMessageA (hwndTip
, TTM_RELAYEVENT
, 0, (LPARAM
)&msg
);
143 TAB_GetCurSel (HWND hwnd
)
145 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
147 return infoPtr
->iSelected
;
151 TAB_GetCurFocus (HWND hwnd
)
153 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
155 return infoPtr
->uFocus
;
159 TAB_GetToolTips (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
161 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
163 if (infoPtr
== NULL
) return 0;
164 return infoPtr
->hwndToolTip
;
168 TAB_SetCurSel (HWND hwnd
,WPARAM wParam
)
170 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
171 INT iItem
= (INT
)wParam
;
175 if ((iItem
>= 0) && (iItem
< infoPtr
->uNumItem
)) {
176 prevItem
=infoPtr
->iSelected
;
177 infoPtr
->iSelected
=iItem
;
178 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
179 TAB_InvalidateTabArea(hwnd
, infoPtr
);
185 TAB_SetCurFocus (HWND hwnd
,WPARAM wParam
)
187 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
188 INT iItem
=(INT
) wParam
;
190 if ((iItem
< 0) || (iItem
>= infoPtr
->uNumItem
)) return 0;
192 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BUTTONS
) {
193 FIXME("Should set input focus\n");
195 int oldFocus
= infoPtr
->uFocus
;
196 if (infoPtr
->iSelected
!= iItem
|| infoPtr
->uFocus
== -1 ) {
197 infoPtr
->uFocus
= iItem
;
198 if (oldFocus
!= -1) {
199 if (TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
)!=TRUE
) {
200 infoPtr
->iSelected
= iItem
;
201 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
204 infoPtr
->iSelected
= iItem
;
205 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
206 TAB_InvalidateTabArea(hwnd
, infoPtr
);
214 TAB_SetToolTips (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
216 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
218 if (infoPtr
== NULL
) return 0;
219 infoPtr
->hwndToolTip
= (HWND
)wParam
;
223 /******************************************************************************
224 * TAB_InternalGetItemRect
226 * This method will calculate the rectangle representing a given tab item in
227 * client coordinates. This method takes scrolling into account.
229 * This method returns TRUE if the item is visible in the window and FALSE
230 * if it is completely outside the client area.
232 static BOOL
TAB_InternalGetItemRect(
239 RECT tmpItemRect
,clientRect
;
240 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
242 /* Perform a sanity check and a trivial visibility check. */
243 if ( (infoPtr
->uNumItem
<= 0) ||
244 (itemIndex
>= infoPtr
->uNumItem
) ||
245 (!((lStyle
& TCS_MULTILINE
) || (lStyle
& TCS_VERTICAL
)) && (itemIndex
< infoPtr
->leftmostVisible
)) )
249 * Avoid special cases in this procedure by assigning the "out"
250 * parameters if the caller didn't supply them
252 if (itemRect
== NULL
)
253 itemRect
= &tmpItemRect
;
255 /* Retrieve the unmodified item rect. */
256 *itemRect
= infoPtr
->items
[itemIndex
].rect
;
258 /* calculate the times bottom and top based on the row */
259 GetClientRect(hwnd
, &clientRect
);
261 if ((lStyle
& TCS_BOTTOM
) && !(lStyle
& TCS_VERTICAL
))
263 itemRect
->bottom
= clientRect
.bottom
-
264 SELECTED_TAB_OFFSET
-
265 itemRect
->top
* (infoPtr
->tabHeight
- 2) -
266 ((lStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: 0);
268 itemRect
->top
= clientRect
.bottom
-
270 itemRect
->top
* (infoPtr
->tabHeight
- 2) -
271 ((lStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: 0);
273 else if((lStyle
& TCS_BOTTOM
) && (lStyle
& TCS_VERTICAL
))
275 itemRect
->right
= clientRect
.right
- SELECTED_TAB_OFFSET
- itemRect
->left
* (infoPtr
->tabHeight
- 2) -
276 ((lStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGY
: 0);
277 itemRect
->left
= clientRect
.right
- infoPtr
->tabHeight
- itemRect
->left
* (infoPtr
->tabHeight
- 2) -
278 ((lStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGY
: 0);
280 else if((lStyle
& TCS_VERTICAL
) && !(lStyle
& TCS_BOTTOM
))
282 itemRect
->right
= clientRect
.left
+ infoPtr
->tabHeight
+ itemRect
->left
* (infoPtr
->tabHeight
- 2) +
283 ((lStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGY
: 0);
284 itemRect
->left
= clientRect
.left
+ SELECTED_TAB_OFFSET
+ itemRect
->left
* (infoPtr
->tabHeight
- 2) +
285 ((lStyle
& TCS_BUTTONS
) ? itemRect
->left
* BUTTON_SPACINGY
: 0);
287 else if(!(lStyle
& TCS_VERTICAL
) && !(lStyle
& TCS_BOTTOM
)) /* not TCS_BOTTOM and not TCS_VERTICAL */
289 itemRect
->bottom
= clientRect
.top
+
291 itemRect
->top
* (infoPtr
->tabHeight
- 2) +
292 ((lStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: 0);
293 itemRect
->top
= clientRect
.top
+
294 SELECTED_TAB_OFFSET
+
295 itemRect
->top
* (infoPtr
->tabHeight
- 2) +
296 ((lStyle
& TCS_BUTTONS
) ? itemRect
->top
* BUTTON_SPACINGY
: 0);
300 * "scroll" it to make sure the item at the very left of the
301 * tab control is the leftmost visible tab.
303 if(lStyle
& TCS_VERTICAL
)
307 -(clientRect
.bottom
- infoPtr
->items
[infoPtr
->leftmostVisible
].rect
.bottom
));
310 * Move the rectangle so the first item is slightly offset from
311 * the bottom of the tab control.
315 -SELECTED_TAB_OFFSET
);
320 -infoPtr
->items
[infoPtr
->leftmostVisible
].rect
.left
,
324 * Move the rectangle so the first item is slightly offset from
325 * the left of the tab control.
332 /* Now, calculate the position of the item as if it were selected. */
333 if (selectedRect
!=NULL
)
335 CopyRect(selectedRect
, itemRect
);
337 /* The rectangle of a selected item is a bit wider. */
338 if(lStyle
& TCS_VERTICAL
)
339 InflateRect(selectedRect
, 0, SELECTED_TAB_OFFSET
);
341 InflateRect(selectedRect
, SELECTED_TAB_OFFSET
, 0);
343 /* If it also a bit higher. */
344 if ((lStyle
& TCS_BOTTOM
) && !(lStyle
& TCS_VERTICAL
))
346 selectedRect
->top
-= 2; /* the border is thicker on the bottom */
347 selectedRect
->bottom
+= SELECTED_TAB_OFFSET
;
349 else if((lStyle
& TCS_BOTTOM
) && (lStyle
& TCS_VERTICAL
))
351 selectedRect
->left
-= 2; /* the border is thicker on the right */
352 selectedRect
->right
+= SELECTED_TAB_OFFSET
;
354 else if(lStyle
& TCS_VERTICAL
)
356 selectedRect
->left
-= SELECTED_TAB_OFFSET
;
357 selectedRect
->right
+= 1;
361 selectedRect
->top
-= SELECTED_TAB_OFFSET
;
362 selectedRect
->bottom
+= 1;
369 static BOOL
TAB_GetItemRect(HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
371 return TAB_InternalGetItemRect(hwnd
, TAB_GetInfoPtr(hwnd
), (INT
)wParam
,
372 (LPRECT
)lParam
, (LPRECT
)NULL
);
375 /******************************************************************************
378 * This method is called to handle keyboard input
380 static LRESULT
TAB_KeyUp(
384 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
390 newItem
= infoPtr
->uFocus
- 1;
393 newItem
= infoPtr
->uFocus
+ 1;
398 * If we changed to a valid item, change the selection
400 if ((newItem
>= 0) &&
401 (newItem
< infoPtr
->uNumItem
) &&
402 (infoPtr
->uFocus
!= newItem
))
404 if (!TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
))
406 infoPtr
->iSelected
= newItem
;
407 infoPtr
->uFocus
= newItem
;
408 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
410 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
411 TAB_InvalidateTabArea(hwnd
, infoPtr
);
418 /******************************************************************************
421 * This method is called whenever the focus goes in or out of this control
422 * it is used to update the visual state of the control.
424 static LRESULT
TAB_FocusChanging(
430 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
435 * Get the rectangle for the item.
437 isVisible
= TAB_InternalGetItemRect(hwnd
,
444 * If the rectangle is not completely invisible, invalidate that
445 * portion of the window.
449 InvalidateRect(hwnd
, &selectedRect
, TRUE
);
453 * Don't otherwise disturb normal behavior.
455 return DefWindowProcA (hwnd
, uMsg
, wParam
, lParam
);
458 static HWND
TAB_InternalHitTest (
468 for (iCount
= 0; iCount
< infoPtr
->uNumItem
; iCount
++)
470 TAB_InternalGetItemRect(hwnd
, infoPtr
, iCount
, &rect
, NULL
);
472 if (PtInRect(&rect
, pt
))
474 *flags
= TCHT_ONITEM
;
479 *flags
= TCHT_NOWHERE
;
484 TAB_HitTest (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
486 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
487 LPTCHITTESTINFO lptest
= (LPTCHITTESTINFO
) lParam
;
489 return TAB_InternalHitTest (hwnd
, infoPtr
, lptest
->pt
, &lptest
->flags
);
492 /******************************************************************************
495 * Napster v2b5 has a tab control for its main navigation which has a client
496 * area that covers the whole area of the dialog pages.
497 * That's why it receives all msgs for that area and the underlying dialog ctrls
499 * So I decided that we should handle WM_NCHITTEST here and return
500 * HTTRANSPARENT if we don't hit the tab control buttons.
501 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
502 * doesn't do it that way. Maybe depends on tab control styles ?
505 TAB_NCHitTest (HWND hwnd
, LPARAM lParam
)
507 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
511 pt
.x
= LOWORD(lParam
);
512 pt
.y
= HIWORD(lParam
);
513 ScreenToClient(hwnd
, &pt
);
515 if (TAB_InternalHitTest(hwnd
, infoPtr
, pt
, &dummyflag
) == -1)
516 return HTTRANSPARENT
;
522 TAB_LButtonDown (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
524 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
528 if (infoPtr
->hwndToolTip
)
529 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
530 WM_LBUTTONDOWN
, wParam
, lParam
);
532 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_FOCUSONBUTTONDOWN
) {
536 if (infoPtr
->hwndToolTip
)
537 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
538 WM_LBUTTONDOWN
, wParam
, lParam
);
540 pt
.x
= (INT
)LOWORD(lParam
);
541 pt
.y
= (INT
)HIWORD(lParam
);
543 newItem
= TAB_InternalHitTest (hwnd
, infoPtr
, pt
, &dummy
);
545 TRACE("On Tab, item %d\n", newItem
);
547 if ((newItem
!= -1) && (infoPtr
->iSelected
!= newItem
))
549 if (TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGING
) != TRUE
)
551 infoPtr
->iSelected
= newItem
;
552 infoPtr
->uFocus
= newItem
;
553 TAB_SendSimpleNotify(hwnd
, TCN_SELCHANGE
);
555 TAB_EnsureSelectionVisible(hwnd
, infoPtr
);
557 TAB_InvalidateTabArea(hwnd
, infoPtr
);
564 TAB_LButtonUp (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
566 TAB_SendSimpleNotify(hwnd
, NM_CLICK
);
572 TAB_RButtonDown (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
574 TAB_SendSimpleNotify(hwnd
, NM_RCLICK
);
578 /******************************************************************************
579 * TAB_DrawLoneItemInterior
581 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
582 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
583 * up the device context and font. This routine does the same setup but
584 * only calls TAB_DrawItemInterior for the single specified item.
587 TAB_DrawLoneItemInterior(HWND hwnd
, TAB_INFO
* infoPtr
, int iItem
)
589 HDC hdc
= GetDC(hwnd
);
590 HFONT hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
591 TAB_DrawItemInterior(hwnd
, hdc
, iItem
, NULL
);
592 SelectObject(hdc
, hOldFont
);
593 ReleaseDC(hwnd
, hdc
);
596 /******************************************************************************
597 * TAB_HotTrackTimerProc
599 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
600 * timer is setup so we can check if the mouse is moved out of our window.
601 * (We don't get an event when the mouse leaves, the mouse-move events just
602 * stop being delivered to our window and just start being delivered to
603 * another window.) This function is called when the timer triggers so
604 * we can check if the mouse has left our window. If so, we un-highlight
605 * the hot-tracked tab.
608 TAB_HotTrackTimerProc
610 HWND hwnd
, /* handle of window for timer messages */
611 UINT uMsg
, /* WM_TIMER message */
612 UINT idEvent
, /* timer identifier */
613 DWORD dwTime
/* current system time */
616 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
618 if (infoPtr
!= NULL
&& infoPtr
->iHotTracked
>= 0)
623 ** If we can't get the cursor position, or if the cursor is outside our
624 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
625 ** "outside" even if it is within our bounding rect if another window
626 ** overlaps. Note also that the case where the cursor stayed within our
627 ** window but has moved off the hot-tracked tab will be handled by the
628 ** WM_MOUSEMOVE event.
630 if (!GetCursorPos(&pt
) || WindowFromPoint(pt
) != hwnd
)
632 /* Redraw iHotTracked to look normal */
633 INT iRedraw
= infoPtr
->iHotTracked
;
634 infoPtr
->iHotTracked
= -1;
635 TAB_DrawLoneItemInterior(hwnd
, infoPtr
, iRedraw
);
637 /* Kill this timer */
638 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
643 /******************************************************************************
646 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
647 * should be highlighted. This function determines which tab in a tab control,
648 * if any, is under the mouse and records that information. The caller may
649 * supply output parameters to receive the item number of the tab item which
650 * was highlighted but isn't any longer and of the tab item which is now
651 * highlighted but wasn't previously. The caller can use this information to
652 * selectively redraw those tab items.
654 * If the caller has a mouse position, it can supply it through the pos
655 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
656 * supplies NULL and this function determines the current mouse position
664 int* out_redrawLeave
,
668 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
673 if (out_redrawLeave
!= NULL
)
674 *out_redrawLeave
= -1;
675 if (out_redrawEnter
!= NULL
)
676 *out_redrawEnter
= -1;
678 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_HOTTRACK
)
686 ScreenToClient(hwnd
, &pt
);
694 item
= TAB_InternalHitTest(hwnd
, infoPtr
, pt
, &flags
);
697 if (item
!= infoPtr
->iHotTracked
)
699 if (infoPtr
->iHotTracked
>= 0)
701 /* Mark currently hot-tracked to be redrawn to look normal */
702 if (out_redrawLeave
!= NULL
)
703 *out_redrawLeave
= infoPtr
->iHotTracked
;
707 /* Kill timer which forces recheck of mouse pos */
708 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
713 /* Start timer so we recheck mouse pos */
714 UINT timerID
= SetTimer
718 TAB_HOTTRACK_TIMER_INTERVAL
,
719 TAB_HotTrackTimerProc
723 return; /* Hot tracking not available */
726 infoPtr
->iHotTracked
= item
;
730 /* Mark new hot-tracked to be redrawn to look highlighted */
731 if (out_redrawEnter
!= NULL
)
732 *out_redrawEnter
= item
;
737 /******************************************************************************
740 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
743 TAB_MouseMove (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
748 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
750 if (infoPtr
->hwndToolTip
)
751 TAB_RelayEvent (infoPtr
->hwndToolTip
, hwnd
,
752 WM_LBUTTONDOWN
, wParam
, lParam
);
754 /* Determine which tab to highlight. Redraw tabs which change highlight
756 TAB_RecalcHotTrack(hwnd
, &lParam
, &redrawLeave
, &redrawEnter
);
758 if (redrawLeave
!= -1)
759 TAB_DrawLoneItemInterior(hwnd
, infoPtr
, redrawLeave
);
760 if (redrawEnter
!= -1)
761 TAB_DrawLoneItemInterior(hwnd
, infoPtr
, redrawEnter
);
766 /******************************************************************************
769 * Calculates the tab control's display area given the window rectangle or
770 * the window rectangle given the requested display rectangle.
772 static LRESULT
TAB_AdjustRect(
777 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
778 DWORD lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
780 if(lStyle
& TCS_VERTICAL
)
782 if (fLarger
) /* Go from display rectangle */
784 /* Add the height of the tabs. */
785 if (lStyle
& TCS_BOTTOM
)
786 prc
->right
+= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
788 prc
->left
-= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
790 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
791 /* Inflate the rectangle for the padding */
792 InflateRect(prc
, DISPLAY_AREA_PADDINGX
, DISPLAY_AREA_PADDINGY
);
794 /* Inflate for the border */
795 InflateRect(prc
, CONTROL_BORDER_SIZEX
, CONTROL_BORDER_SIZEX
);
797 else /* Go from window rectangle. */
799 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
800 /* Deflate the rectangle for the border */
801 InflateRect(prc
, -CONTROL_BORDER_SIZEX
, -CONTROL_BORDER_SIZEX
);
803 /* Deflate the rectangle for the padding */
804 InflateRect(prc
, -DISPLAY_AREA_PADDINGX
, -DISPLAY_AREA_PADDINGY
);
806 /* Remove the height of the tabs. */
807 if (lStyle
& TCS_BOTTOM
)
808 prc
->right
-= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
810 prc
->left
+= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
814 if (fLarger
) /* Go from display rectangle */
816 /* Add the height of the tabs. */
817 if (lStyle
& TCS_BOTTOM
)
818 prc
->bottom
+= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
820 prc
->top
-= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
822 /* Inflate the rectangle for the padding */
823 InflateRect(prc
, DISPLAY_AREA_PADDINGX
, DISPLAY_AREA_PADDINGY
);
825 /* Inflate for the border */
826 InflateRect(prc
, CONTROL_BORDER_SIZEX
, CONTROL_BORDER_SIZEX
);
828 else /* Go from window rectangle. */
830 /* Deflate the rectangle for the border */
831 InflateRect(prc
, -CONTROL_BORDER_SIZEX
, -CONTROL_BORDER_SIZEX
);
833 /* Deflate the rectangle for the padding */
834 InflateRect(prc
, -DISPLAY_AREA_PADDINGX
, -DISPLAY_AREA_PADDINGY
);
836 /* Remove the height of the tabs. */
837 if (lStyle
& TCS_BOTTOM
)
838 prc
->bottom
-= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
840 prc
->top
+= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
847 /******************************************************************************
850 * This method will handle the notification from the scroll control and
851 * perform the scrolling operation on the tab control.
853 static LRESULT
TAB_OnHScroll(
859 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
861 if(nScrollCode
== SB_THUMBPOSITION
&& nPos
!= infoPtr
->leftmostVisible
)
863 if(nPos
< infoPtr
->leftmostVisible
)
864 infoPtr
->leftmostVisible
--;
866 infoPtr
->leftmostVisible
++;
868 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
869 TAB_InvalidateTabArea(hwnd
, infoPtr
);
870 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
871 MAKELONG(infoPtr
->leftmostVisible
, 0));
877 /******************************************************************************
880 * This method will check the current scrolling state and make sure the
881 * scrolling control is displayed (or not).
883 static void TAB_SetupScrolling(
886 const RECT
* clientRect
)
889 DWORD lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
891 if (infoPtr
->needsScrolling
)
897 * Calculate the position of the scroll control.
899 if(lStyle
& TCS_VERTICAL
)
901 controlPos
.right
= clientRect
->right
;
902 controlPos
.left
= controlPos
.right
- 2 * GetSystemMetrics(SM_CXHSCROLL
);
904 if (lStyle
& TCS_BOTTOM
)
906 controlPos
.top
= clientRect
->bottom
- infoPtr
->tabHeight
;
907 controlPos
.bottom
= controlPos
.top
+ GetSystemMetrics(SM_CYHSCROLL
);
911 controlPos
.bottom
= clientRect
->top
+ infoPtr
->tabHeight
;
912 controlPos
.top
= controlPos
.bottom
- GetSystemMetrics(SM_CYHSCROLL
);
917 controlPos
.right
= clientRect
->right
;
918 controlPos
.left
= controlPos
.right
- 2 * GetSystemMetrics(SM_CXHSCROLL
);
920 if (lStyle
& TCS_BOTTOM
)
922 controlPos
.top
= clientRect
->bottom
- infoPtr
->tabHeight
;
923 controlPos
.bottom
= controlPos
.top
+ GetSystemMetrics(SM_CYHSCROLL
);
927 controlPos
.bottom
= clientRect
->top
+ infoPtr
->tabHeight
;
928 controlPos
.top
= controlPos
.bottom
- GetSystemMetrics(SM_CYHSCROLL
);
933 * If we don't have a scroll control yet, we want to create one.
934 * If we have one, we want to make sure it's positioned properly.
936 if (infoPtr
->hwndUpDown
==0)
938 infoPtr
->hwndUpDown
= CreateWindowA("msctls_updown32",
940 WS_VISIBLE
| WS_CHILD
| UDS_HORZ
,
941 controlPos
.left
, controlPos
.top
,
942 controlPos
.right
- controlPos
.left
,
943 controlPos
.bottom
- controlPos
.top
,
951 SetWindowPos(infoPtr
->hwndUpDown
,
953 controlPos
.left
, controlPos
.top
,
954 controlPos
.right
- controlPos
.left
,
955 controlPos
.bottom
- controlPos
.top
,
956 SWP_SHOWWINDOW
| SWP_NOZORDER
);
959 /* Now calculate upper limit of the updown control range.
960 * We do this by calculating how many tabs will be offscreen when the
961 * last tab is visible.
963 if(infoPtr
->uNumItem
)
965 vsize
= clientRect
->right
- (controlPos
.right
- controlPos
.left
+ 1);
966 maxRange
= infoPtr
->uNumItem
;
967 tabwidth
= infoPtr
->items
[maxRange
- 1].rect
.right
;
969 for(; maxRange
> 0; maxRange
--)
971 if(tabwidth
- infoPtr
->items
[maxRange
- 1].rect
.left
> vsize
)
975 if(maxRange
== infoPtr
->uNumItem
)
981 /* If we once had a scroll control... hide it */
982 if (infoPtr
->hwndUpDown
!=0)
983 ShowWindow(infoPtr
->hwndUpDown
, SW_HIDE
);
985 if (infoPtr
->hwndUpDown
)
986 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETRANGE32
, 0, maxRange
);
989 /******************************************************************************
992 * This method will calculate the position rectangles of all the items in the
993 * control. The rectangle calculated starts at 0 for the first item in the
994 * list and ignores scrolling and selection.
995 * It also uses the current font to determine the height of the tab row and
996 * it checks if all the tabs fit in the client area of the window. If they
997 * dont, a scrolling control is added.
999 static void TAB_SetItemBounds (HWND hwnd
)
1001 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
1002 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1003 TEXTMETRICA fontMetrics
;
1006 INT curItemRowCount
;
1007 HFONT hFont
, hOldFont
;
1016 * We need to get text information so we need a DC and we need to select
1021 hFont
= infoPtr
->hFont
? infoPtr
->hFont
: GetStockObject (SYSTEM_FONT
);
1022 hOldFont
= SelectObject (hdc
, hFont
);
1025 * We will base the rectangle calculations on the client rectangle
1028 GetClientRect(hwnd
, &clientRect
);
1030 /* if TCS_VERTICAL then swap the height and width so this code places the
1031 tabs along the top of the rectangle and we can just rotate them after
1032 rather than duplicate all of the below code */
1033 if(lStyle
& TCS_VERTICAL
)
1035 iTemp
= clientRect
.bottom
;
1036 clientRect
.bottom
= clientRect
.right
;
1037 clientRect
.right
= iTemp
;
1040 /* The leftmost item will be "0" aligned */
1042 curItemRowCount
= infoPtr
->uNumItem
? 1 : 0;
1044 if (!(lStyle
& TCS_FIXEDWIDTH
) && !((lStyle
& TCS_OWNERDRAWFIXED
) && infoPtr
->fSizeSet
) )
1047 int icon_height
= 0;
1049 /* Use the current font to determine the height of a tab. */
1050 GetTextMetricsA(hdc
, &fontMetrics
);
1052 /* Get the icon height */
1054 ImageList_GetIconSize(infoPtr
->himl
, 0, &icon_height
);
1056 /* Take the highest between font or icon */
1057 if (fontMetrics
.tmHeight
> icon_height
)
1058 item_height
= fontMetrics
.tmHeight
;
1060 item_height
= icon_height
;
1063 * Make sure there is enough space for the letters + icon + growing the
1064 * selected item + extra space for the selected item.
1066 infoPtr
->tabHeight
= item_height
+ 2 * VERTICAL_ITEM_PADDING
+
1067 SELECTED_TAB_OFFSET
;
1071 for (curItem
= 0; curItem
< infoPtr
->uNumItem
; curItem
++)
1073 /* Set the leftmost position of the tab. */
1074 infoPtr
->items
[curItem
].rect
.left
= curItemLeftPos
;
1076 if ( (lStyle
& TCS_FIXEDWIDTH
) || ((lStyle
& TCS_OWNERDRAWFIXED
) && infoPtr
->fSizeSet
))
1078 infoPtr
->items
[curItem
].rect
.right
= infoPtr
->items
[curItem
].rect
.left
+
1080 2 * HORIZONTAL_ITEM_PADDING
;
1087 /* Calculate how wide the tab is depending on the text it contains */
1088 GetTextExtentPoint32W(hdc
, infoPtr
->items
[curItem
].pszText
,
1089 lstrlenW(infoPtr
->items
[curItem
].pszText
), &size
);
1091 /* under Windows, there seems to be a minimum width of 2x the height
1092 * for button style tabs */
1093 if (lStyle
& TCS_BUTTONS
)
1094 size
.cx
= max(size
.cx
, 2 * (infoPtr
->tabHeight
- 2));
1096 /* Add the icon width */
1099 ImageList_GetIconSize(infoPtr
->himl
, &icon_width
, 0);
1103 infoPtr
->items
[curItem
].rect
.right
= infoPtr
->items
[curItem
].rect
.left
+
1104 size
.cx
+ icon_width
+
1105 num
* HORIZONTAL_ITEM_PADDING
;
1109 * Check if this is a multiline tab control and if so
1110 * check to see if we should wrap the tabs
1112 * Because we are going to arange all these tabs evenly
1113 * really we are basically just counting rows at this point
1117 if (((lStyle
& TCS_MULTILINE
) || (lStyle
& TCS_VERTICAL
)) &&
1118 (infoPtr
->items
[curItem
].rect
.right
> clientRect
.right
))
1120 infoPtr
->items
[curItem
].rect
.right
-=
1121 infoPtr
->items
[curItem
].rect
.left
;
1123 infoPtr
->items
[curItem
].rect
.left
= 0;
1127 infoPtr
->items
[curItem
].rect
.bottom
= 0;
1128 infoPtr
->items
[curItem
].rect
.top
= curItemRowCount
- 1;
1130 TRACE("TextSize: %li\n", size
.cx
);
1131 TRACE("Rect: T %i, L %i, B %i, R %i\n",
1132 infoPtr
->items
[curItem
].rect
.top
,
1133 infoPtr
->items
[curItem
].rect
.left
,
1134 infoPtr
->items
[curItem
].rect
.bottom
,
1135 infoPtr
->items
[curItem
].rect
.right
);
1138 * The leftmost position of the next item is the rightmost position
1141 if (lStyle
& TCS_BUTTONS
)
1143 curItemLeftPos
= infoPtr
->items
[curItem
].rect
.right
+ 1;
1144 if (lStyle
& TCS_FLATBUTTONS
)
1145 curItemLeftPos
+= FLAT_BTN_SPACINGX
;
1148 curItemLeftPos
= infoPtr
->items
[curItem
].rect
.right
;
1151 if (!((lStyle
& TCS_MULTILINE
) || (lStyle
& TCS_VERTICAL
)))
1154 * Check if we need a scrolling control.
1156 infoPtr
->needsScrolling
= (curItemLeftPos
+ (2 * SELECTED_TAB_OFFSET
) >
1159 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1160 if(!infoPtr
->needsScrolling
)
1161 infoPtr
->leftmostVisible
= 0;
1163 TAB_SetupScrolling(hwnd
, infoPtr
, &clientRect
);
1166 /* Set the number of rows */
1167 infoPtr
->uNumRows
= curItemRowCount
;
1169 if (((lStyle
& TCS_MULTILINE
) || (lStyle
& TCS_VERTICAL
)) && (infoPtr
->uNumItem
> 0))
1171 INT widthDiff
, remainder
;
1172 INT tabPerRow
,remTab
;
1174 INT iIndexStart
=0,iIndexEnd
=0, iCount
=0;
1177 * Ok windows tries to even out the rows. place the same
1178 * number of tabs in each row. So lets give that a shot
1181 tabPerRow
= infoPtr
->uNumItem
/ (infoPtr
->uNumRows
);
1182 remTab
= infoPtr
->uNumItem
% (infoPtr
->uNumRows
);
1184 for (iItm
=0,iRow
=0,iCount
=0,curItemLeftPos
=0;
1185 iItm
<infoPtr
->uNumItem
;
1188 /* if we have reached the maximum number of tabs on this row */
1189 /* move to the next row, reset our current item left position and */
1190 /* the count of items on this row */
1191 if (iCount
>= ((iRow
<remTab
)?tabPerRow
+ 1:tabPerRow
))
1198 /* normalize the current rect */
1200 /* shift the item to the left side of the clientRect */
1201 infoPtr
->items
[iItm
].rect
.right
-=
1202 infoPtr
->items
[iItm
].rect
.left
;
1203 infoPtr
->items
[iItm
].rect
.left
= 0;
1205 /* shift the item to the right to place it as the next item in this row */
1206 infoPtr
->items
[iItm
].rect
.left
+= curItemLeftPos
;
1207 infoPtr
->items
[iItm
].rect
.right
+= curItemLeftPos
;
1208 infoPtr
->items
[iItm
].rect
.top
= iRow
;
1209 if (lStyle
& TCS_BUTTONS
)
1211 curItemLeftPos
= infoPtr
->items
[iItm
].rect
.right
+ 1;
1212 if (lStyle
& TCS_FLATBUTTONS
)
1213 curItemLeftPos
+= FLAT_BTN_SPACINGX
;
1216 curItemLeftPos
= infoPtr
->items
[iItm
].rect
.right
;
1223 while(iIndexStart
< infoPtr
->uNumItem
)
1226 * find the indexs of the row
1228 /* find the first item on the next row */
1229 for (iIndexEnd
=iIndexStart
;
1230 (iIndexEnd
< infoPtr
->uNumItem
) &&
1231 (infoPtr
->items
[iIndexEnd
].rect
.top
==
1232 infoPtr
->items
[iIndexStart
].rect
.top
) ;
1234 /* intentionally blank */;
1237 * we need to justify these tabs so they fill the whole given
1241 /* find the amount of space remaining on this row */
1242 widthDiff
= clientRect
.right
- (2 * SELECTED_TAB_OFFSET
) -
1243 infoPtr
->items
[iIndexEnd
- 1].rect
.right
;
1245 /* iCount is the number of tab items on this row */
1246 iCount
= iIndexEnd
- iIndexStart
;
1251 remainder
= widthDiff
% iCount
;
1252 widthDiff
= widthDiff
/ iCount
;
1253 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1254 for (iIndex
=iIndexStart
,iCount
=0; iIndex
< iIndexEnd
;
1257 infoPtr
->items
[iIndex
].rect
.left
+= iCount
* widthDiff
;
1258 infoPtr
->items
[iIndex
].rect
.right
+= (iCount
+ 1) * widthDiff
;
1260 infoPtr
->items
[iIndex
- 1].rect
.right
+= remainder
;
1262 else /* we have only one item on this row, make it take up the entire row */
1264 infoPtr
->items
[iIndexStart
].rect
.left
= clientRect
.left
;
1265 infoPtr
->items
[iIndexStart
].rect
.right
= clientRect
.right
- 4;
1269 iIndexStart
= iIndexEnd
;
1274 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1275 if(lStyle
& TCS_VERTICAL
)
1278 for(iIndex
= 0; iIndex
< infoPtr
->uNumItem
; iIndex
++)
1280 rcItem
= &(infoPtr
->items
[iIndex
].rect
);
1282 rcOriginal
= *rcItem
;
1284 /* this is rotating the items by 90 degrees around the center of the control */
1285 rcItem
->top
= (clientRect
.right
- (rcOriginal
.left
- clientRect
.left
)) - (rcOriginal
.right
- rcOriginal
.left
);
1286 rcItem
->bottom
= rcItem
->top
+ (rcOriginal
.right
- rcOriginal
.left
);
1287 rcItem
->left
= rcOriginal
.top
;
1288 rcItem
->right
= rcOriginal
.bottom
;
1292 TAB_EnsureSelectionVisible(hwnd
,infoPtr
);
1293 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
1296 SelectObject (hdc
, hOldFont
);
1297 ReleaseDC (hwnd
, hdc
);
1300 /******************************************************************************
1301 * TAB_DrawItemInterior
1303 * This method is used to draw the interior (text and icon) of a single tab
1304 * into the tab control.
1307 TAB_DrawItemInterior
1315 TAB_INFO
* infoPtr
= TAB_GetInfoPtr(hwnd
);
1316 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1320 HPEN htextPen
= GetSysColorPen (COLOR_BTNTEXT
);
1324 if (drawRect
== NULL
)
1331 * Get the rectangle for the item.
1333 isVisible
= TAB_InternalGetItemRect(hwnd
, infoPtr
, iItem
, &itemRect
, &selectedRect
);
1338 * Make sure drawRect points to something valid; simplifies code.
1340 drawRect
= &localRect
;
1343 * This logic copied from the part of TAB_DrawItem which draws
1344 * the tab background. It's important to keep it in sync. I
1345 * would have liked to avoid code duplication, but couldn't figure
1346 * out how without making spaghetti of TAB_DrawItem.
1348 if (lStyle
& TCS_BUTTONS
)
1350 *drawRect
= itemRect
;
1351 if (iItem
== infoPtr
->iSelected
)
1359 if (iItem
== infoPtr
->iSelected
)
1360 *drawRect
= selectedRect
;
1362 *drawRect
= itemRect
;
1371 holdPen
= SelectObject(hdc
, htextPen
);
1373 oldBkMode
= SetBkMode(hdc
, TRANSPARENT
);
1374 SetTextColor(hdc
, GetSysColor((iItem
== infoPtr
->iHotTracked
) ? COLOR_HIGHLIGHT
: COLOR_BTNTEXT
));
1377 * Deflate the rectangle to acount for the padding
1379 if(lStyle
& TCS_VERTICAL
)
1380 InflateRect(drawRect
, -VERTICAL_ITEM_PADDING
, -HORIZONTAL_ITEM_PADDING
);
1382 InflateRect(drawRect
, -HORIZONTAL_ITEM_PADDING
, -VERTICAL_ITEM_PADDING
);
1386 * if owner draw, tell the owner to draw
1388 if ((lStyle
& TCS_OWNERDRAWFIXED
) && GetParent(hwnd
))
1394 * get the control id
1396 id
= GetWindowLongA( hwnd
, GWL_ID
);
1399 * put together the DRAWITEMSTRUCT
1401 dis
.CtlType
= ODT_TAB
;
1404 dis
.itemAction
= ODA_DRAWENTIRE
;
1405 if ( iItem
== infoPtr
->iSelected
)
1406 dis
.itemState
= ODS_SELECTED
;
1409 dis
.hwndItem
= hwnd
; /* */
1411 dis
.rcItem
= *drawRect
; /* */
1412 dis
.itemData
= infoPtr
->items
[iItem
].lParam
;
1415 * send the draw message
1417 SendMessageA( GetParent(hwnd
), WM_DRAWITEM
, (WPARAM
)id
, (LPARAM
)&dis
);
1428 HFONT hOldFont
= 0; /* stop uninitialized warning */
1430 INT nEscapement
= 0; /* stop uninitialized warning */
1431 INT nOrientation
= 0; /* stop uninitialized warning */
1434 /* used to center the icon and text in the tab */
1438 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1439 rcImage
= *drawRect
;
1444 * Setup for text output
1446 oldBkMode
= SetBkMode(hdc
, TRANSPARENT
);
1447 SetTextColor(hdc
, GetSysColor((iItem
== infoPtr
->iHotTracked
) ? COLOR_HIGHLIGHT
: COLOR_BTNTEXT
));
1449 /* get the rectangle that the text fits in */
1450 DrawTextW(hdc
, infoPtr
->items
[iItem
].pszText
, -1,
1451 &rcText
, DT_CALCRECT
);
1454 * If not owner draw, then do the drawing ourselves.
1458 if (infoPtr
->himl
&& (infoPtr
->items
[iItem
].mask
& TCIF_IMAGE
))
1460 ImageList_GetIconSize(infoPtr
->himl
, &cx
, &cy
);
1462 if(lStyle
& TCS_VERTICAL
)
1463 center_offset
= ((drawRect
->bottom
- drawRect
->top
) - (cy
+ VERTICAL_ITEM_PADDING
+ (rcText
.right
- rcText
.left
))) / 2;
1465 center_offset
= ((drawRect
->right
- drawRect
->left
) - (cx
+ HORIZONTAL_ITEM_PADDING
+ (rcText
.right
- rcText
.left
))) / 2;
1467 if((lStyle
& TCS_VERTICAL
) && (lStyle
& TCS_BOTTOM
))
1469 /* rcImage.left = drawRect->left; */ /* explicit from above rcImage = *drawRect */
1470 rcImage
.top
= drawRect
->top
+ center_offset
;
1471 rcImage
.left
= drawRect
->right
- cx
; /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1472 /* right side of the tab, but the image still uses the left as its x position */
1473 /* this keeps the image always drawn off of the same side of the tab */
1474 drawRect
->top
= rcImage
.top
+ (cx
+ VERTICAL_ITEM_PADDING
);
1476 else if(lStyle
& TCS_VERTICAL
)
1478 /* rcImage.left = drawRect->left; */ /* explicit from above rcImage = *drawRect */
1479 rcImage
.top
= drawRect
->bottom
- cy
- center_offset
;
1481 drawRect
->bottom
= rcImage
.top
- VERTICAL_ITEM_PADDING
;
1483 else /* normal style, whether TCS_BOTTOM or not */
1485 rcImage
.left
= drawRect
->left
+ center_offset
;
1486 /* rcImage.top = drawRect->top; */ /* explicit from above rcImage = *drawRect */
1488 drawRect
->left
= rcImage
.left
+ cx
+ HORIZONTAL_ITEM_PADDING
;
1494 infoPtr
->items
[iItem
].iImage
,
1500 } else /* no image, so just shift the drawRect borders around */
1502 if(lStyle
& TCS_VERTICAL
)
1506 currently the rcText rect is flawed because the rotated font does not
1507 often match the horizontal font. So leave this as 0
1508 ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1510 if(lStyle
& TCS_BOTTOM
)
1511 drawRect
->top
+=center_offset
;
1513 drawRect
->bottom
-=center_offset
;
1517 center_offset
= ((drawRect
->right
- drawRect
->left
) - (rcText
.right
- rcText
.left
)) / 2;
1518 drawRect
->left
+=center_offset
;
1523 if (lStyle
& TCS_RIGHTJUSTIFY
)
1524 uHorizAlign
= DT_CENTER
;
1526 uHorizAlign
= DT_LEFT
;
1528 if(lStyle
& TCS_VERTICAL
) /* if we are vertical rotate the text and each character */
1530 if(lStyle
& TCS_BOTTOM
)
1533 nOrientation
= -900;
1542 /* to get a font with the escapement and orientation we are looking for, we need to */
1543 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1544 if(lStyle
& TCS_VERTICAL
)
1546 if (!GetObjectA((infoPtr
->hFont
) ?
1547 infoPtr
->hFont
: GetStockObject(SYSTEM_FONT
),
1548 sizeof(LOGFONTA
),&logfont
))
1552 lstrcpyA(logfont
.lfFaceName
, "Arial");
1553 logfont
.lfHeight
= -MulDiv(iPointSize
, GetDeviceCaps(hdc
, LOGPIXELSY
),
1555 logfont
.lfWeight
= FW_NORMAL
;
1556 logfont
.lfItalic
= 0;
1557 logfont
.lfUnderline
= 0;
1558 logfont
.lfStrikeOut
= 0;
1561 logfont
.lfEscapement
= nEscapement
;
1562 logfont
.lfOrientation
= nOrientation
;
1563 hFont
= CreateFontIndirectA(&logfont
);
1564 hOldFont
= SelectObject(hdc
, hFont
);
1567 if (lStyle
& TCS_VERTICAL
)
1570 (lStyle
& TCS_BOTTOM
) ? drawRect
->right
: drawRect
->left
,
1571 (!(lStyle
& TCS_BOTTOM
)) ? drawRect
->bottom
: drawRect
->top
,
1574 infoPtr
->items
[iItem
].pszText
,
1575 lstrlenW(infoPtr
->items
[iItem
].pszText
),
1583 infoPtr
->items
[iItem
].pszText
,
1584 lstrlenW(infoPtr
->items
[iItem
].pszText
),
1586 uHorizAlign
| DT_SINGLELINE
1590 /* clean things up */
1591 *drawRect
= rcTemp
; /* restore drawRect */
1593 if(lStyle
& TCS_VERTICAL
)
1595 SelectObject(hdc
, hOldFont
); /* restore the original font */
1597 DeleteObject(hFont
);
1604 SetBkMode(hdc
, oldBkMode
);
1605 SelectObject(hdc
, holdPen
);
1608 /******************************************************************************
1611 * This method is used to draw a single tab into the tab control.
1613 static void TAB_DrawItem(
1618 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1619 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1626 * Get the rectangle for the item.
1628 isVisible
= TAB_InternalGetItemRect(hwnd
,
1636 HBRUSH hbr
= CreateSolidBrush (GetSysColor(COLOR_BTNFACE
));
1637 HPEN hwPen
= GetSysColorPen (COLOR_3DHILIGHT
);
1638 HPEN hbPen
= GetSysColorPen (COLOR_3DDKSHADOW
);
1639 HPEN hShade
= GetSysColorPen (COLOR_BTNSHADOW
);
1642 BOOL deleteBrush
= TRUE
;
1644 if (lStyle
& TCS_BUTTONS
)
1646 /* Get item rectangle */
1649 holdPen
= SelectObject (hdc
, hwPen
);
1651 /* Separators between flat buttons */
1652 /* FIXME: test and correct this if necessary for TCS_FLATBUTTONS style */
1653 if (lStyle
& TCS_FLATBUTTONS
)
1655 int x
= r
.right
+ FLAT_BTN_SPACINGX
- 2;
1658 MoveToEx (hdc
, x
, r
.bottom
- 1, NULL
);
1659 LineTo (hdc
, x
, r
.top
- 1);
1663 SelectObject(hdc
, hbPen
);
1664 MoveToEx (hdc
, x
, r
.bottom
- 1, NULL
);
1665 LineTo (hdc
, x
, r
.top
- 1);
1668 SelectObject (hdc
, hShade
);
1669 MoveToEx (hdc
, x
- 1, r
.bottom
- 1, NULL
);
1670 LineTo (hdc
, x
- 1, r
.top
- 1);
1673 if (iItem
== infoPtr
->iSelected
)
1675 /* Background color */
1676 if (!((lStyle
& TCS_OWNERDRAWFIXED
) && infoPtr
->fSizeSet
))
1678 COLORREF bk
= GetSysColor(COLOR_3DHILIGHT
);
1680 hbr
= GetSysColorBrush(COLOR_SCROLLBAR
);
1682 SetTextColor(hdc
, GetSysColor(COLOR_3DFACE
));
1683 SetBkColor(hdc
, bk
);
1685 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1686 * we better use 0x55aa bitmap brush to make scrollbar's background
1687 * look different from the window background.
1689 if (bk
== GetSysColor(COLOR_WINDOW
))
1690 hbr
= COMCTL32_hPattern55AABrush
;
1692 deleteBrush
= FALSE
;
1695 /* Erase the background */
1696 FillRect(hdc
, &r
, hbr
);
1700 * The rectangles calculated exclude the right and bottom
1701 * borders of the rectangle. To simplify the following code, those
1702 * borders are shaved-off beforehand.
1708 SelectObject(hdc
, hwPen
);
1709 MoveToEx (hdc
, r
.left
, r
.bottom
, NULL
);
1710 LineTo (hdc
, r
.right
, r
.bottom
);
1711 LineTo (hdc
, r
.right
, r
.top
+ 1);
1714 SelectObject(hdc
, hbPen
);
1715 LineTo (hdc
, r
.left
+ 1, r
.top
+ 1);
1716 LineTo (hdc
, r
.left
+ 1, r
.bottom
);
1719 SelectObject (hdc
, hShade
);
1720 MoveToEx (hdc
, r
.right
, r
.top
, NULL
);
1721 LineTo (hdc
, r
.left
, r
.top
);
1722 LineTo (hdc
, r
.left
, r
.bottom
);
1726 /* Erase the background */
1727 FillRect(hdc
, &r
, hbr
);
1729 if (!(lStyle
& TCS_FLATBUTTONS
))
1732 MoveToEx (hdc
, r
.left
, r
.bottom
, NULL
);
1733 LineTo (hdc
, r
.left
, r
.top
);
1734 LineTo (hdc
, r
.right
, r
.top
);
1737 SelectObject(hdc
, hbPen
);
1738 LineTo (hdc
, r
.right
, r
.bottom
);
1739 LineTo (hdc
, r
.left
, r
.bottom
);
1742 SelectObject (hdc
, hShade
);
1743 MoveToEx (hdc
, r
.right
- 1, r
.top
, NULL
);
1744 LineTo (hdc
, r
.right
- 1, r
.bottom
- 1);
1745 LineTo (hdc
, r
.left
+ 1, r
.bottom
- 1);
1749 else /* !TCS_BUTTONS */
1751 /* Background color */
1753 hbr
= CreateSolidBrush(GetSysColor(COLOR_BTNFACE
));
1755 /* We draw a rectangle of different sizes depending on the selection
1757 if (iItem
== infoPtr
->iSelected
)
1763 * Erase the background.
1764 * This is necessary when drawing the selected item since it is larger
1765 * than the others, it might overlap with stuff already drawn by the
1768 FillRect(hdc
, &r
, hbr
);
1772 * The rectangles calculated exclude the right and bottom
1773 * borders of the rectangle. To simplify the following code, those
1774 * borders are shaved-off beforehand.
1779 holdPen
= SelectObject (hdc
, hwPen
);
1780 if(lStyle
& TCS_VERTICAL
)
1782 if (lStyle
& TCS_BOTTOM
)
1785 MoveToEx (hdc
, r
.left
, r
.top
, NULL
);
1786 LineTo (hdc
, r
.right
- ROUND_CORNER_SIZE
, r
.top
);
1787 LineTo (hdc
, r
.right
, r
.top
+ ROUND_CORNER_SIZE
);
1790 SelectObject(hdc
, hbPen
);
1791 LineTo (hdc
, r
.right
, r
.bottom
- ROUND_CORNER_SIZE
);
1792 LineTo (hdc
, r
.right
- ROUND_CORNER_SIZE
, r
.bottom
);
1793 LineTo (hdc
, r
.left
- 1, r
.bottom
);
1796 SelectObject (hdc
, hShade
);
1797 MoveToEx (hdc
, r
.right
- 1, r
.top
, NULL
);
1798 LineTo (hdc
, r
.right
- 1, r
.bottom
- 1);
1799 LineTo (hdc
, r
.left
- 1, r
.bottom
- 1);
1804 MoveToEx (hdc
, r
.right
, r
.top
, NULL
);
1805 LineTo (hdc
, r
.left
+ ROUND_CORNER_SIZE
, r
.top
);
1806 LineTo (hdc
, r
.left
, r
.top
+ ROUND_CORNER_SIZE
);
1807 LineTo (hdc
, r
.left
, r
.bottom
- ROUND_CORNER_SIZE
);
1810 SelectObject(hdc
, hbPen
);
1811 LineTo (hdc
, r
.left
+ ROUND_CORNER_SIZE
, r
.bottom
);
1812 LineTo (hdc
, r
.right
+ 1, r
.bottom
);
1815 SelectObject (hdc
, hShade
);
1816 MoveToEx (hdc
, r
.left
+ ROUND_CORNER_SIZE
- 1, r
.bottom
- 1, NULL
);
1817 LineTo (hdc
, r
.right
+ 1, r
.bottom
- 1);
1822 if (lStyle
& TCS_BOTTOM
)
1825 MoveToEx (hdc
, r
.left
, r
.top
, NULL
);
1826 LineTo (hdc
, r
.left
, r
.bottom
- ROUND_CORNER_SIZE
);
1827 LineTo (hdc
, r
.left
+ ROUND_CORNER_SIZE
, r
.bottom
);
1830 SelectObject(hdc
, hbPen
);
1831 LineTo (hdc
, r
.right
- ROUND_CORNER_SIZE
, r
.bottom
);
1832 LineTo (hdc
, r
.right
, r
.bottom
- ROUND_CORNER_SIZE
);
1833 LineTo (hdc
, r
.right
, r
.top
- 1);
1836 SelectObject (hdc
, hShade
);
1837 MoveToEx (hdc
, r
.left
, r
.bottom
- 1, NULL
);
1838 LineTo (hdc
, r
.right
- ROUND_CORNER_SIZE
- 1, r
.bottom
- 1);
1839 LineTo (hdc
, r
.right
- 1, r
.bottom
- ROUND_CORNER_SIZE
- 1);
1840 LineTo (hdc
, r
.right
- 1, r
.top
- 1);
1845 if(infoPtr
->items
[iItem
].rect
.left
== 0) /* if leftmost draw the line longer */
1846 MoveToEx (hdc
, r
.left
, r
.bottom
, NULL
);
1848 MoveToEx (hdc
, r
.left
, r
.bottom
- 1, NULL
);
1850 LineTo (hdc
, r
.left
, r
.top
+ ROUND_CORNER_SIZE
);
1851 LineTo (hdc
, r
.left
+ ROUND_CORNER_SIZE
, r
.top
);
1852 LineTo (hdc
, r
.right
- ROUND_CORNER_SIZE
, r
.top
);
1855 SelectObject(hdc
, hbPen
);
1856 LineTo (hdc
, r
.right
, r
.top
+ ROUND_CORNER_SIZE
);
1857 LineTo (hdc
, r
.right
, r
.bottom
+ 1);
1861 SelectObject (hdc
, hShade
);
1862 MoveToEx (hdc
, r
.right
- 1, r
.top
+ ROUND_CORNER_SIZE
, NULL
);
1863 LineTo (hdc
, r
.right
- 1, r
.bottom
+ 1);
1868 /* This modifies r to be the text rectangle. */
1870 HFONT hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
1871 TAB_DrawItemInterior(hwnd
, hdc
, iItem
, &r
);
1872 SelectObject(hdc
,hOldFont
);
1875 /* Draw the focus rectangle */
1876 if (((lStyle
& TCS_FOCUSNEVER
) == 0) &&
1877 (GetFocus() == hwnd
) &&
1878 (iItem
== infoPtr
->uFocus
) )
1881 InflateRect(&r
, -1, -1);
1883 DrawFocusRect(hdc
, &r
);
1887 SelectObject(hdc
, holdPen
);
1888 if (deleteBrush
) DeleteObject(hbr
);
1892 /******************************************************************************
1895 * This method is used to draw the raised border around the tab control
1898 static void TAB_DrawBorder (HWND hwnd
, HDC hdc
)
1900 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1902 HPEN hwPen
= GetSysColorPen (COLOR_3DHILIGHT
);
1903 HPEN hbPen
= GetSysColorPen (COLOR_3DDKSHADOW
);
1904 HPEN hShade
= GetSysColorPen (COLOR_BTNSHADOW
);
1906 DWORD lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
1908 GetClientRect (hwnd
, &rect
);
1911 * Adjust for the style
1914 if (infoPtr
->uNumItem
)
1916 if ((lStyle
& TCS_BOTTOM
) && !(lStyle
& TCS_VERTICAL
))
1918 rect
.bottom
-= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
1920 else if((lStyle
& TCS_BOTTOM
) && (lStyle
& TCS_VERTICAL
))
1922 rect
.right
-= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
1924 else if(lStyle
& TCS_VERTICAL
)
1926 rect
.left
+= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 2;
1928 else /* not TCS_VERTICAL and not TCS_BOTTOM */
1930 rect
.top
+= (infoPtr
->tabHeight
- 2) * infoPtr
->uNumRows
+ 1;
1935 * Shave-off the right and bottom margins (exluded in the
1942 htmPen
= SelectObject (hdc
, hwPen
);
1944 MoveToEx (hdc
, rect
.left
, rect
.bottom
, NULL
);
1945 LineTo (hdc
, rect
.left
, rect
.top
);
1946 LineTo (hdc
, rect
.right
, rect
.top
);
1949 SelectObject (hdc
, hbPen
);
1950 LineTo (hdc
, rect
.right
, rect
.bottom
);
1951 LineTo (hdc
, rect
.left
, rect
.bottom
);
1954 SelectObject (hdc
, hShade
);
1955 MoveToEx (hdc
, rect
.right
- 1, rect
.top
, NULL
);
1956 LineTo (hdc
, rect
.right
- 1, rect
.bottom
- 1);
1957 LineTo (hdc
, rect
.left
, rect
.bottom
- 1);
1959 SelectObject(hdc
, htmPen
);
1962 /******************************************************************************
1965 * This method repaints the tab control..
1967 static void TAB_Refresh (HWND hwnd
, HDC hdc
)
1969 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
1973 if (!infoPtr
->DoRedraw
)
1976 hOldFont
= SelectObject (hdc
, infoPtr
->hFont
);
1978 if (GetWindowLongA(hwnd
, GWL_STYLE
) & TCS_BUTTONS
)
1980 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
1981 TAB_DrawItem (hwnd
, hdc
, i
);
1985 /* Draw all the non selected item first */
1986 for (i
= 0; i
< infoPtr
->uNumItem
; i
++)
1988 if (i
!= infoPtr
->iSelected
)
1989 TAB_DrawItem (hwnd
, hdc
, i
);
1992 /* Now, draw the border, draw it before the selected item
1993 * since the selected item overwrites part of the border. */
1994 TAB_DrawBorder (hwnd
, hdc
);
1996 /* Then, draw the selected item */
1997 TAB_DrawItem (hwnd
, hdc
, infoPtr
->iSelected
);
1999 /* If we haven't set the current focus yet, set it now.
2000 * Only happens when we first paint the tab controls */
2001 if (infoPtr
->uFocus
== -1)
2002 TAB_SetCurFocus(hwnd
, infoPtr
->iSelected
);
2005 SelectObject (hdc
, hOldFont
);
2009 TAB_GetRowCount (HWND hwnd
)
2011 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2013 return infoPtr
->uNumRows
;
2017 TAB_SetRedraw (HWND hwnd
, WPARAM wParam
)
2019 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2021 infoPtr
->DoRedraw
=(BOOL
) wParam
;
2025 static LRESULT
TAB_EraseBackground(
2032 HBRUSH brush
= CreateSolidBrush(GetSysColor(COLOR_BTNFACE
));
2034 hdc
= givenDC
? givenDC
: GetDC(hwnd
);
2036 GetClientRect(hwnd
, &clientRect
);
2038 FillRect(hdc
, &clientRect
, brush
);
2041 ReleaseDC(hwnd
, hdc
);
2043 DeleteObject(brush
);
2048 /******************************************************************************
2049 * TAB_EnsureSelectionVisible
2051 * This method will make sure that the current selection is completely
2052 * visible by scrolling until it is.
2054 static void TAB_EnsureSelectionVisible(
2058 INT iSelected
= infoPtr
->iSelected
;
2059 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
2060 INT iOrigLeftmostVisible
= infoPtr
->leftmostVisible
;
2062 /* set the items row to the bottommost row or topmost row depending on
2064 if ((infoPtr
->uNumRows
> 1) && !(lStyle
& TCS_BUTTONS
))
2069 if(lStyle
& TCS_VERTICAL
)
2070 newselected
= infoPtr
->items
[iSelected
].rect
.left
;
2072 newselected
= infoPtr
->items
[iSelected
].rect
.top
;
2074 /* the target row is always (number of rows - 1)
2075 as row 0 is furthest from the clientRect */
2076 iTargetRow
= infoPtr
->uNumRows
- 1;
2078 if (newselected
!= iTargetRow
)
2081 if(lStyle
& TCS_VERTICAL
)
2083 for (i
=0; i
< infoPtr
->uNumItem
; i
++)
2085 /* move everything in the row of the selected item to the iTargetRow */
2086 if (infoPtr
->items
[i
].rect
.left
== newselected
)
2087 infoPtr
->items
[i
].rect
.left
= iTargetRow
;
2090 if (infoPtr
->items
[i
].rect
.left
> newselected
)
2091 infoPtr
->items
[i
].rect
.left
-=1;
2097 for (i
=0; i
< infoPtr
->uNumItem
; i
++)
2099 if (infoPtr
->items
[i
].rect
.top
== newselected
)
2100 infoPtr
->items
[i
].rect
.top
= iTargetRow
;
2103 if (infoPtr
->items
[i
].rect
.top
> newselected
)
2104 infoPtr
->items
[i
].rect
.top
-=1;
2108 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
2113 * Do the trivial cases first.
2115 if ( (!infoPtr
->needsScrolling
) ||
2116 (infoPtr
->hwndUpDown
==0) || (lStyle
& TCS_VERTICAL
))
2119 if (infoPtr
->leftmostVisible
>= iSelected
)
2121 infoPtr
->leftmostVisible
= iSelected
;
2128 /* Calculate the part of the client area that is visible */
2129 GetClientRect(hwnd
, &r
);
2132 GetClientRect(infoPtr
->hwndUpDown
, &r
);
2135 if ((infoPtr
->items
[iSelected
].rect
.right
-
2136 infoPtr
->items
[iSelected
].rect
.left
) >= width
)
2138 /* Special case: width of selected item is greater than visible
2141 infoPtr
->leftmostVisible
= iSelected
;
2145 for (i
= infoPtr
->leftmostVisible
; i
< infoPtr
->uNumItem
; i
++)
2147 if ((infoPtr
->items
[iSelected
].rect
.right
-
2148 infoPtr
->items
[i
].rect
.left
) < width
)
2151 infoPtr
->leftmostVisible
= i
;
2155 if (infoPtr
->leftmostVisible
!= iOrigLeftmostVisible
)
2156 TAB_RecalcHotTrack(hwnd
, NULL
, NULL
, NULL
);
2158 SendMessageA(infoPtr
->hwndUpDown
, UDM_SETPOS
, 0,
2159 MAKELONG(infoPtr
->leftmostVisible
, 0));
2162 /******************************************************************************
2163 * TAB_InvalidateTabArea
2165 * This method will invalidate the portion of the control that contains the
2166 * tabs. It is called when the state of the control changes and needs
2169 static void TAB_InvalidateTabArea(
2174 DWORD lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
2175 INT lastRow
= infoPtr
->uNumRows
- 1;
2177 if (lastRow
< 0) return;
2179 GetClientRect(hwnd
, &clientRect
);
2181 if ((lStyle
& TCS_BOTTOM
) && !(lStyle
& TCS_VERTICAL
))
2183 clientRect
.top
= clientRect
.bottom
-
2184 infoPtr
->tabHeight
-
2185 lastRow
* (infoPtr
->tabHeight
- 2) -
2186 ((lStyle
& TCS_BUTTONS
) ? lastRow
* BUTTON_SPACINGY
: 0) - 2;
2188 else if((lStyle
& TCS_BOTTOM
) && (lStyle
& TCS_VERTICAL
))
2190 clientRect
.left
= clientRect
.right
- infoPtr
->tabHeight
-
2191 lastRow
* (infoPtr
->tabHeight
- 2) -
2192 ((lStyle
& TCS_BUTTONS
) ? lastRow
* BUTTON_SPACINGY
: 0) - 2;
2194 else if(lStyle
& TCS_VERTICAL
)
2196 clientRect
.right
= clientRect
.left
+ infoPtr
->tabHeight
+
2197 lastRow
* (infoPtr
->tabHeight
- 2) -
2198 ((lStyle
& TCS_BUTTONS
) ? lastRow
* BUTTON_SPACINGY
: 0) + 1;
2203 clientRect
.bottom
= clientRect
.top
+ infoPtr
->tabHeight
+
2204 lastRow
* (infoPtr
->tabHeight
- 2) +
2205 ((lStyle
& TCS_BUTTONS
) ? lastRow
* BUTTON_SPACINGY
: 0) + 1;
2208 InvalidateRect(hwnd
, &clientRect
, TRUE
);
2212 TAB_Paint (HWND hwnd
, WPARAM wParam
)
2217 hdc
= wParam
== 0 ? BeginPaint (hwnd
, &ps
) : (HDC
)wParam
;
2218 TAB_Refresh (hwnd
, hdc
);
2221 EndPaint (hwnd
, &ps
);
2227 TAB_InsertItemA (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2229 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2234 GetClientRect (hwnd
, &rect
);
2235 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd
,
2236 rect
.top
, rect
.left
, rect
.bottom
, rect
.right
);
2238 pti
= (TCITEMA
*)lParam
;
2239 iItem
= (INT
)wParam
;
2241 if (iItem
< 0) return -1;
2242 if (iItem
> infoPtr
->uNumItem
)
2243 iItem
= infoPtr
->uNumItem
;
2245 if (infoPtr
->uNumItem
== 0) {
2246 infoPtr
->items
= COMCTL32_Alloc (sizeof (TAB_ITEM
));
2247 infoPtr
->uNumItem
++;
2248 infoPtr
->iSelected
= 0;
2251 TAB_ITEM
*oldItems
= infoPtr
->items
;
2253 infoPtr
->uNumItem
++;
2254 infoPtr
->items
= COMCTL32_Alloc (sizeof (TAB_ITEM
) * infoPtr
->uNumItem
);
2256 /* pre insert copy */
2258 memcpy (&infoPtr
->items
[0], &oldItems
[0],
2259 iItem
* sizeof(TAB_ITEM
));
2262 /* post insert copy */
2263 if (iItem
< infoPtr
->uNumItem
- 1) {
2264 memcpy (&infoPtr
->items
[iItem
+1], &oldItems
[iItem
],
2265 (infoPtr
->uNumItem
- iItem
- 1) * sizeof(TAB_ITEM
));
2269 if (iItem
<= infoPtr
->iSelected
)
2270 infoPtr
->iSelected
++;
2272 COMCTL32_Free (oldItems
);
2275 infoPtr
->items
[iItem
].mask
= pti
->mask
;
2276 if (pti
->mask
& TCIF_TEXT
)
2277 Str_SetPtrAtoW (&infoPtr
->items
[iItem
].pszText
, pti
->pszText
);
2279 if (pti
->mask
& TCIF_IMAGE
)
2280 infoPtr
->items
[iItem
].iImage
= pti
->iImage
;
2282 if (pti
->mask
& TCIF_PARAM
)
2283 infoPtr
->items
[iItem
].lParam
= pti
->lParam
;
2285 TAB_SetItemBounds(hwnd
);
2286 TAB_InvalidateTabArea(hwnd
, infoPtr
);
2288 TRACE("[%04x]: added item %d %s\n",
2289 hwnd
, iItem
, debugstr_w(infoPtr
->items
[iItem
].pszText
));
2296 TAB_InsertItemW (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2298 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2303 GetClientRect (hwnd
, &rect
);
2304 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd
,
2305 rect
.top
, rect
.left
, rect
.bottom
, rect
.right
);
2307 pti
= (TCITEMW
*)lParam
;
2308 iItem
= (INT
)wParam
;
2310 if (iItem
< 0) return -1;
2311 if (iItem
> infoPtr
->uNumItem
)
2312 iItem
= infoPtr
->uNumItem
;
2314 if (infoPtr
->uNumItem
== 0) {
2315 infoPtr
->items
= COMCTL32_Alloc (sizeof (TAB_ITEM
));
2316 infoPtr
->uNumItem
++;
2317 infoPtr
->iSelected
= 0;
2320 TAB_ITEM
*oldItems
= infoPtr
->items
;
2322 infoPtr
->uNumItem
++;
2323 infoPtr
->items
= COMCTL32_Alloc (sizeof (TAB_ITEM
) * infoPtr
->uNumItem
);
2325 /* pre insert copy */
2327 memcpy (&infoPtr
->items
[0], &oldItems
[0],
2328 iItem
* sizeof(TAB_ITEM
));
2331 /* post insert copy */
2332 if (iItem
< infoPtr
->uNumItem
- 1) {
2333 memcpy (&infoPtr
->items
[iItem
+1], &oldItems
[iItem
],
2334 (infoPtr
->uNumItem
- iItem
- 1) * sizeof(TAB_ITEM
));
2338 if (iItem
<= infoPtr
->iSelected
)
2339 infoPtr
->iSelected
++;
2341 COMCTL32_Free (oldItems
);
2344 infoPtr
->items
[iItem
].mask
= pti
->mask
;
2345 if (pti
->mask
& TCIF_TEXT
)
2346 Str_SetPtrW (&infoPtr
->items
[iItem
].pszText
, pti
->pszText
);
2348 if (pti
->mask
& TCIF_IMAGE
)
2349 infoPtr
->items
[iItem
].iImage
= pti
->iImage
;
2351 if (pti
->mask
& TCIF_PARAM
)
2352 infoPtr
->items
[iItem
].lParam
= pti
->lParam
;
2354 TAB_SetItemBounds(hwnd
);
2355 TAB_InvalidateTabArea(hwnd
, infoPtr
);
2357 TRACE("[%04x]: added item %d %s\n",
2358 hwnd
, iItem
, debugstr_w(infoPtr
->items
[iItem
].pszText
));
2365 TAB_SetItemSize (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2367 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2368 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
2371 if ((lStyle
& TCS_FIXEDWIDTH
) || (lStyle
& TCS_OWNERDRAWFIXED
))
2373 lResult
= MAKELONG(infoPtr
->tabWidth
, infoPtr
->tabHeight
);
2374 infoPtr
->tabWidth
= (INT
)LOWORD(lParam
);
2375 infoPtr
->tabHeight
= (INT
)HIWORD(lParam
);
2377 infoPtr
->fSizeSet
= TRUE
;
2383 TAB_SetItemA (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2385 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2390 iItem
= (INT
)wParam
;
2391 tabItem
= (LPTCITEMA
)lParam
;
2393 TRACE("%d %p\n", iItem
, tabItem
);
2394 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
)) return FALSE
;
2396 wineItem
= &infoPtr
->items
[iItem
];
2398 if (tabItem
->mask
& TCIF_IMAGE
)
2399 wineItem
->iImage
= tabItem
->iImage
;
2401 if (tabItem
->mask
& TCIF_PARAM
)
2402 wineItem
->lParam
= tabItem
->lParam
;
2404 if (tabItem
->mask
& TCIF_RTLREADING
)
2405 FIXME("TCIF_RTLREADING\n");
2407 if (tabItem
->mask
& TCIF_STATE
)
2408 wineItem
->dwState
= tabItem
->dwState
;
2410 if (tabItem
->mask
& TCIF_TEXT
)
2411 Str_SetPtrAtoW(&wineItem
->pszText
, tabItem
->pszText
);
2413 /* Update and repaint tabs */
2414 TAB_SetItemBounds(hwnd
);
2415 TAB_InvalidateTabArea(hwnd
,infoPtr
);
2422 TAB_SetItemW (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2424 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2429 iItem
= (INT
)wParam
;
2430 tabItem
= (LPTCITEMW
)lParam
;
2432 TRACE("%d %p\n", iItem
, tabItem
);
2433 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
)) return FALSE
;
2435 wineItem
= &infoPtr
->items
[iItem
];
2437 if (tabItem
->mask
& TCIF_IMAGE
)
2438 wineItem
->iImage
= tabItem
->iImage
;
2440 if (tabItem
->mask
& TCIF_PARAM
)
2441 wineItem
->lParam
= tabItem
->lParam
;
2443 if (tabItem
->mask
& TCIF_RTLREADING
)
2444 FIXME("TCIF_RTLREADING\n");
2446 if (tabItem
->mask
& TCIF_STATE
)
2447 wineItem
->dwState
= tabItem
->dwState
;
2449 if (tabItem
->mask
& TCIF_TEXT
)
2450 Str_SetPtrW(&wineItem
->pszText
, tabItem
->pszText
);
2452 /* Update and repaint tabs */
2453 TAB_SetItemBounds(hwnd
);
2454 TAB_InvalidateTabArea(hwnd
,infoPtr
);
2461 TAB_GetItemCount (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2463 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2465 return infoPtr
->uNumItem
;
2470 TAB_GetItemA (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2472 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2477 iItem
= (INT
)wParam
;
2478 tabItem
= (LPTCITEMA
)lParam
;
2480 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
))
2483 wineItem
= &infoPtr
->items
[iItem
];
2485 if (tabItem
->mask
& TCIF_IMAGE
)
2486 tabItem
->iImage
= wineItem
->iImage
;
2488 if (tabItem
->mask
& TCIF_PARAM
)
2489 tabItem
->lParam
= wineItem
->lParam
;
2491 if (tabItem
->mask
& TCIF_RTLREADING
)
2492 FIXME("TCIF_RTLREADING\n");
2494 if (tabItem
->mask
& TCIF_STATE
)
2495 tabItem
->dwState
= wineItem
->dwState
;
2497 if (tabItem
->mask
& TCIF_TEXT
)
2498 Str_GetPtrWtoA (wineItem
->pszText
, tabItem
->pszText
, tabItem
->cchTextMax
);
2505 TAB_GetItemW (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2507 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2512 iItem
= (INT
)wParam
;
2513 tabItem
= (LPTCITEMW
)lParam
;
2515 if ((iItem
<0) || (iItem
>=infoPtr
->uNumItem
))
2518 wineItem
=& infoPtr
->items
[iItem
];
2520 if (tabItem
->mask
& TCIF_IMAGE
)
2521 tabItem
->iImage
= wineItem
->iImage
;
2523 if (tabItem
->mask
& TCIF_PARAM
)
2524 tabItem
->lParam
= wineItem
->lParam
;
2526 if (tabItem
->mask
& TCIF_RTLREADING
)
2527 FIXME("TCIF_RTLREADING\n");
2529 if (tabItem
->mask
& TCIF_STATE
)
2530 tabItem
->dwState
= wineItem
->dwState
;
2532 if (tabItem
->mask
& TCIF_TEXT
)
2533 Str_GetPtrW (wineItem
->pszText
, tabItem
->pszText
, tabItem
->cchTextMax
);
2540 TAB_DeleteItem (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2542 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2543 INT iItem
= (INT
) wParam
;
2544 BOOL bResult
= FALSE
;
2546 if ((iItem
>= 0) && (iItem
< infoPtr
->uNumItem
))
2548 TAB_ITEM
*oldItems
= infoPtr
->items
;
2550 infoPtr
->uNumItem
--;
2551 infoPtr
->items
= COMCTL32_Alloc(sizeof (TAB_ITEM
) * infoPtr
->uNumItem
);
2554 memcpy(&infoPtr
->items
[0], &oldItems
[0], iItem
* sizeof(TAB_ITEM
));
2556 if (iItem
< infoPtr
->uNumItem
)
2557 memcpy(&infoPtr
->items
[iItem
], &oldItems
[iItem
+ 1],
2558 (infoPtr
->uNumItem
- iItem
) * sizeof(TAB_ITEM
));
2560 COMCTL32_Free(oldItems
);
2562 /* Readjust the selected index */
2563 if ((iItem
== infoPtr
->iSelected
) && (iItem
> 0))
2564 infoPtr
->iSelected
--;
2566 if (iItem
< infoPtr
->iSelected
)
2567 infoPtr
->iSelected
--;
2569 if (infoPtr
->uNumItem
== 0)
2570 infoPtr
->iSelected
= -1;
2572 /* Reposition and repaint tabs */
2573 TAB_SetItemBounds(hwnd
);
2574 TAB_InvalidateTabArea(hwnd
,infoPtr
);
2583 TAB_DeleteAllItems (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2585 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2587 COMCTL32_Free (infoPtr
->items
);
2588 infoPtr
->uNumItem
= 0;
2589 infoPtr
->iSelected
= -1;
2590 if (infoPtr
->iHotTracked
>= 0)
2591 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
2592 infoPtr
->iHotTracked
= -1;
2594 TAB_SetItemBounds(hwnd
);
2595 TAB_InvalidateTabArea(hwnd
,infoPtr
);
2601 TAB_GetFont (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2603 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2606 return (LRESULT
)infoPtr
->hFont
;
2610 TAB_SetFont (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2613 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2615 TRACE("%x %lx\n",wParam
, lParam
);
2617 infoPtr
->hFont
= (HFONT
)wParam
;
2619 TAB_SetItemBounds(hwnd
);
2621 TAB_InvalidateTabArea(hwnd
, infoPtr
);
2628 TAB_GetImageList (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2630 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2633 return (LRESULT
)infoPtr
->himl
;
2637 TAB_SetImageList (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2639 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2640 HIMAGELIST himlPrev
;
2643 himlPrev
= infoPtr
->himl
;
2644 infoPtr
->himl
= (HIMAGELIST
)lParam
;
2645 return (LRESULT
)himlPrev
;
2649 TAB_GetUnicodeFormat (HWND hwnd
)
2651 TAB_INFO
*infoPtr
= TAB_GetInfoPtr (hwnd
);
2652 return infoPtr
->bUnicode
;
2656 TAB_SetUnicodeFormat (HWND hwnd
, WPARAM wParam
)
2658 TAB_INFO
*infoPtr
= TAB_GetInfoPtr (hwnd
);
2659 BOOL bTemp
= infoPtr
->bUnicode
;
2661 infoPtr
->bUnicode
= (BOOL
)wParam
;
2667 TAB_Size (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2670 /* I'm not really sure what the following code was meant to do.
2671 This is what it is doing:
2672 When WM_SIZE is sent with SIZE_RESTORED, the control
2673 gets positioned in the top left corner.
2677 UINT uPosFlags,cx,cy;
2681 parent = GetParent (hwnd);
2682 GetClientRect(parent, &parent_rect);
2685 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2686 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2688 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2689 cx, cy, uPosFlags | SWP_NOZORDER);
2691 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2694 /* Recompute the size/position of the tabs. */
2695 TAB_SetItemBounds (hwnd
);
2697 /* Force a repaint of the control. */
2698 InvalidateRect(hwnd
, NULL
, TRUE
);
2705 TAB_Create (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2708 TEXTMETRICA fontMetrics
;
2713 infoPtr
= (TAB_INFO
*)COMCTL32_Alloc (sizeof(TAB_INFO
));
2715 SetWindowLongA(hwnd
, 0, (DWORD
)infoPtr
);
2717 infoPtr
->uNumItem
= 0;
2718 infoPtr
->uNumRows
= 0;
2721 infoPtr
->hcurArrow
= LoadCursorA (0, IDC_ARROWA
);
2722 infoPtr
->iSelected
= -1;
2723 infoPtr
->iHotTracked
= -1;
2724 infoPtr
->uFocus
= -1;
2725 infoPtr
->hwndToolTip
= 0;
2726 infoPtr
->DoRedraw
= TRUE
;
2727 infoPtr
->needsScrolling
= FALSE
;
2728 infoPtr
->hwndUpDown
= 0;
2729 infoPtr
->leftmostVisible
= 0;
2730 infoPtr
->fSizeSet
= FALSE
;
2731 infoPtr
->bUnicode
= IsWindowUnicode (hwnd
);
2733 TRACE("Created tab control, hwnd [%04x]\n", hwnd
);
2735 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2736 if you don't specify it in CreateWindow. This is necessary in
2737 order for paint to work correctly. This follows windows behaviour. */
2738 dwStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
2739 SetWindowLongA(hwnd
, GWL_STYLE
, dwStyle
|WS_CLIPSIBLINGS
);
2741 if (dwStyle
& TCS_TOOLTIPS
) {
2742 /* Create tooltip control */
2743 infoPtr
->hwndToolTip
=
2744 CreateWindowExA (0, TOOLTIPS_CLASSA
, NULL
, 0,
2745 CW_USEDEFAULT
, CW_USEDEFAULT
,
2746 CW_USEDEFAULT
, CW_USEDEFAULT
,
2749 /* Send NM_TOOLTIPSCREATED notification */
2750 if (infoPtr
->hwndToolTip
) {
2751 NMTOOLTIPSCREATED nmttc
;
2753 nmttc
.hdr
.hwndFrom
= hwnd
;
2754 nmttc
.hdr
.idFrom
= GetWindowLongA(hwnd
, GWL_ID
);
2755 nmttc
.hdr
.code
= NM_TOOLTIPSCREATED
;
2756 nmttc
.hwndToolTips
= infoPtr
->hwndToolTip
;
2758 SendMessageA (GetParent (hwnd
), WM_NOTIFY
,
2759 (WPARAM
)GetWindowLongA(hwnd
, GWL_ID
), (LPARAM
)&nmttc
);
2764 * We need to get text information so we need a DC and we need to select
2768 hOldFont
= SelectObject (hdc
, GetStockObject (SYSTEM_FONT
));
2770 /* Use the system font to determine the initial height of a tab. */
2771 GetTextMetricsA(hdc
, &fontMetrics
);
2774 * Make sure there is enough space for the letters + growing the
2775 * selected item + extra space for the selected item.
2777 infoPtr
->tabHeight
= fontMetrics
.tmHeight
+ 2 * VERTICAL_ITEM_PADDING
+
2778 SELECTED_TAB_OFFSET
;
2780 /* Initialize the width of a tab. */
2781 infoPtr
->tabWidth
= DEFAULT_TAB_WIDTH
;
2783 SelectObject (hdc
, hOldFont
);
2784 ReleaseDC(hwnd
, hdc
);
2790 TAB_Destroy (HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2792 TAB_INFO
*infoPtr
= TAB_GetInfoPtr(hwnd
);
2798 if (infoPtr
->items
) {
2799 for (iItem
= 0; iItem
< infoPtr
->uNumItem
; iItem
++) {
2800 if (infoPtr
->items
[iItem
].pszText
)
2801 COMCTL32_Free (infoPtr
->items
[iItem
].pszText
);
2803 COMCTL32_Free (infoPtr
->items
);
2806 if (infoPtr
->hwndToolTip
)
2807 DestroyWindow (infoPtr
->hwndToolTip
);
2809 if (infoPtr
->hwndUpDown
)
2810 DestroyWindow(infoPtr
->hwndUpDown
);
2812 if (infoPtr
->iHotTracked
>= 0)
2813 KillTimer(hwnd
, TAB_HOTTRACK_TIMER
);
2815 COMCTL32_Free (infoPtr
);
2816 SetWindowLongA(hwnd
, 0, 0);
2820 static LRESULT WINAPI
2821 TAB_WindowProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
2824 TRACE("hwnd=%x msg=%x wParam=%x lParam=%lx\n", hwnd
, uMsg
, wParam
, lParam
);
2825 if (!TAB_GetInfoPtr(hwnd
) && (uMsg
!= WM_CREATE
))
2826 return DefWindowProcA (hwnd
, uMsg
, wParam
, lParam
);
2830 case TCM_GETIMAGELIST
:
2831 return TAB_GetImageList (hwnd
, wParam
, lParam
);
2833 case TCM_SETIMAGELIST
:
2834 return TAB_SetImageList (hwnd
, wParam
, lParam
);
2836 case TCM_GETITEMCOUNT
:
2837 return TAB_GetItemCount (hwnd
, wParam
, lParam
);
2840 return TAB_GetItemA (hwnd
, wParam
, lParam
);
2843 return TAB_GetItemW (hwnd
, wParam
, lParam
);
2846 return TAB_SetItemA (hwnd
, wParam
, lParam
);
2849 return TAB_SetItemW (hwnd
, wParam
, lParam
);
2851 case TCM_DELETEITEM
:
2852 return TAB_DeleteItem (hwnd
, wParam
, lParam
);
2854 case TCM_DELETEALLITEMS
:
2855 return TAB_DeleteAllItems (hwnd
, wParam
, lParam
);
2857 case TCM_GETITEMRECT
:
2858 return TAB_GetItemRect (hwnd
, wParam
, lParam
);
2861 return TAB_GetCurSel (hwnd
);
2864 return TAB_HitTest (hwnd
, wParam
, lParam
);
2867 return TAB_SetCurSel (hwnd
, wParam
);
2869 case TCM_INSERTITEMA
:
2870 return TAB_InsertItemA (hwnd
, wParam
, lParam
);
2872 case TCM_INSERTITEMW
:
2873 return TAB_InsertItemW (hwnd
, wParam
, lParam
);
2875 case TCM_SETITEMEXTRA
:
2876 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
2879 case TCM_ADJUSTRECT
:
2880 return TAB_AdjustRect (hwnd
, (BOOL
)wParam
, (LPRECT
)lParam
);
2882 case TCM_SETITEMSIZE
:
2883 return TAB_SetItemSize (hwnd
, wParam
, lParam
);
2885 case TCM_REMOVEIMAGE
:
2886 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
2889 case TCM_SETPADDING
:
2890 FIXME("Unimplemented msg TCM_SETPADDING\n");
2893 case TCM_GETROWCOUNT
:
2894 return TAB_GetRowCount(hwnd
);
2896 case TCM_GETUNICODEFORMAT
:
2897 return TAB_GetUnicodeFormat (hwnd
);
2899 case TCM_SETUNICODEFORMAT
:
2900 return TAB_SetUnicodeFormat (hwnd
, wParam
);
2902 case TCM_HIGHLIGHTITEM
:
2903 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
2906 case TCM_GETTOOLTIPS
:
2907 return TAB_GetToolTips (hwnd
, wParam
, lParam
);
2909 case TCM_SETTOOLTIPS
:
2910 return TAB_SetToolTips (hwnd
, wParam
, lParam
);
2912 case TCM_GETCURFOCUS
:
2913 return TAB_GetCurFocus (hwnd
);
2915 case TCM_SETCURFOCUS
:
2916 return TAB_SetCurFocus (hwnd
, wParam
);
2918 case TCM_SETMINTABWIDTH
:
2919 FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
2922 case TCM_DESELECTALL
:
2923 FIXME("Unimplemented msg TCM_DESELECTALL\n");
2926 case TCM_GETEXTENDEDSTYLE
:
2927 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
2930 case TCM_SETEXTENDEDSTYLE
:
2931 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
2935 return TAB_GetFont (hwnd
, wParam
, lParam
);
2938 return TAB_SetFont (hwnd
, wParam
, lParam
);
2941 return TAB_Create (hwnd
, wParam
, lParam
);
2944 return TAB_Destroy (hwnd
, wParam
, lParam
);
2947 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2949 case WM_LBUTTONDOWN
:
2950 return TAB_LButtonDown (hwnd
, wParam
, lParam
);
2953 return TAB_LButtonUp (hwnd
, wParam
, lParam
);
2956 return SendMessageA(GetParent(hwnd
), WM_NOTIFY
, wParam
, lParam
);
2958 case WM_RBUTTONDOWN
:
2959 return TAB_RButtonDown (hwnd
, wParam
, lParam
);
2962 return TAB_MouseMove (hwnd
, wParam
, lParam
);
2965 return TAB_EraseBackground (hwnd
, (HDC
)wParam
);
2968 return TAB_Paint (hwnd
, wParam
);
2971 return TAB_Size (hwnd
, wParam
, lParam
);
2974 return TAB_SetRedraw (hwnd
, wParam
);
2977 return TAB_OnHScroll(hwnd
, (int)LOWORD(wParam
), (int)HIWORD(wParam
), (HWND
)lParam
);
2979 case WM_STYLECHANGED
:
2980 TAB_SetItemBounds (hwnd
);
2981 InvalidateRect(hwnd
, NULL
, TRUE
);
2986 return TAB_FocusChanging(hwnd
, uMsg
, wParam
, lParam
);
2989 return TAB_KeyUp(hwnd
, wParam
);
2991 return TAB_NCHitTest(hwnd
, lParam
);
2994 if (uMsg
>= WM_USER
)
2995 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
2996 uMsg
, wParam
, lParam
);
2997 return DefWindowProcA(hwnd
, uMsg
, wParam
, lParam
);
3009 ZeroMemory (&wndClass
, sizeof(WNDCLASSA
));
3010 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
;
3011 wndClass
.lpfnWndProc
= (WNDPROC
)TAB_WindowProc
;
3012 wndClass
.cbClsExtra
= 0;
3013 wndClass
.cbWndExtra
= sizeof(TAB_INFO
*);
3014 wndClass
.hCursor
= LoadCursorA (0, IDC_ARROWA
);
3015 wndClass
.hbrBackground
= (HBRUSH
)NULL
;
3016 wndClass
.lpszClassName
= WC_TABCONTROLA
;
3018 RegisterClassA (&wndClass
);
3023 TAB_Unregister (void)
3025 UnregisterClassA (WC_TABCONTROLA
, (HINSTANCE
)NULL
);