Made HEAP_strdup* functions inline (temporary).
[wine/multimedia.git] / dlls / comctl32 / tab.c
blob66a41429a5c56099272ac80f8421c6b22a55b8ed
1 /*
2 * Tab control
4 * Copyright 1998 Anders Carlsson
5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Francis Beaudet
8 * TODO:
9 * Image list support
10 * Unicode support (under construction)
12 * FIXME:
13 * UpDown control not displayed until after a tab is clicked on
16 #include <string.h>
18 #include "winbase.h"
19 #include "commctrl.h"
20 #include "comctl32.h"
21 #include "debugtools.h"
22 #include <math.h>
24 DEFAULT_DEBUG_CHANNEL(tab);
26 typedef struct
28 UINT mask;
29 DWORD dwState;
30 LPWSTR pszText;
31 INT iImage;
32 LPARAM lParam;
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 */
39 } TAB_ITEM;
41 typedef struct
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 */
63 } TAB_INFO;
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 /******************************************************************************
90 * Prototypes
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);
98 static BOOL
99 TAB_SendSimpleNotify (HWND hwnd, UINT code)
101 NMHDR nmhdr;
103 nmhdr.hwndFrom = hwnd;
104 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
105 nmhdr.code = code;
107 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
108 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
111 static VOID
112 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
113 WPARAM wParam, LPARAM lParam)
115 MSG msg;
117 msg.hwnd = hwndMsg;
118 msg.message = uMsg;
119 msg.wParam = wParam;
120 msg.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);
128 static LRESULT
129 TAB_GetCurSel (HWND hwnd)
131 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
133 return infoPtr->iSelected;
136 static LRESULT
137 TAB_GetCurFocus (HWND hwnd)
139 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
141 return infoPtr->uFocus;
144 static LRESULT
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;
153 static LRESULT
154 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
156 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
157 INT iItem = (INT)wParam;
158 INT prevItem;
160 prevItem = -1;
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);
167 return prevItem;
170 static LRESULT
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");
180 } else {
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);
192 return 0;
195 static LRESULT
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;
202 return 0;
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(
215 HWND hwnd,
216 TAB_INFO* infoPtr,
217 INT itemIndex,
218 RECT* itemRect,
219 RECT* selectedRect)
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)) )
228 return FALSE;
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 -
251 infoPtr->tabHeight -
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 +
272 infoPtr->tabHeight +
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)
287 OffsetRect(itemRect,
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.
295 OffsetRect(itemRect,
297 -SELECTED_TAB_OFFSET);
299 } else
301 OffsetRect(itemRect,
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.
309 OffsetRect(itemRect,
310 SELECTED_TAB_OFFSET,
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);
322 else
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;
341 else
343 selectedRect->top -= SELECTED_TAB_OFFSET;
344 selectedRect->bottom += 1;
348 return TRUE;
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 /******************************************************************************
358 * TAB_KeyUp
360 * This method is called to handle keyboard input
362 static LRESULT TAB_KeyUp(
363 HWND hwnd,
364 WPARAM keyCode)
366 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
367 int newItem = -1;
369 switch (keyCode)
371 case VK_LEFT:
372 newItem = infoPtr->uFocus - 1;
373 break;
374 case VK_RIGHT:
375 newItem = infoPtr->uFocus + 1;
376 break;
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);
397 return 0;
400 /******************************************************************************
401 * TAB_FocusChanging
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(
407 HWND hwnd,
408 UINT uMsg,
409 WPARAM wParam,
410 LPARAM lParam)
412 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
413 RECT selectedRect;
414 BOOL isVisible;
417 * Get the rectangle for the item.
419 isVisible = TAB_InternalGetItemRect(hwnd,
420 infoPtr,
421 infoPtr->uFocus,
422 NULL,
423 &selectedRect);
426 * If the rectangle is not completely invisible, invalidate that
427 * portion of the window.
429 if (isVisible)
431 InvalidateRect(hwnd, &selectedRect, TRUE);
435 * Don't otherwise disturb normal behavior.
437 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
440 static HWND TAB_InternalHitTest (
441 HWND hwnd,
442 TAB_INFO* infoPtr,
443 POINT pt,
444 UINT* flags)
447 RECT rect;
448 int iCount;
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;
457 return iCount;
461 *flags = TCHT_NOWHERE;
462 return -1;
465 static LRESULT
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 /******************************************************************************
475 * TAB_NCHitTest
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
480 * are dead.
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 ?
486 static LRESULT
487 TAB_NCHitTest (HWND hwnd, LPARAM lParam)
489 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
490 POINT pt;
491 UINT dummyflag;
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;
499 else
500 return HTCLIENT;
503 static LRESULT
504 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
506 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
507 POINT pt;
508 INT newItem, dummy;
510 if (infoPtr->hwndToolTip)
511 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
512 WM_LBUTTONDOWN, wParam, lParam);
514 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
515 SetFocus (hwnd);
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);
542 return 0;
545 static LRESULT
546 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
548 TAB_SendSimpleNotify(hwnd, NM_CLICK);
550 return 0;
553 static LRESULT
554 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
556 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
557 return 0;
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.
568 static void
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.
589 static VOID CALLBACK
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)
602 POINT pt;
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 /******************************************************************************
626 * TAB_RecalcHotTrack
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
639 * itself.
641 static void
642 TAB_RecalcHotTrack
644 HWND hwnd,
645 const LPARAM* pos,
646 int* out_redrawLeave,
647 int* out_redrawEnter
650 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
652 int item = -1;
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)
662 POINT pt;
663 UINT flags;
665 if (pos == NULL)
667 GetCursorPos(&pt);
668 ScreenToClient(hwnd, &pt);
670 else
672 pt.x = LOWORD(*pos);
673 pt.y = HIWORD(*pos);
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;
687 if (item < 0)
689 /* Kill timer which forces recheck of mouse pos */
690 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
693 else
695 /* Start timer so we recheck mouse pos */
696 UINT timerID = SetTimer
698 hwnd,
699 TAB_HOTTRACK_TIMER,
700 TAB_HOTTRACK_TIMER_INTERVAL,
701 TAB_HotTrackTimerProc
704 if (timerID == 0)
705 return; /* Hot tracking not available */
708 infoPtr->iHotTracked = item;
710 if (item >= 0)
712 /* Mark new hot-tracked to be redrawn to look highlighted */
713 if (out_redrawEnter != NULL)
714 *out_redrawEnter = item;
719 /******************************************************************************
720 * TAB_MouseMove
722 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
724 static LRESULT
725 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
727 int redrawLeave;
728 int redrawEnter;
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
737 ** status. */
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);
745 return 0;
748 /******************************************************************************
749 * TAB_AdjustRect
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(
755 HWND hwnd,
756 WPARAM fLarger,
757 LPRECT prc)
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;
769 else
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;
791 else
792 prc->left += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
795 else {
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;
801 else
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;
821 else
822 prc->top += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
826 return 0;
829 /******************************************************************************
830 * TAB_OnHScroll
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(
836 HWND hwnd,
837 int nScrollCode,
838 int nPos,
839 HWND hwndScroll)
841 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
843 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
845 if(nPos < infoPtr->leftmostVisible)
846 infoPtr->leftmostVisible--;
847 else
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));
856 return 0;
859 /******************************************************************************
860 * TAB_SetupScroling
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(
866 HWND hwnd,
867 TAB_INFO* infoPtr,
868 const RECT* clientRect)
870 INT maxRange = 0;
871 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
873 if (infoPtr->needsScrolling)
875 RECT controlPos;
876 INT vsize, tabwidth;
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);
891 else
893 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
894 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
897 else
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);
907 else
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,
926 hwnd,
927 (HMENU)NULL,
928 (HINSTANCE)NULL,
929 NULL);
931 else
933 SetWindowPos(infoPtr->hwndUpDown,
934 (HWND)NULL,
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)
954 break;
957 if(maxRange == infoPtr->uNumItem)
958 maxRange--;
961 else
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 /******************************************************************************
972 * TAB_SetItemBounds
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;
986 INT curItem;
987 INT curItemLeftPos;
988 INT curItemRowCount;
989 HFONT hFont, hOldFont;
990 HDC hdc;
991 RECT clientRect;
992 SIZE size;
993 INT iTemp;
994 RECT* rcItem;
995 INT iIndex;
998 * We need to get text information so we need a DC and we need to select
999 * a font.
1001 hdc = GetDC(hwnd);
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
1008 * of the control.
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 */
1022 curItemLeftPos = 0;
1023 curItemRowCount = 0;
1025 if (!(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet) )
1027 int item_height;
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 */
1034 if (infoPtr->himl)
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;
1040 else
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 +
1060 infoPtr->tabWidth +
1061 2 * HORIZONTAL_ITEM_PADDING;
1063 else
1065 int icon_width = 0;
1066 int num = 2;
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 */
1078 if (infoPtr->himl)
1080 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1081 num++;
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;
1105 curItemRowCount++;
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
1120 * of this one.
1122 if (lStyle & TCS_BUTTONS)
1124 curItemLeftPos = infoPtr->items[curItem].rect.right + 1;
1125 if (lStyle & TCS_FLATBUTTONS)
1126 curItemLeftPos += FLAT_BTN_SPACINGX;
1128 else
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) >
1138 clientRect.right);
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;
1154 INT iRow,iItm;
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;
1168 iItm++,iCount++)
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))
1175 iRow++;
1176 curItemLeftPos = 0;
1177 iCount = 0;
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;
1197 else
1198 curItemLeftPos = infoPtr->items[iItm].rect.right;
1202 * Justify the rows
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) ;
1215 iIndexEnd++)
1216 /* intentionaly blank */;
1219 * we need to justify these tabs so they fill the whole given
1220 * client area
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;
1231 if (iCount > 1)
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;
1237 iIndex++,iCount++)
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)
1259 RECT rcOriginal;
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);
1277 /* Cleanup */
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.
1288 static void
1289 TAB_DrawItemInterior
1291 HWND hwnd,
1292 HDC hdc,
1293 INT iItem,
1294 RECT* drawRect
1297 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1298 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1300 RECT localRect;
1302 HPEN htextPen = GetSysColorPen (COLOR_BTNTEXT);
1303 HPEN holdPen;
1304 INT oldBkMode;
1306 if (drawRect == NULL)
1308 BOOL isVisible;
1309 RECT itemRect;
1310 RECT selectedRect;
1313 * Get the rectangle for the item.
1315 isVisible = TAB_InternalGetItemRect(hwnd, infoPtr, iItem, &itemRect, &selectedRect);
1316 if (!isVisible)
1317 return;
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)
1335 drawRect->right--;
1336 drawRect->bottom--;
1339 else
1341 if (iItem == infoPtr->iSelected)
1342 *drawRect = selectedRect;
1343 else
1344 *drawRect = itemRect;
1345 drawRect->right--;
1346 drawRect->bottom--;
1351 * Text pen
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);
1363 else
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))
1372 DRAWITEMSTRUCT dis;
1373 UINT id;
1376 * get the control id
1378 id = GetWindowLongA( hwnd, GWL_ID );
1381 * put together the DRAWITEMSTRUCT
1383 dis.CtlType = ODT_TAB;
1384 dis.CtlID = id;
1385 dis.itemID = iItem;
1386 dis.itemAction = ODA_DRAWENTIRE;
1387 if ( iItem == infoPtr->iSelected )
1388 dis.itemState = ODS_SELECTED;
1389 else
1390 dis.itemState = 0;
1391 dis.hwndItem = hwnd; /* */
1392 dis.hDC = hdc;
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 );
1401 else
1403 INT cx;
1404 INT cy;
1405 UINT uHorizAlign;
1406 RECT rcTemp;
1407 RECT rcImage;
1408 LOGFONTA logfont;
1409 HFONT hFont;
1410 HFONT hOldFont = 0; /* stop uninitialized warning */
1412 INT nEscapement = 0; /* stop uninitialized warning */
1413 INT nOrientation = 0; /* stop uninitialized warning */
1414 INT iPointSize;
1416 /* used to center the icon and text in the tab */
1417 RECT rcText;
1418 INT center_offset;
1420 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1421 rcImage = *drawRect;
1423 rcTemp = *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.
1438 * Draw the icon.
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;
1446 else
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;
1473 ImageList_Draw
1475 infoPtr->himl,
1476 infoPtr->items[iItem].iImage,
1477 hdc,
1478 rcImage.left,
1479 rcImage.top + 1,
1480 ILD_NORMAL
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;
1490 else
1491 drawRect->bottom-=center_offset;
1493 else
1495 center_offset = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1496 drawRect->left+=center_offset;
1500 /* Draw the text */
1501 if (lStyle & TCS_RIGHTJUSTIFY)
1502 uHorizAlign = DT_CENTER;
1503 else
1504 uHorizAlign = DT_LEFT;
1506 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1508 if(lStyle & TCS_BOTTOM)
1510 nEscapement = -900;
1511 nOrientation = -900;
1513 else
1515 nEscapement = 900;
1516 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)
1524 iPointSize = 9;
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);
1539 ExtTextOutW(hdc,
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 */
1557 * Cleanup
1559 SetBkMode(hdc, oldBkMode);
1560 SelectObject(hdc, holdPen);
1563 /******************************************************************************
1564 * TAB_DrawItem
1566 * This method is used to draw a single tab into the tab control.
1568 static void TAB_DrawItem(
1569 HWND hwnd,
1570 HDC hdc,
1571 INT iItem)
1573 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1574 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1575 RECT itemRect;
1576 RECT selectedRect;
1577 BOOL isVisible;
1578 RECT r;
1581 * Get the rectangle for the item.
1583 isVisible = TAB_InternalGetItemRect(hwnd,
1584 infoPtr,
1585 iItem,
1586 &itemRect,
1587 &selectedRect);
1589 if (isVisible)
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);
1596 HPEN holdPen;
1597 BOOL deleteBrush = TRUE;
1599 if (lStyle & TCS_BUTTONS)
1601 /* Get item rectangle */
1602 r = itemRect;
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;
1612 /* highlight */
1613 MoveToEx (hdc, x, r.bottom - 1, NULL);
1614 LineTo (hdc, x, r.top - 1);
1615 x--;
1617 /* shadow */
1618 SelectObject(hdc, hbPen);
1619 MoveToEx (hdc, x, r.bottom - 1, NULL);
1620 LineTo (hdc, x, r.top - 1);
1622 /* shade */
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);
1634 DeleteObject(hbr);
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);
1654 * Draw the tab now.
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.
1659 r.right--;
1660 r.bottom--;
1662 /* highlight */
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);
1668 /* shadow */
1669 SelectObject(hdc, hbPen);
1670 LineTo (hdc, r.left + 1, r.top + 1);
1671 LineTo (hdc, r.left + 1, r.bottom);
1673 /* shade */
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);
1679 else
1681 /* Erase the background */
1682 FillRect(hdc, &r, hbr);
1684 if (!(lStyle & TCS_FLATBUTTONS))
1686 /* highlight */
1687 MoveToEx (hdc, r.left, r.bottom, NULL);
1688 LineTo (hdc, r.left, r.top);
1689 LineTo (hdc, r.right, r.top);
1691 /* shadow */
1692 SelectObject(hdc, hbPen);
1693 LineTo (hdc, r.right, r.bottom);
1694 LineTo (hdc, r.left, r.bottom);
1696 /* shade */
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 */
1707 DeleteObject(hbr);
1708 hbr = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1710 /* We draw a rectangle of different sizes depending on the selection
1711 * state. */
1712 if (iItem == infoPtr->iSelected)
1713 r = selectedRect;
1714 else
1715 r = itemRect;
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
1721 * other tabs
1723 FillRect(hdc, &r, hbr);
1726 * Draw the tab now.
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.
1731 r.right--;
1732 r.bottom--;
1734 holdPen = SelectObject (hdc, hwPen);
1735 if(lStyle & TCS_VERTICAL)
1737 if (lStyle & TCS_BOTTOM)
1739 /* highlight */
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);
1744 /* shadow */
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);
1750 /* shade */
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);
1756 else
1758 /* highlight */
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);
1764 /* shadow */
1765 SelectObject(hdc, hbPen);
1766 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom);
1767 LineTo (hdc, r.right + 1, r.bottom);
1769 /* shade */
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);
1775 else
1777 if (lStyle & TCS_BOTTOM)
1779 /* highlight */
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);
1784 /* shadow */
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);
1790 /* shade */
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);
1797 else
1799 /* highlight */
1800 if(infoPtr->items[iItem].rect.left == 0) /* if leftmost draw the line longer */
1801 MoveToEx (hdc, r.left, r.bottom, NULL);
1802 else
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);
1809 /* shadow */
1810 SelectObject(hdc, hbPen);
1811 LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE);
1812 LineTo (hdc, r.right, r.bottom + 1);
1815 /* shade */
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) )
1831 r = itemRect;
1832 InflateRect(&r, -1, -1);
1834 DrawFocusRect(hdc, &r);
1837 /* Cleanup */
1838 SelectObject(hdc, holdPen);
1839 if (deleteBrush) DeleteObject(hbr);
1843 /******************************************************************************
1844 * TAB_DrawBorder
1846 * This method is used to draw the raised border around the tab control
1847 * "content" area.
1849 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
1851 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1852 HPEN htmPen;
1853 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
1854 HPEN hbPen = GetSysColorPen (COLOR_3DDKSHADOW);
1855 HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW);
1856 RECT rect;
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
1883 * rect)
1885 rect.right--;
1886 rect.bottom--;
1888 /* highlight */
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);
1895 /* Dark Shadow */
1896 SelectObject (hdc, hbPen);
1897 LineTo (hdc, rect.right, rect.bottom );
1898 LineTo (hdc, rect.left, rect.bottom);
1900 /* shade */
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 /******************************************************************************
1910 * TAB_Refresh
1912 * This method repaints the tab control..
1914 static void TAB_Refresh (HWND hwnd, HDC hdc)
1916 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1917 HFONT hOldFont;
1918 INT i;
1920 if (!infoPtr->DoRedraw)
1921 return;
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);
1930 else
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);
1955 static DWORD
1956 TAB_GetRowCount (HWND hwnd )
1958 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1960 return infoPtr->uNumRows;
1963 static LRESULT
1964 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
1966 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1968 infoPtr->DoRedraw=(BOOL) wParam;
1969 return 0;
1972 static LRESULT TAB_EraseBackground(
1973 HWND hwnd,
1974 HDC givenDC)
1976 HDC hdc;
1977 RECT clientRect;
1979 HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1981 hdc = givenDC ? givenDC : GetDC(hwnd);
1983 GetClientRect(hwnd, &clientRect);
1985 FillRect(hdc, &clientRect, brush);
1987 if (givenDC==0)
1988 ReleaseDC(hwnd, hdc);
1990 DeleteObject(brush);
1992 return 0;
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(
2002 HWND hwnd,
2003 TAB_INFO* infoPtr)
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
2010 * style */
2011 if ((infoPtr->uNumRows > 0) && !(lStyle & TCS_BUTTONS))
2013 INT newselected;
2014 INT iTargetRow;
2016 if(lStyle & TCS_VERTICAL)
2017 newselected = infoPtr->items[iSelected].rect.left;
2018 else
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)
2026 INT i;
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;
2034 else
2036 if (infoPtr->items[i].rect.left > newselected)
2037 infoPtr->items[i].rect.left-=1;
2041 else
2043 for (i=0; i < infoPtr->uNumItem; i++)
2045 if (infoPtr->items[i].rect.top == newselected )
2046 infoPtr->items[i].rect.top = iTargetRow;
2047 else
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))
2063 return;
2065 if (infoPtr->leftmostVisible >= iSelected)
2067 infoPtr->leftmostVisible = iSelected;
2069 else
2071 RECT r;
2072 INT width, i;
2074 /* Calculate the part of the client area that is visible */
2075 GetClientRect(hwnd, &r);
2076 width = r.right;
2078 GetClientRect(infoPtr->hwndUpDown, &r);
2079 width -= r.right;
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
2085 * part of control.
2087 infoPtr->leftmostVisible = iSelected;
2089 else
2091 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2093 if ((infoPtr->items[iSelected].rect.right -
2094 infoPtr->items[i].rect.left) < width)
2095 break;
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
2113 * to be redisplayed
2115 static void TAB_InvalidateTabArea(
2116 HWND hwnd,
2117 TAB_INFO* infoPtr)
2119 RECT clientRect;
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;
2144 else
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);
2154 static LRESULT
2155 TAB_Paint (HWND hwnd, WPARAM wParam)
2157 HDC hdc;
2158 PAINTSTRUCT ps;
2160 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2161 TAB_Refresh (hwnd, hdc);
2163 if(!wParam)
2164 EndPaint (hwnd, &ps);
2166 return 0;
2169 static LRESULT
2170 TAB_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2172 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2173 TCITEMA *pti;
2174 INT iItem;
2175 RECT rect;
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;
2193 else {
2194 TAB_ITEM *oldItems = infoPtr->items;
2196 infoPtr->uNumItem++;
2197 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2199 /* pre insert copy */
2200 if (iItem > 0) {
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));
2234 return iItem;
2238 static LRESULT
2239 TAB_InsertItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2241 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2242 TCITEMW *pti;
2243 INT iItem;
2244 RECT rect;
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;
2262 else {
2263 TAB_ITEM *oldItems = infoPtr->items;
2265 infoPtr->uNumItem++;
2266 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2268 /* pre insert copy */
2269 if (iItem > 0) {
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));
2303 return iItem;
2307 static LRESULT
2308 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2310 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2311 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2312 LONG lResult = 0;
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;
2322 return lResult;
2325 static LRESULT
2326 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2328 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2329 TCITEMA *tabItem;
2330 TAB_ITEM *wineItem;
2331 INT iItem;
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);
2360 return TRUE;
2364 static LRESULT
2365 TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2367 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2368 TCITEMW *tabItem;
2369 TAB_ITEM *wineItem;
2370 INT iItem;
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);
2399 return TRUE;
2403 static LRESULT
2404 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2406 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2408 return infoPtr->uNumItem;
2412 static LRESULT
2413 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2415 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2416 TCITEMA *tabItem;
2417 TAB_ITEM *wineItem;
2418 INT iItem;
2420 iItem = (INT)wParam;
2421 tabItem = (LPTCITEMA)lParam;
2422 TRACE("\n");
2423 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2424 return FALSE;
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);
2443 return TRUE;
2447 static LRESULT
2448 TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2450 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2451 TCITEMW *tabItem;
2452 TAB_ITEM *wineItem;
2453 INT iItem;
2455 iItem = (INT)wParam;
2456 tabItem = (LPTCITEMW)lParam;
2457 TRACE("\n");
2458 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2459 return FALSE;
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);
2478 return TRUE;
2482 static LRESULT
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);
2496 if (iItem > 0)
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);
2519 bResult = TRUE;
2522 return bResult;
2525 static LRESULT
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);
2539 return TRUE;
2543 static LRESULT
2544 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2546 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2548 TRACE("\n");
2549 return (LRESULT)infoPtr->hFont;
2552 static LRESULT
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);
2566 return 0;
2570 static LRESULT
2571 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2573 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2575 TRACE("\n");
2576 return (LRESULT)infoPtr->himl;
2579 static LRESULT
2580 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2582 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2583 HIMAGELIST himlPrev;
2585 TRACE("\n");
2586 himlPrev = infoPtr->himl;
2587 infoPtr->himl= (HIMAGELIST)lParam;
2588 return (LRESULT)himlPrev;
2591 static LRESULT
2592 TAB_GetUnicodeFormat (HWND hwnd)
2594 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2595 return infoPtr->bUnicode;
2598 static LRESULT
2599 TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
2601 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2602 BOOL bTemp = infoPtr->bUnicode;
2604 infoPtr->bUnicode = (BOOL)wParam;
2606 return bTemp;
2609 static LRESULT
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.
2618 RECT parent_rect;
2619 HWND parent;
2620 UINT uPosFlags,cx,cy;
2622 uPosFlags=0;
2623 if (!wParam) {
2624 parent = GetParent (hwnd);
2625 GetClientRect(parent, &parent_rect);
2626 cx=LOWORD (lParam);
2627 cy=HIWORD (lParam);
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);
2633 } else {
2634 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2635 } */
2637 /* Recompute the size/position of the tabs. */
2638 TAB_SetItemBounds (hwnd);
2640 /* Force a repaint of the control. */
2641 InvalidateRect(hwnd, NULL, TRUE);
2643 return 0;
2647 static LRESULT
2648 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2650 TAB_INFO *infoPtr;
2651 TEXTMETRICA fontMetrics;
2652 HDC hdc;
2653 HFONT hOldFont;
2654 DWORD dwStyle;
2656 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
2658 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
2660 infoPtr->uNumItem = 0;
2661 infoPtr->uNumRows = 0;
2662 infoPtr->hFont = 0;
2663 infoPtr->items = 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,
2690 hwnd, 0, 0, 0);
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
2708 * a font.
2710 hdc = GetDC(hwnd);
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);
2729 return 0;
2732 static LRESULT
2733 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
2735 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2736 INT iItem;
2738 if (!infoPtr)
2739 return 0;
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);
2760 return 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);
2771 switch (uMsg)
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);
2782 case TCM_GETITEMA:
2783 return TAB_GetItemA (hwnd, wParam, lParam);
2785 case TCM_GETITEMW:
2786 return TAB_GetItemW (hwnd, wParam, lParam);
2788 case TCM_SETITEMA:
2789 return TAB_SetItemA (hwnd, wParam, lParam);
2791 case TCM_SETITEMW:
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);
2803 case TCM_GETCURSEL:
2804 return TAB_GetCurSel (hwnd);
2806 case TCM_HITTEST:
2807 return TAB_HitTest (hwnd, wParam, lParam);
2809 case TCM_SETCURSEL:
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");
2820 return 0;
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");
2830 return 0;
2832 case TCM_SETPADDING:
2833 FIXME("Unimplemented msg TCM_SETPADDING\n");
2834 return 0;
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");
2847 return 0;
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");
2863 return 0;
2865 case TCM_DESELECTALL:
2866 FIXME("Unimplemented msg TCM_DESELECTALL\n");
2867 return 0;
2869 case TCM_GETEXTENDEDSTYLE:
2870 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
2871 return 0;
2873 case TCM_SETEXTENDEDSTYLE:
2874 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
2875 return 0;
2877 case WM_GETFONT:
2878 return TAB_GetFont (hwnd, wParam, lParam);
2880 case WM_SETFONT:
2881 return TAB_SetFont (hwnd, wParam, lParam);
2883 case WM_CREATE:
2884 return TAB_Create (hwnd, wParam, lParam);
2886 case WM_NCDESTROY:
2887 return TAB_Destroy (hwnd, wParam, lParam);
2889 case WM_GETDLGCODE:
2890 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2892 case WM_LBUTTONDOWN:
2893 return TAB_LButtonDown (hwnd, wParam, lParam);
2895 case WM_LBUTTONUP:
2896 return TAB_LButtonUp (hwnd, wParam, lParam);
2898 case WM_RBUTTONDOWN:
2899 return TAB_RButtonDown (hwnd, wParam, lParam);
2901 case WM_MOUSEMOVE:
2902 return TAB_MouseMove (hwnd, wParam, lParam);
2904 case WM_ERASEBKGND:
2905 return TAB_EraseBackground (hwnd, (HDC)wParam);
2907 case WM_PAINT:
2908 return TAB_Paint (hwnd, wParam);
2910 case WM_SIZE:
2911 return TAB_Size (hwnd, wParam, lParam);
2913 case WM_SETREDRAW:
2914 return TAB_SetRedraw (hwnd, wParam);
2916 case WM_HSCROLL:
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);
2922 return 0;
2924 case WM_KILLFOCUS:
2925 case WM_SETFOCUS:
2926 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
2928 case WM_KEYUP:
2929 return TAB_KeyUp(hwnd, wParam);
2930 case WM_NCHITTEST:
2931 return TAB_NCHitTest(hwnd, lParam);
2933 default:
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);
2940 return 0;
2944 VOID
2945 TAB_Register (void)
2947 WNDCLASSA wndClass;
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);
2962 VOID
2963 TAB_Unregister (void)
2965 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);