Slightly improve keyboard tracking in combobox.
[wine/testsucceed.git] / dlls / comctl32 / tab.c
blob45c3a56d221275d7dd7eb4264b8904470696891f
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 int oldFocus = infoPtr->uFocus;
182 if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) {
183 infoPtr->uFocus = iItem;
184 if (oldFocus != -1) {
185 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
186 infoPtr->iSelected = iItem;
187 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
189 else
190 infoPtr->iSelected = iItem;
191 TAB_EnsureSelectionVisible(hwnd, infoPtr);
192 TAB_InvalidateTabArea(hwnd, infoPtr);
196 return 0;
199 static LRESULT
200 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
202 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
204 if (infoPtr == NULL) return 0;
205 infoPtr->hwndToolTip = (HWND)wParam;
206 return 0;
209 /******************************************************************************
210 * TAB_InternalGetItemRect
212 * This method will calculate the rectangle representing a given tab item in
213 * client coordinates. This method takes scrolling into account.
215 * This method returns TRUE if the item is visible in the window and FALSE
216 * if it is completely outside the client area.
218 static BOOL TAB_InternalGetItemRect(
219 HWND hwnd,
220 TAB_INFO* infoPtr,
221 INT itemIndex,
222 RECT* itemRect,
223 RECT* selectedRect)
225 RECT tmpItemRect,clientRect;
226 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
228 /* Perform a sanity check and a trivial visibility check. */
229 if ( (infoPtr->uNumItem <= 0) ||
230 (itemIndex >= infoPtr->uNumItem) ||
231 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) )
232 return FALSE;
235 * Avoid special cases in this procedure by assigning the "out"
236 * parameters if the caller didn't supply them
238 if (itemRect == NULL)
239 itemRect = &tmpItemRect;
241 /* Retrieve the unmodified item rect. */
242 *itemRect = infoPtr->items[itemIndex].rect;
244 /* calculate the times bottom and top based on the row */
245 GetClientRect(hwnd, &clientRect);
247 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
249 itemRect->bottom = clientRect.bottom -
250 SELECTED_TAB_OFFSET -
251 itemRect->top * (infoPtr->tabHeight - 2) -
252 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
254 itemRect->top = clientRect.bottom -
255 infoPtr->tabHeight -
256 itemRect->top * (infoPtr->tabHeight - 2) -
257 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
259 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
261 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * (infoPtr->tabHeight - 2) -
262 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
263 itemRect->left = clientRect.right - infoPtr->tabHeight - itemRect->left * (infoPtr->tabHeight - 2) -
264 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
266 else if((lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM))
268 itemRect->right = clientRect.left + infoPtr->tabHeight + itemRect->left * (infoPtr->tabHeight - 2) +
269 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
270 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * (infoPtr->tabHeight - 2) +
271 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0);
273 else if(!(lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM)) /* not TCS_BOTTOM and not TCS_VERTICAL */
275 itemRect->bottom = clientRect.top +
276 infoPtr->tabHeight +
277 itemRect->top * (infoPtr->tabHeight - 2) +
278 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
279 itemRect->top = clientRect.top +
280 SELECTED_TAB_OFFSET +
281 itemRect->top * (infoPtr->tabHeight - 2) +
282 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0);
286 * "scroll" it to make sure the item at the very left of the
287 * tab control is the leftmost visible tab.
289 if(lStyle & TCS_VERTICAL)
291 OffsetRect(itemRect,
293 -(clientRect.bottom - infoPtr->items[infoPtr->leftmostVisible].rect.bottom));
296 * Move the rectangle so the first item is slightly offset from
297 * the bottom of the tab control.
299 OffsetRect(itemRect,
301 -SELECTED_TAB_OFFSET);
303 } else
305 OffsetRect(itemRect,
306 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
310 * Move the rectangle so the first item is slightly offset from
311 * the left of the tab control.
313 OffsetRect(itemRect,
314 SELECTED_TAB_OFFSET,
318 /* Now, calculate the position of the item as if it were selected. */
319 if (selectedRect!=NULL)
321 CopyRect(selectedRect, itemRect);
323 /* The rectangle of a selected item is a bit wider. */
324 if(lStyle & TCS_VERTICAL)
325 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET);
326 else
327 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
329 /* If it also a bit higher. */
330 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
332 selectedRect->top -= 2; /* the border is thicker on the bottom */
333 selectedRect->bottom += SELECTED_TAB_OFFSET;
335 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
337 selectedRect->left -= 2; /* the border is thicker on the right */
338 selectedRect->right += SELECTED_TAB_OFFSET;
340 else if(lStyle & TCS_VERTICAL)
342 selectedRect->left -= SELECTED_TAB_OFFSET;
343 selectedRect->right += 1;
345 else
347 selectedRect->top -= SELECTED_TAB_OFFSET;
348 selectedRect->bottom += 1;
352 return TRUE;
355 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
357 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
358 (LPRECT)lParam, (LPRECT)NULL);
361 /******************************************************************************
362 * TAB_KeyUp
364 * This method is called to handle keyboard input
366 static LRESULT TAB_KeyUp(
367 HWND hwnd,
368 WPARAM keyCode)
370 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
371 int newItem = -1;
373 switch (keyCode)
375 case VK_LEFT:
376 newItem = infoPtr->uFocus - 1;
377 break;
378 case VK_RIGHT:
379 newItem = infoPtr->uFocus + 1;
380 break;
384 * If we changed to a valid item, change the selection
386 if ((newItem >= 0) &&
387 (newItem < infoPtr->uNumItem) &&
388 (infoPtr->uFocus != newItem))
390 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
392 infoPtr->iSelected = newItem;
393 infoPtr->uFocus = newItem;
394 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
396 TAB_EnsureSelectionVisible(hwnd, infoPtr);
397 TAB_InvalidateTabArea(hwnd, infoPtr);
401 return 0;
404 /******************************************************************************
405 * TAB_FocusChanging
407 * This method is called whenever the focus goes in or out of this control
408 * it is used to update the visual state of the control.
410 static LRESULT TAB_FocusChanging(
411 HWND hwnd,
412 UINT uMsg,
413 WPARAM wParam,
414 LPARAM lParam)
416 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
417 RECT selectedRect;
418 BOOL isVisible;
421 * Get the rectangle for the item.
423 isVisible = TAB_InternalGetItemRect(hwnd,
424 infoPtr,
425 infoPtr->uFocus,
426 NULL,
427 &selectedRect);
430 * If the rectangle is not completely invisible, invalidate that
431 * portion of the window.
433 if (isVisible)
435 InvalidateRect(hwnd, &selectedRect, TRUE);
439 * Don't otherwise disturb normal behavior.
441 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
444 static HWND TAB_InternalHitTest (
445 HWND hwnd,
446 TAB_INFO* infoPtr,
447 POINT pt,
448 UINT* flags)
451 RECT rect;
452 int iCount;
454 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
456 TAB_InternalGetItemRect(hwnd, infoPtr, iCount, &rect, NULL);
458 if (PtInRect(&rect, pt))
460 *flags = TCHT_ONITEM;
461 return iCount;
465 *flags = TCHT_NOWHERE;
466 return -1;
469 static LRESULT
470 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
472 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
473 LPTCHITTESTINFO lptest = (LPTCHITTESTINFO) lParam;
475 return TAB_InternalHitTest (hwnd, infoPtr, lptest->pt, &lptest->flags);
478 /******************************************************************************
479 * TAB_NCHitTest
481 * Napster v2b5 has a tab control for its main navigation which has a client
482 * area that covers the whole area of the dialog pages.
483 * That's why it receives all msgs for that area and the underlying dialog ctrls
484 * are dead.
485 * So I decided that we should handle WM_NCHITTEST here and return
486 * HTTRANSPARENT if we don't hit the tab control buttons.
487 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
488 * doesn't do it that way. Maybe depends on tab control styles ?
490 static LRESULT
491 TAB_NCHitTest (HWND hwnd, LPARAM lParam)
493 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
494 POINT pt;
495 UINT dummyflag;
497 pt.x = LOWORD(lParam);
498 pt.y = HIWORD(lParam);
499 ScreenToClient(hwnd, &pt);
501 if (TAB_InternalHitTest(hwnd, infoPtr, pt, &dummyflag) == -1)
502 return HTTRANSPARENT;
503 else
504 return HTCLIENT;
507 static LRESULT
508 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
510 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
511 POINT pt;
512 INT newItem, dummy;
514 if (infoPtr->hwndToolTip)
515 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
516 WM_LBUTTONDOWN, wParam, lParam);
518 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
519 SetFocus (hwnd);
522 if (infoPtr->hwndToolTip)
523 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
524 WM_LBUTTONDOWN, wParam, lParam);
526 pt.x = (INT)LOWORD(lParam);
527 pt.y = (INT)HIWORD(lParam);
529 newItem = TAB_InternalHitTest (hwnd, infoPtr, pt, &dummy);
531 TRACE("On Tab, item %d\n", newItem);
533 if ((newItem != -1) && (infoPtr->iSelected != newItem))
535 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING) != TRUE)
537 infoPtr->iSelected = newItem;
538 infoPtr->uFocus = newItem;
539 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
541 TAB_EnsureSelectionVisible(hwnd, infoPtr);
543 TAB_InvalidateTabArea(hwnd, infoPtr);
546 return 0;
549 static LRESULT
550 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
552 TAB_SendSimpleNotify(hwnd, NM_CLICK);
554 return 0;
557 static LRESULT
558 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
560 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
561 return 0;
564 /******************************************************************************
565 * TAB_DrawLoneItemInterior
567 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
568 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
569 * up the device context and font. This routine does the same setup but
570 * only calls TAB_DrawItemInterior for the single specified item.
572 static void
573 TAB_DrawLoneItemInterior(HWND hwnd, TAB_INFO* infoPtr, int iItem)
575 HDC hdc = GetDC(hwnd);
576 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
577 TAB_DrawItemInterior(hwnd, hdc, iItem, NULL);
578 SelectObject(hdc, hOldFont);
579 ReleaseDC(hwnd, hdc);
582 /******************************************************************************
583 * TAB_HotTrackTimerProc
585 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
586 * timer is setup so we can check if the mouse is moved out of our window.
587 * (We don't get an event when the mouse leaves, the mouse-move events just
588 * stop being delivered to our window and just start being delivered to
589 * another window.) This function is called when the timer triggers so
590 * we can check if the mouse has left our window. If so, we un-highlight
591 * the hot-tracked tab.
593 static VOID CALLBACK
594 TAB_HotTrackTimerProc
596 HWND hwnd, /* handle of window for timer messages */
597 UINT uMsg, /* WM_TIMER message */
598 UINT idEvent, /* timer identifier */
599 DWORD dwTime /* current system time */
602 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
604 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
606 POINT pt;
609 ** If we can't get the cursor position, or if the cursor is outside our
610 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
611 ** "outside" even if it is within our bounding rect if another window
612 ** overlaps. Note also that the case where the cursor stayed within our
613 ** window but has moved off the hot-tracked tab will be handled by the
614 ** WM_MOUSEMOVE event.
616 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
618 /* Redraw iHotTracked to look normal */
619 INT iRedraw = infoPtr->iHotTracked;
620 infoPtr->iHotTracked = -1;
621 TAB_DrawLoneItemInterior(hwnd, infoPtr, iRedraw);
623 /* Kill this timer */
624 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
629 /******************************************************************************
630 * TAB_RecalcHotTrack
632 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
633 * should be highlighted. This function determines which tab in a tab control,
634 * if any, is under the mouse and records that information. The caller may
635 * supply output parameters to receive the item number of the tab item which
636 * was highlighted but isn't any longer and of the tab item which is now
637 * highlighted but wasn't previously. The caller can use this information to
638 * selectively redraw those tab items.
640 * If the caller has a mouse position, it can supply it through the pos
641 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
642 * supplies NULL and this function determines the current mouse position
643 * itself.
645 static void
646 TAB_RecalcHotTrack
648 HWND hwnd,
649 const LPARAM* pos,
650 int* out_redrawLeave,
651 int* out_redrawEnter
654 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
656 int item = -1;
659 if (out_redrawLeave != NULL)
660 *out_redrawLeave = -1;
661 if (out_redrawEnter != NULL)
662 *out_redrawEnter = -1;
664 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_HOTTRACK)
666 POINT pt;
667 UINT flags;
669 if (pos == NULL)
671 GetCursorPos(&pt);
672 ScreenToClient(hwnd, &pt);
674 else
676 pt.x = LOWORD(*pos);
677 pt.y = HIWORD(*pos);
680 item = TAB_InternalHitTest(hwnd, infoPtr, pt, &flags);
683 if (item != infoPtr->iHotTracked)
685 if (infoPtr->iHotTracked >= 0)
687 /* Mark currently hot-tracked to be redrawn to look normal */
688 if (out_redrawLeave != NULL)
689 *out_redrawLeave = infoPtr->iHotTracked;
691 if (item < 0)
693 /* Kill timer which forces recheck of mouse pos */
694 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
697 else
699 /* Start timer so we recheck mouse pos */
700 UINT timerID = SetTimer
702 hwnd,
703 TAB_HOTTRACK_TIMER,
704 TAB_HOTTRACK_TIMER_INTERVAL,
705 TAB_HotTrackTimerProc
708 if (timerID == 0)
709 return; /* Hot tracking not available */
712 infoPtr->iHotTracked = item;
714 if (item >= 0)
716 /* Mark new hot-tracked to be redrawn to look highlighted */
717 if (out_redrawEnter != NULL)
718 *out_redrawEnter = item;
723 /******************************************************************************
724 * TAB_MouseMove
726 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
728 static LRESULT
729 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
731 int redrawLeave;
732 int redrawEnter;
734 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
736 if (infoPtr->hwndToolTip)
737 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
738 WM_LBUTTONDOWN, wParam, lParam);
740 /* Determine which tab to highlight. Redraw tabs which change highlight
741 ** status. */
742 TAB_RecalcHotTrack(hwnd, &lParam, &redrawLeave, &redrawEnter);
744 if (redrawLeave != -1)
745 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawLeave);
746 if (redrawEnter != -1)
747 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawEnter);
749 return 0;
752 /******************************************************************************
753 * TAB_AdjustRect
755 * Calculates the tab control's display area given the window rectangle or
756 * the window rectangle given the requested display rectangle.
758 static LRESULT TAB_AdjustRect(
759 HWND hwnd,
760 WPARAM fLarger,
761 LPRECT prc)
763 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
764 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
766 if(lStyle & TCS_VERTICAL)
768 if (fLarger) /* Go from display rectangle */
770 /* Add the height of the tabs. */
771 if (lStyle & TCS_BOTTOM)
772 prc->right += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
773 else
774 prc->left -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
776 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
777 /* Inflate the rectangle for the padding */
778 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
780 /* Inflate for the border */
781 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
783 else /* Go from window rectangle. */
785 /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */
786 /* Deflate the rectangle for the border */
787 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
789 /* Deflate the rectangle for the padding */
790 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
792 /* Remove the height of the tabs. */
793 if (lStyle & TCS_BOTTOM)
794 prc->right -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
795 else
796 prc->left += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
799 else {
800 if (fLarger) /* Go from display rectangle */
802 /* Add the height of the tabs. */
803 if (lStyle & TCS_BOTTOM)
804 prc->bottom += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
805 else
806 prc->top -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
808 /* Inflate the rectangle for the padding */
809 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
811 /* Inflate for the border */
812 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
814 else /* Go from window rectangle. */
816 /* Deflate the rectangle for the border */
817 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
819 /* Deflate the rectangle for the padding */
820 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
822 /* Remove the height of the tabs. */
823 if (lStyle & TCS_BOTTOM)
824 prc->bottom -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
825 else
826 prc->top += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
830 return 0;
833 /******************************************************************************
834 * TAB_OnHScroll
836 * This method will handle the notification from the scroll control and
837 * perform the scrolling operation on the tab control.
839 static LRESULT TAB_OnHScroll(
840 HWND hwnd,
841 int nScrollCode,
842 int nPos,
843 HWND hwndScroll)
845 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
847 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
849 if(nPos < infoPtr->leftmostVisible)
850 infoPtr->leftmostVisible--;
851 else
852 infoPtr->leftmostVisible++;
854 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
855 TAB_InvalidateTabArea(hwnd, infoPtr);
856 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
857 MAKELONG(infoPtr->leftmostVisible, 0));
860 return 0;
863 /******************************************************************************
864 * TAB_SetupScrolling
866 * This method will check the current scrolling state and make sure the
867 * scrolling control is displayed (or not).
869 static void TAB_SetupScrolling(
870 HWND hwnd,
871 TAB_INFO* infoPtr,
872 const RECT* clientRect)
874 INT maxRange = 0;
875 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
877 if (infoPtr->needsScrolling)
879 RECT controlPos;
880 INT vsize, tabwidth;
883 * Calculate the position of the scroll control.
885 if(lStyle & TCS_VERTICAL)
887 controlPos.right = clientRect->right;
888 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
890 if (lStyle & TCS_BOTTOM)
892 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
893 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
895 else
897 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
898 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
901 else
903 controlPos.right = clientRect->right;
904 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL);
906 if (lStyle & TCS_BOTTOM)
908 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
909 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
911 else
913 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
914 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
919 * If we don't have a scroll control yet, we want to create one.
920 * If we have one, we want to make sure it's positioned properly.
922 if (infoPtr->hwndUpDown==0)
924 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
926 WS_VISIBLE | WS_CHILD | UDS_HORZ,
927 controlPos.left, controlPos.top,
928 controlPos.right - controlPos.left,
929 controlPos.bottom - controlPos.top,
930 hwnd,
931 (HMENU)NULL,
932 (HINSTANCE)NULL,
933 NULL);
935 else
937 SetWindowPos(infoPtr->hwndUpDown,
938 (HWND)NULL,
939 controlPos.left, controlPos.top,
940 controlPos.right - controlPos.left,
941 controlPos.bottom - controlPos.top,
942 SWP_SHOWWINDOW | SWP_NOZORDER);
945 /* Now calculate upper limit of the updown control range.
946 * We do this by calculating how many tabs will be offscreen when the
947 * last tab is visible.
949 if(infoPtr->uNumItem)
951 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
952 maxRange = infoPtr->uNumItem;
953 tabwidth = infoPtr->items[maxRange - 1].rect.right;
955 for(; maxRange > 0; maxRange--)
957 if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
958 break;
961 if(maxRange == infoPtr->uNumItem)
962 maxRange--;
965 else
967 /* If we once had a scroll control... hide it */
968 if (infoPtr->hwndUpDown!=0)
969 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
971 if (infoPtr->hwndUpDown)
972 SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
975 /******************************************************************************
976 * TAB_SetItemBounds
978 * This method will calculate the position rectangles of all the items in the
979 * control. The rectangle calculated starts at 0 for the first item in the
980 * list and ignores scrolling and selection.
981 * It also uses the current font to determine the height of the tab row and
982 * it checks if all the tabs fit in the client area of the window. If they
983 * dont, a scrolling control is added.
985 static void TAB_SetItemBounds (HWND hwnd)
987 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
988 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
989 TEXTMETRICA fontMetrics;
990 INT curItem;
991 INT curItemLeftPos;
992 INT curItemRowCount;
993 HFONT hFont, hOldFont;
994 HDC hdc;
995 RECT clientRect;
996 SIZE size;
997 INT iTemp;
998 RECT* rcItem;
999 INT iIndex;
1002 * We need to get text information so we need a DC and we need to select
1003 * a font.
1005 hdc = GetDC(hwnd);
1007 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
1008 hOldFont = SelectObject (hdc, hFont);
1011 * We will base the rectangle calculations on the client rectangle
1012 * of the control.
1014 GetClientRect(hwnd, &clientRect);
1016 /* if TCS_VERTICAL then swap the height and width so this code places the
1017 tabs along the top of the rectangle and we can just rotate them after
1018 rather than duplicate all of the below code */
1019 if(lStyle & TCS_VERTICAL)
1021 iTemp = clientRect.bottom;
1022 clientRect.bottom = clientRect.right;
1023 clientRect.right = iTemp;
1026 /* The leftmost item will be "0" aligned */
1027 curItemLeftPos = 0;
1028 curItemRowCount = infoPtr->uNumItem ? 1 : 0;
1030 if (!(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet) )
1032 int item_height;
1033 int icon_height = 0;
1035 /* Use the current font to determine the height of a tab. */
1036 GetTextMetricsA(hdc, &fontMetrics);
1038 /* Get the icon height */
1039 if (infoPtr->himl)
1040 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
1042 /* Take the highest between font or icon */
1043 if (fontMetrics.tmHeight > icon_height)
1044 item_height = fontMetrics.tmHeight;
1045 else
1046 item_height = icon_height;
1049 * Make sure there is enough space for the letters + icon + growing the
1050 * selected item + extra space for the selected item.
1052 infoPtr->tabHeight = item_height + 2 * VERTICAL_ITEM_PADDING +
1053 SELECTED_TAB_OFFSET;
1057 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
1059 /* Set the leftmost position of the tab. */
1060 infoPtr->items[curItem].rect.left = curItemLeftPos;
1062 if ( (lStyle & TCS_FIXEDWIDTH) || ((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
1064 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1065 infoPtr->tabWidth +
1066 2 * HORIZONTAL_ITEM_PADDING;
1068 else
1070 int icon_width = 0;
1071 int num = 2;
1073 /* Calculate how wide the tab is depending on the text it contains */
1074 GetTextExtentPoint32W(hdc, infoPtr->items[curItem].pszText,
1075 lstrlenW(infoPtr->items[curItem].pszText), &size);
1077 /* under Windows, there seems to be a minimum width of 2x the height
1078 * for button style tabs */
1079 if (lStyle & TCS_BUTTONS)
1080 size.cx = max(size.cx, 2 * (infoPtr->tabHeight - 2));
1082 /* Add the icon width */
1083 if (infoPtr->himl)
1085 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
1086 num++;
1089 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
1090 size.cx + icon_width +
1091 num * HORIZONTAL_ITEM_PADDING;
1095 * Check if this is a multiline tab control and if so
1096 * check to see if we should wrap the tabs
1098 * Because we are going to arange all these tabs evenly
1099 * really we are basically just counting rows at this point
1103 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) &&
1104 (infoPtr->items[curItem].rect.right > clientRect.right))
1106 infoPtr->items[curItem].rect.right -=
1107 infoPtr->items[curItem].rect.left;
1109 infoPtr->items[curItem].rect.left = 0;
1110 curItemRowCount++;
1113 infoPtr->items[curItem].rect.bottom = 0;
1114 infoPtr->items[curItem].rect.top = curItemRowCount - 1;
1116 TRACE("TextSize: %li\n", size.cx);
1117 TRACE("Rect: T %i, L %i, B %i, R %i\n",
1118 infoPtr->items[curItem].rect.top,
1119 infoPtr->items[curItem].rect.left,
1120 infoPtr->items[curItem].rect.bottom,
1121 infoPtr->items[curItem].rect.right);
1124 * The leftmost position of the next item is the rightmost position
1125 * of this one.
1127 if (lStyle & TCS_BUTTONS)
1129 curItemLeftPos = infoPtr->items[curItem].rect.right + 1;
1130 if (lStyle & TCS_FLATBUTTONS)
1131 curItemLeftPos += FLAT_BTN_SPACINGX;
1133 else
1134 curItemLeftPos = infoPtr->items[curItem].rect.right;
1137 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)))
1140 * Check if we need a scrolling control.
1142 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) >
1143 clientRect.right);
1145 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1146 if(!infoPtr->needsScrolling)
1147 infoPtr->leftmostVisible = 0;
1149 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
1152 /* Set the number of rows */
1153 infoPtr->uNumRows = curItemRowCount;
1155 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->uNumItem > 0))
1157 INT widthDiff, remainder;
1158 INT tabPerRow,remTab;
1159 INT iRow,iItm;
1160 INT iIndexStart=0,iIndexEnd=0, iCount=0;
1163 * Ok Microsoft trys to even out the rows. place the same
1164 * number of tabs in each row. So lets give that a shot
1168 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows);
1169 remTab = infoPtr->uNumItem % (infoPtr->uNumRows);
1171 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1172 iItm<infoPtr->uNumItem;
1173 iItm++,iCount++)
1175 /* if we have reached the maximum number of tabs on this row */
1176 /* move to the next row, reset our current item left position and */
1177 /* the count of items on this row */
1178 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow))
1180 iRow++;
1181 curItemLeftPos = 0;
1182 iCount = 0;
1185 /* normalize the current rect */
1187 /* shift the item to the left side of the clientRect */
1188 infoPtr->items[iItm].rect.right -=
1189 infoPtr->items[iItm].rect.left;
1190 infoPtr->items[iItm].rect.left = 0;
1192 /* shift the item to the right to place it as the next item in this row */
1193 infoPtr->items[iItm].rect.left += curItemLeftPos;
1194 infoPtr->items[iItm].rect.right += curItemLeftPos;
1195 infoPtr->items[iItm].rect.top = iRow;
1196 if (lStyle & TCS_BUTTONS)
1198 curItemLeftPos = infoPtr->items[iItm].rect.right + 1;
1199 if (lStyle & TCS_FLATBUTTONS)
1200 curItemLeftPos += FLAT_BTN_SPACINGX;
1202 else
1203 curItemLeftPos = infoPtr->items[iItm].rect.right;
1207 * Justify the rows
1210 while(iIndexStart < infoPtr->uNumItem)
1213 * find the indexs of the row
1215 /* find the first item on the next row */
1216 for (iIndexEnd=iIndexStart;
1217 (iIndexEnd < infoPtr->uNumItem) &&
1218 (infoPtr->items[iIndexEnd].rect.top ==
1219 infoPtr->items[iIndexStart].rect.top) ;
1220 iIndexEnd++)
1221 /* intentionaly blank */;
1224 * we need to justify these tabs so they fill the whole given
1225 * client area
1228 /* find the amount of space remaining on this row */
1229 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) -
1230 infoPtr->items[iIndexEnd - 1].rect.right;
1232 /* iCount is the number of tab items on this row */
1233 iCount = iIndexEnd - iIndexStart;
1236 if (iCount > 1)
1238 remainder = widthDiff % iCount;
1239 widthDiff = widthDiff / iCount;
1240 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */
1241 for (iIndex=iIndexStart,iCount=0; iIndex < iIndexEnd;
1242 iIndex++,iCount++)
1244 infoPtr->items[iIndex].rect.left += iCount * widthDiff;
1245 infoPtr->items[iIndex].rect.right += (iCount + 1) * widthDiff;
1247 infoPtr->items[iIndex - 1].rect.right += remainder;
1249 else /* we have only one item on this row, make it take up the entire row */
1251 infoPtr->items[iIndexStart].rect.left = clientRect.left;
1252 infoPtr->items[iIndexStart].rect.right = clientRect.right - 4;
1256 iIndexStart = iIndexEnd;
1261 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */
1262 if(lStyle & TCS_VERTICAL)
1264 RECT rcOriginal;
1265 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++)
1267 rcItem = &(infoPtr->items[iIndex].rect);
1269 rcOriginal = *rcItem;
1271 /* this is rotating the items by 90 degrees around the center of the control */
1272 rcItem->top = (clientRect.right - (rcOriginal.left - clientRect.left)) - (rcOriginal.right - rcOriginal.left);
1273 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left);
1274 rcItem->left = rcOriginal.top;
1275 rcItem->right = rcOriginal.bottom;
1279 TAB_EnsureSelectionVisible(hwnd,infoPtr);
1280 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1282 /* Cleanup */
1283 SelectObject (hdc, hOldFont);
1284 ReleaseDC (hwnd, hdc);
1287 /******************************************************************************
1288 * TAB_DrawItemInterior
1290 * This method is used to draw the interior (text and icon) of a single tab
1291 * into the tab control.
1293 static void
1294 TAB_DrawItemInterior
1296 HWND hwnd,
1297 HDC hdc,
1298 INT iItem,
1299 RECT* drawRect
1302 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1303 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1305 RECT localRect;
1307 HPEN htextPen = GetSysColorPen (COLOR_BTNTEXT);
1308 HPEN holdPen;
1309 INT oldBkMode;
1311 if (drawRect == NULL)
1313 BOOL isVisible;
1314 RECT itemRect;
1315 RECT selectedRect;
1318 * Get the rectangle for the item.
1320 isVisible = TAB_InternalGetItemRect(hwnd, infoPtr, iItem, &itemRect, &selectedRect);
1321 if (!isVisible)
1322 return;
1325 * Make sure drawRect points to something valid; simplifies code.
1327 drawRect = &localRect;
1330 * This logic copied from the part of TAB_DrawItem which draws
1331 * the tab background. It's important to keep it in sync. I
1332 * would have liked to avoid code duplication, but couldn't figure
1333 * out how without making spaghetti of TAB_DrawItem.
1335 if (lStyle & TCS_BUTTONS)
1337 *drawRect = itemRect;
1338 if (iItem == infoPtr->iSelected)
1340 drawRect->right--;
1341 drawRect->bottom--;
1344 else
1346 if (iItem == infoPtr->iSelected)
1347 *drawRect = selectedRect;
1348 else
1349 *drawRect = itemRect;
1350 drawRect->right--;
1351 drawRect->bottom--;
1356 * Text pen
1358 holdPen = SelectObject(hdc, htextPen);
1360 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1361 SetTextColor(hdc, GetSysColor((iItem == infoPtr->iHotTracked) ? COLOR_HIGHLIGHT : COLOR_BTNTEXT));
1364 * Deflate the rectangle to acount for the padding
1366 if(lStyle & TCS_VERTICAL)
1367 InflateRect(drawRect, -VERTICAL_ITEM_PADDING, -HORIZONTAL_ITEM_PADDING);
1368 else
1369 InflateRect(drawRect, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
1373 * if owner draw, tell the owner to draw
1375 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd))
1377 DRAWITEMSTRUCT dis;
1378 UINT id;
1381 * get the control id
1383 id = GetWindowLongA( hwnd, GWL_ID );
1386 * put together the DRAWITEMSTRUCT
1388 dis.CtlType = ODT_TAB;
1389 dis.CtlID = id;
1390 dis.itemID = iItem;
1391 dis.itemAction = ODA_DRAWENTIRE;
1392 if ( iItem == infoPtr->iSelected )
1393 dis.itemState = ODS_SELECTED;
1394 else
1395 dis.itemState = 0;
1396 dis.hwndItem = hwnd; /* */
1397 dis.hDC = hdc;
1398 dis.rcItem = *drawRect; /* */
1399 dis.itemData = infoPtr->items[iItem].lParam;
1402 * send the draw message
1404 SendMessageA( GetParent(hwnd), WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1406 else
1408 INT cx;
1409 INT cy;
1410 UINT uHorizAlign;
1411 RECT rcTemp;
1412 RECT rcImage;
1413 LOGFONTA logfont;
1414 HFONT hFont = 0;
1415 HFONT hOldFont = 0; /* stop uninitialized warning */
1417 INT nEscapement = 0; /* stop uninitialized warning */
1418 INT nOrientation = 0; /* stop uninitialized warning */
1419 INT iPointSize;
1421 /* used to center the icon and text in the tab */
1422 RECT rcText;
1423 INT center_offset;
1425 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */
1426 rcImage = *drawRect;
1428 rcTemp = *drawRect;
1431 * Setup for text output
1433 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1434 SetTextColor(hdc, GetSysColor((iItem == infoPtr->iHotTracked) ? COLOR_HIGHLIGHT : COLOR_BTNTEXT));
1436 /* get the rectangle that the text fits in */
1437 DrawTextW(hdc, infoPtr->items[iItem].pszText, -1,
1438 &rcText, DT_CALCRECT);
1439 rcText.right += 4;
1441 * If not owner draw, then do the drawing ourselves.
1443 * Draw the icon.
1445 if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE))
1447 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1449 if(lStyle & TCS_VERTICAL)
1450 center_offset = ((drawRect->bottom - drawRect->top) - (cy + VERTICAL_ITEM_PADDING + (rcText.right - rcText.left))) / 2;
1451 else
1452 center_offset = ((drawRect->right - drawRect->left) - (cx + HORIZONTAL_ITEM_PADDING + (rcText.right - rcText.left))) / 2;
1454 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM))
1456 /* rcImage.left = drawRect->left; */ /* explicit from above rcImage = *drawRect */
1457 rcImage.top = drawRect->top + center_offset;
1458 rcImage.left = drawRect->right - cx; /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */
1459 /* right side of the tab, but the image still uses the left as its x position */
1460 /* this keeps the image always drawn off of the same side of the tab */
1461 drawRect->top = rcImage.top + (cx + VERTICAL_ITEM_PADDING);
1463 else if(lStyle & TCS_VERTICAL)
1465 /* rcImage.left = drawRect->left; */ /* explicit from above rcImage = *drawRect */
1466 rcImage.top = drawRect->bottom - cy - center_offset;
1468 drawRect->bottom = rcImage.top - VERTICAL_ITEM_PADDING;
1470 else /* normal style, whether TCS_BOTTOM or not */
1472 rcImage.left = drawRect->left + center_offset;
1473 /* rcImage.top = drawRect->top; */ /* explicit from above rcImage = *drawRect */
1475 drawRect->left = rcImage.left + cx + HORIZONTAL_ITEM_PADDING;
1478 ImageList_Draw
1480 infoPtr->himl,
1481 infoPtr->items[iItem].iImage,
1482 hdc,
1483 rcImage.left,
1484 rcImage.top + 1,
1485 ILD_NORMAL
1487 } else /* no image, so just shift the drawRect borders around */
1489 if(lStyle & TCS_VERTICAL)
1491 center_offset = 0;
1493 currently the rcText rect is flawed because the rotated font does not
1494 often match the horizontal font. So leave this as 0
1495 ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2;
1497 if(lStyle & TCS_BOTTOM)
1498 drawRect->top+=center_offset;
1499 else
1500 drawRect->bottom-=center_offset;
1502 else
1504 center_offset = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2;
1505 drawRect->left+=center_offset;
1509 /* Draw the text */
1510 if (lStyle & TCS_RIGHTJUSTIFY)
1511 uHorizAlign = DT_CENTER;
1512 else
1513 uHorizAlign = DT_LEFT;
1515 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */
1517 if(lStyle & TCS_BOTTOM)
1519 nEscapement = -900;
1520 nOrientation = -900;
1522 else
1524 nEscapement = 900;
1525 nOrientation = 900;
1529 /* to get a font with the escapement and orientation we are looking for, we need to */
1530 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */
1531 if(lStyle & TCS_VERTICAL)
1533 if (!GetObjectA((infoPtr->hFont) ?
1534 infoPtr->hFont : GetStockObject(SYSTEM_FONT),
1535 sizeof(LOGFONTA),&logfont))
1537 iPointSize = 9;
1539 lstrcpyA(logfont.lfFaceName, "Arial");
1540 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
1541 72);
1542 logfont.lfWeight = FW_NORMAL;
1543 logfont.lfItalic = 0;
1544 logfont.lfUnderline = 0;
1545 logfont.lfStrikeOut = 0;
1548 logfont.lfEscapement = nEscapement;
1549 logfont.lfOrientation = nOrientation;
1550 hFont = CreateFontIndirectA(&logfont);
1551 hOldFont = SelectObject(hdc, hFont);
1554 if (lStyle & TCS_VERTICAL)
1556 ExtTextOutW(hdc,
1557 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left,
1558 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top,
1559 ETO_CLIPPED,
1560 drawRect,
1561 infoPtr->items[iItem].pszText,
1562 lstrlenW(infoPtr->items[iItem].pszText),
1565 else
1567 DrawTextW
1569 hdc,
1570 infoPtr->items[iItem].pszText,
1571 lstrlenW(infoPtr->items[iItem].pszText),
1572 drawRect,
1573 uHorizAlign | DT_SINGLELINE
1577 /* clean things up */
1578 *drawRect = rcTemp; /* restore drawRect */
1580 if(lStyle & TCS_VERTICAL)
1582 SelectObject(hdc, hOldFont); /* restore the original font */
1583 if (hFont)
1584 DeleteObject(hFont);
1589 * Cleanup
1591 SetBkMode(hdc, oldBkMode);
1592 SelectObject(hdc, holdPen);
1595 /******************************************************************************
1596 * TAB_DrawItem
1598 * This method is used to draw a single tab into the tab control.
1600 static void TAB_DrawItem(
1601 HWND hwnd,
1602 HDC hdc,
1603 INT iItem)
1605 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1606 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1607 RECT itemRect;
1608 RECT selectedRect;
1609 BOOL isVisible;
1610 RECT r;
1613 * Get the rectangle for the item.
1615 isVisible = TAB_InternalGetItemRect(hwnd,
1616 infoPtr,
1617 iItem,
1618 &itemRect,
1619 &selectedRect);
1621 if (isVisible)
1623 HBRUSH hbr = CreateSolidBrush (GetSysColor(COLOR_BTNFACE));
1624 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
1625 HPEN hbPen = GetSysColorPen (COLOR_3DDKSHADOW);
1626 HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW);
1628 HPEN holdPen;
1629 BOOL deleteBrush = TRUE;
1631 if (lStyle & TCS_BUTTONS)
1633 /* Get item rectangle */
1634 r = itemRect;
1636 holdPen = SelectObject (hdc, hwPen);
1638 /* Separators between flat buttons */
1639 /* FIXME: test and correct this if necessary for TCS_FLATBUTTONS style */
1640 if (lStyle & TCS_FLATBUTTONS)
1642 int x = r.right + FLAT_BTN_SPACINGX - 2;
1644 /* highlight */
1645 MoveToEx (hdc, x, r.bottom - 1, NULL);
1646 LineTo (hdc, x, r.top - 1);
1647 x--;
1649 /* shadow */
1650 SelectObject(hdc, hbPen);
1651 MoveToEx (hdc, x, r.bottom - 1, NULL);
1652 LineTo (hdc, x, r.top - 1);
1654 /* shade */
1655 SelectObject (hdc, hShade );
1656 MoveToEx (hdc, x - 1, r.bottom - 1, NULL);
1657 LineTo (hdc, x - 1, r.top - 1);
1660 if (iItem == infoPtr->iSelected)
1662 /* Background color */
1663 if (!((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
1665 COLORREF bk = GetSysColor(COLOR_3DHILIGHT);
1666 DeleteObject(hbr);
1667 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1669 SetTextColor(hdc, GetSysColor(COLOR_3DFACE));
1670 SetBkColor(hdc, bk);
1672 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1673 * we better use 0x55aa bitmap brush to make scrollbar's background
1674 * look different from the window background.
1676 if (bk == GetSysColor(COLOR_WINDOW))
1677 hbr = COMCTL32_hPattern55AABrush;
1679 deleteBrush = FALSE;
1682 /* Erase the background */
1683 FillRect(hdc, &r, hbr);
1686 * Draw the tab now.
1687 * The rectangles calculated exclude the right and bottom
1688 * borders of the rectangle. To simplify the following code, those
1689 * borders are shaved-off beforehand.
1691 r.right--;
1692 r.bottom--;
1694 /* highlight */
1695 SelectObject(hdc, hwPen);
1696 MoveToEx (hdc, r.left, r.bottom, NULL);
1697 LineTo (hdc, r.right, r.bottom);
1698 LineTo (hdc, r.right, r.top + 1);
1700 /* shadow */
1701 SelectObject(hdc, hbPen);
1702 LineTo (hdc, r.left + 1, r.top + 1);
1703 LineTo (hdc, r.left + 1, r.bottom);
1705 /* shade */
1706 SelectObject (hdc, hShade );
1707 MoveToEx (hdc, r.right, r.top, NULL);
1708 LineTo (hdc, r.left, r.top);
1709 LineTo (hdc, r.left, r.bottom);
1711 else
1713 /* Erase the background */
1714 FillRect(hdc, &r, hbr);
1716 if (!(lStyle & TCS_FLATBUTTONS))
1718 /* highlight */
1719 MoveToEx (hdc, r.left, r.bottom, NULL);
1720 LineTo (hdc, r.left, r.top);
1721 LineTo (hdc, r.right, r.top);
1723 /* shadow */
1724 SelectObject(hdc, hbPen);
1725 LineTo (hdc, r.right, r.bottom);
1726 LineTo (hdc, r.left, r.bottom);
1728 /* shade */
1729 SelectObject (hdc, hShade );
1730 MoveToEx (hdc, r.right - 1, r.top, NULL);
1731 LineTo (hdc, r.right - 1, r.bottom - 1);
1732 LineTo (hdc, r.left + 1, r.bottom - 1);
1736 else /* !TCS_BUTTONS */
1738 /* Background color */
1739 DeleteObject(hbr);
1740 hbr = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1742 /* We draw a rectangle of different sizes depending on the selection
1743 * state. */
1744 if (iItem == infoPtr->iSelected)
1745 r = selectedRect;
1746 else
1747 r = itemRect;
1750 * Erase the background.
1751 * This is necessary when drawing the selected item since it is larger
1752 * than the others, it might overlap with stuff already drawn by the
1753 * other tabs
1755 FillRect(hdc, &r, hbr);
1758 * Draw the tab now.
1759 * The rectangles calculated exclude the right and bottom
1760 * borders of the rectangle. To simplify the following code, those
1761 * borders are shaved-off beforehand.
1763 r.right--;
1764 r.bottom--;
1766 holdPen = SelectObject (hdc, hwPen);
1767 if(lStyle & TCS_VERTICAL)
1769 if (lStyle & TCS_BOTTOM)
1771 /* highlight */
1772 MoveToEx (hdc, r.left, r.top, NULL);
1773 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top);
1774 LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE);
1776 /* shadow */
1777 SelectObject(hdc, hbPen);
1778 LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE);
1779 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom);
1780 LineTo (hdc, r.left - 1, r.bottom);
1782 /* shade */
1783 SelectObject (hdc, hShade );
1784 MoveToEx (hdc, r.right - 1, r.top, NULL);
1785 LineTo (hdc, r.right - 1, r.bottom - 1);
1786 LineTo (hdc, r.left - 1, r.bottom - 1);
1788 else
1790 /* highlight */
1791 MoveToEx (hdc, r.right, r.top, NULL);
1792 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top);
1793 LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE);
1794 LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE);
1796 /* shadow */
1797 SelectObject(hdc, hbPen);
1798 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom);
1799 LineTo (hdc, r.right + 1, r.bottom);
1801 /* shade */
1802 SelectObject (hdc, hShade );
1803 MoveToEx (hdc, r.left + ROUND_CORNER_SIZE - 1, r.bottom - 1, NULL);
1804 LineTo (hdc, r.right + 1, r.bottom - 1);
1807 else
1809 if (lStyle & TCS_BOTTOM)
1811 /* highlight */
1812 MoveToEx (hdc, r.left, r.top, NULL);
1813 LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE);
1814 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom);
1816 /* shadow */
1817 SelectObject(hdc, hbPen);
1818 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom);
1819 LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE);
1820 LineTo (hdc, r.right, r.top - 1);
1822 /* shade */
1823 SelectObject (hdc, hShade );
1824 MoveToEx (hdc, r.left, r.bottom - 1, NULL);
1825 LineTo (hdc, r.right - ROUND_CORNER_SIZE - 1, r.bottom - 1);
1826 LineTo (hdc, r.right - 1, r.bottom - ROUND_CORNER_SIZE - 1);
1827 LineTo (hdc, r.right - 1, r.top - 1);
1829 else
1831 /* highlight */
1832 if(infoPtr->items[iItem].rect.left == 0) /* if leftmost draw the line longer */
1833 MoveToEx (hdc, r.left, r.bottom, NULL);
1834 else
1835 MoveToEx (hdc, r.left, r.bottom - 1, NULL);
1837 LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE);
1838 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top);
1839 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top);
1841 /* shadow */
1842 SelectObject(hdc, hbPen);
1843 LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE);
1844 LineTo (hdc, r.right, r.bottom + 1);
1847 /* shade */
1848 SelectObject (hdc, hShade );
1849 MoveToEx (hdc, r.right - 1, r.top + ROUND_CORNER_SIZE, NULL);
1850 LineTo (hdc, r.right - 1, r.bottom + 1);
1855 /* This modifies r to be the text rectangle. */
1857 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1858 TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
1859 SelectObject(hdc,hOldFont);
1861 /* Draw the focus rectangle */
1862 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
1863 (GetFocus() == hwnd) &&
1864 (iItem == infoPtr->uFocus) )
1866 r = itemRect;
1867 InflateRect(&r, -1, -1);
1869 DrawFocusRect(hdc, &r);
1872 /* Cleanup */
1873 SelectObject(hdc, holdPen);
1874 if (deleteBrush) DeleteObject(hbr);
1878 /******************************************************************************
1879 * TAB_DrawBorder
1881 * This method is used to draw the raised border around the tab control
1882 * "content" area.
1884 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
1886 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1887 HPEN htmPen;
1888 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
1889 HPEN hbPen = GetSysColorPen (COLOR_3DDKSHADOW);
1890 HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW);
1891 RECT rect;
1892 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1894 GetClientRect (hwnd, &rect);
1897 * Adjust for the style
1900 if (infoPtr->uNumItem)
1902 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
1904 rect.bottom -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
1906 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
1908 rect.right -= (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
1910 else if(lStyle & TCS_VERTICAL)
1912 rect.left += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 2;
1914 else /* not TCS_VERTICAL and not TCS_BOTTOM */
1916 rect.top += (infoPtr->tabHeight - 2) * infoPtr->uNumRows + 1;
1921 * Shave-off the right and bottom margins (exluded in the
1922 * rect)
1924 rect.right--;
1925 rect.bottom--;
1927 /* highlight */
1928 htmPen = SelectObject (hdc, hwPen);
1930 MoveToEx (hdc, rect.left, rect.bottom, NULL);
1931 LineTo (hdc, rect.left, rect.top);
1932 LineTo (hdc, rect.right, rect.top);
1934 /* Dark Shadow */
1935 SelectObject (hdc, hbPen);
1936 LineTo (hdc, rect.right, rect.bottom );
1937 LineTo (hdc, rect.left, rect.bottom);
1939 /* shade */
1940 SelectObject (hdc, hShade );
1941 MoveToEx (hdc, rect.right - 1, rect.top, NULL);
1942 LineTo (hdc, rect.right - 1, rect.bottom - 1);
1943 LineTo (hdc, rect.left, rect.bottom - 1);
1945 SelectObject(hdc, htmPen);
1948 /******************************************************************************
1949 * TAB_Refresh
1951 * This method repaints the tab control..
1953 static void TAB_Refresh (HWND hwnd, HDC hdc)
1955 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1956 HFONT hOldFont;
1957 INT i;
1959 if (!infoPtr->DoRedraw)
1960 return;
1962 hOldFont = SelectObject (hdc, infoPtr->hFont);
1964 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
1966 for (i = 0; i < infoPtr->uNumItem; i++)
1967 TAB_DrawItem (hwnd, hdc, i);
1969 else
1971 /* Draw all the non selected item first */
1972 for (i = 0; i < infoPtr->uNumItem; i++)
1974 if (i != infoPtr->iSelected)
1975 TAB_DrawItem (hwnd, hdc, i);
1978 /* Now, draw the border, draw it before the selected item
1979 * since the selected item overwrites part of the border. */
1980 TAB_DrawBorder (hwnd, hdc);
1982 /* Then, draw the selected item */
1983 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
1985 /* If we haven't set the current focus yet, set it now.
1986 * Only happens when we first paint the tab controls */
1987 if (infoPtr->uFocus == -1)
1988 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
1991 SelectObject (hdc, hOldFont);
1994 static DWORD
1995 TAB_GetRowCount (HWND hwnd )
1997 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1999 return infoPtr->uNumRows;
2002 static LRESULT
2003 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
2005 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2007 infoPtr->DoRedraw=(BOOL) wParam;
2008 return 0;
2011 static LRESULT TAB_EraseBackground(
2012 HWND hwnd,
2013 HDC givenDC)
2015 HDC hdc;
2016 RECT clientRect;
2018 HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
2020 hdc = givenDC ? givenDC : GetDC(hwnd);
2022 GetClientRect(hwnd, &clientRect);
2024 FillRect(hdc, &clientRect, brush);
2026 if (givenDC==0)
2027 ReleaseDC(hwnd, hdc);
2029 DeleteObject(brush);
2031 return 0;
2034 /******************************************************************************
2035 * TAB_EnsureSelectionVisible
2037 * This method will make sure that the current selection is completely
2038 * visible by scrolling until it is.
2040 static void TAB_EnsureSelectionVisible(
2041 HWND hwnd,
2042 TAB_INFO* infoPtr)
2044 INT iSelected = infoPtr->iSelected;
2045 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2046 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
2048 /* set the items row to the bottommost row or topmost row depending on
2049 * style */
2050 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS))
2052 INT newselected;
2053 INT iTargetRow;
2055 if(lStyle & TCS_VERTICAL)
2056 newselected = infoPtr->items[iSelected].rect.left;
2057 else
2058 newselected = infoPtr->items[iSelected].rect.top;
2060 /* the target row is always (number of rows - 1)
2061 as row 0 is furthest from the clientRect */
2062 iTargetRow = infoPtr->uNumRows - 1;
2064 if (newselected != iTargetRow)
2066 INT i;
2067 if(lStyle & TCS_VERTICAL)
2069 for (i=0; i < infoPtr->uNumItem; i++)
2071 /* move everything in the row of the selected item to the iTargetRow */
2072 if (infoPtr->items[i].rect.left == newselected )
2073 infoPtr->items[i].rect.left = iTargetRow;
2074 else
2076 if (infoPtr->items[i].rect.left > newselected)
2077 infoPtr->items[i].rect.left-=1;
2081 else
2083 for (i=0; i < infoPtr->uNumItem; i++)
2085 if (infoPtr->items[i].rect.top == newselected )
2086 infoPtr->items[i].rect.top = iTargetRow;
2087 else
2089 if (infoPtr->items[i].rect.top > newselected)
2090 infoPtr->items[i].rect.top-=1;
2094 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2099 * Do the trivial cases first.
2101 if ( (!infoPtr->needsScrolling) ||
2102 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL))
2103 return;
2105 if (infoPtr->leftmostVisible >= iSelected)
2107 infoPtr->leftmostVisible = iSelected;
2109 else
2111 RECT r;
2112 INT width, i;
2114 /* Calculate the part of the client area that is visible */
2115 GetClientRect(hwnd, &r);
2116 width = r.right;
2118 GetClientRect(infoPtr->hwndUpDown, &r);
2119 width -= r.right;
2121 if ((infoPtr->items[iSelected].rect.right -
2122 infoPtr->items[iSelected].rect.left) >= width )
2124 /* Special case: width of selected item is greater than visible
2125 * part of control.
2127 infoPtr->leftmostVisible = iSelected;
2129 else
2131 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
2133 if ((infoPtr->items[iSelected].rect.right -
2134 infoPtr->items[i].rect.left) < width)
2135 break;
2137 infoPtr->leftmostVisible = i;
2141 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
2142 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
2144 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
2145 MAKELONG(infoPtr->leftmostVisible, 0));
2148 /******************************************************************************
2149 * TAB_InvalidateTabArea
2151 * This method will invalidate the portion of the control that contains the
2152 * tabs. It is called when the state of the control changes and needs
2153 * to be redisplayed
2155 static void TAB_InvalidateTabArea(
2156 HWND hwnd,
2157 TAB_INFO* infoPtr)
2159 RECT clientRect;
2160 DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2161 INT lastRow = infoPtr->uNumRows - 1;
2163 if (lastRow < 0) return;
2165 GetClientRect(hwnd, &clientRect);
2167 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL))
2169 clientRect.top = clientRect.bottom -
2170 infoPtr->tabHeight -
2171 lastRow * (infoPtr->tabHeight - 2) -
2172 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 2;
2174 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
2176 clientRect.left = clientRect.right - infoPtr->tabHeight -
2177 lastRow * (infoPtr->tabHeight - 2) -
2178 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) - 2;
2180 else if(lStyle & TCS_VERTICAL)
2182 clientRect.right = clientRect.left + infoPtr->tabHeight +
2183 lastRow * (infoPtr->tabHeight - 2) -
2184 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 1;
2187 else
2189 clientRect.bottom = clientRect.top + infoPtr->tabHeight +
2190 lastRow * (infoPtr->tabHeight - 2) +
2191 ((lStyle & TCS_BUTTONS) ? lastRow * BUTTON_SPACINGY : 0) + 1;
2194 InvalidateRect(hwnd, &clientRect, TRUE);
2197 static LRESULT
2198 TAB_Paint (HWND hwnd, WPARAM wParam)
2200 HDC hdc;
2201 PAINTSTRUCT ps;
2203 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2204 TAB_Refresh (hwnd, hdc);
2206 if(!wParam)
2207 EndPaint (hwnd, &ps);
2209 return 0;
2212 static LRESULT
2213 TAB_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2215 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2216 TCITEMA *pti;
2217 INT iItem;
2218 RECT rect;
2220 GetClientRect (hwnd, &rect);
2221 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
2222 rect.top, rect.left, rect.bottom, rect.right);
2224 pti = (TCITEMA *)lParam;
2225 iItem = (INT)wParam;
2227 if (iItem < 0) return -1;
2228 if (iItem > infoPtr->uNumItem)
2229 iItem = infoPtr->uNumItem;
2231 if (infoPtr->uNumItem == 0) {
2232 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
2233 infoPtr->uNumItem++;
2234 infoPtr->iSelected = 0;
2236 else {
2237 TAB_ITEM *oldItems = infoPtr->items;
2239 infoPtr->uNumItem++;
2240 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2242 /* pre insert copy */
2243 if (iItem > 0) {
2244 memcpy (&infoPtr->items[0], &oldItems[0],
2245 iItem * sizeof(TAB_ITEM));
2248 /* post insert copy */
2249 if (iItem < infoPtr->uNumItem - 1) {
2250 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2251 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2255 if (iItem <= infoPtr->iSelected)
2256 infoPtr->iSelected++;
2258 COMCTL32_Free (oldItems);
2261 infoPtr->items[iItem].mask = pti->mask;
2262 if (pti->mask & TCIF_TEXT)
2263 Str_SetPtrAtoW (&infoPtr->items[iItem].pszText, pti->pszText);
2265 if (pti->mask & TCIF_IMAGE)
2266 infoPtr->items[iItem].iImage = pti->iImage;
2268 if (pti->mask & TCIF_PARAM)
2269 infoPtr->items[iItem].lParam = pti->lParam;
2271 TAB_SetItemBounds(hwnd);
2272 TAB_InvalidateTabArea(hwnd, infoPtr);
2274 TRACE("[%04x]: added item %d '%s'\n",
2275 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2277 return iItem;
2281 static LRESULT
2282 TAB_InsertItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2284 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2285 TCITEMW *pti;
2286 INT iItem;
2287 RECT rect;
2289 GetClientRect (hwnd, &rect);
2290 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
2291 rect.top, rect.left, rect.bottom, rect.right);
2293 pti = (TCITEMW *)lParam;
2294 iItem = (INT)wParam;
2296 if (iItem < 0) return -1;
2297 if (iItem > infoPtr->uNumItem)
2298 iItem = infoPtr->uNumItem;
2300 if (infoPtr->uNumItem == 0) {
2301 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
2302 infoPtr->uNumItem++;
2303 infoPtr->iSelected = 0;
2305 else {
2306 TAB_ITEM *oldItems = infoPtr->items;
2308 infoPtr->uNumItem++;
2309 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
2311 /* pre insert copy */
2312 if (iItem > 0) {
2313 memcpy (&infoPtr->items[0], &oldItems[0],
2314 iItem * sizeof(TAB_ITEM));
2317 /* post insert copy */
2318 if (iItem < infoPtr->uNumItem - 1) {
2319 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
2320 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
2324 if (iItem <= infoPtr->iSelected)
2325 infoPtr->iSelected++;
2327 COMCTL32_Free (oldItems);
2330 infoPtr->items[iItem].mask = pti->mask;
2331 if (pti->mask & TCIF_TEXT)
2332 Str_SetPtrW (&infoPtr->items[iItem].pszText, pti->pszText);
2334 if (pti->mask & TCIF_IMAGE)
2335 infoPtr->items[iItem].iImage = pti->iImage;
2337 if (pti->mask & TCIF_PARAM)
2338 infoPtr->items[iItem].lParam = pti->lParam;
2340 TAB_SetItemBounds(hwnd);
2341 TAB_InvalidateTabArea(hwnd, infoPtr);
2343 TRACE("[%04x]: added item %d '%s'\n",
2344 hwnd, iItem, debugstr_w(infoPtr->items[iItem].pszText));
2346 return iItem;
2350 static LRESULT
2351 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
2353 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2354 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2355 LONG lResult = 0;
2357 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
2359 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
2360 infoPtr->tabWidth = (INT)LOWORD(lParam);
2361 infoPtr->tabHeight = (INT)HIWORD(lParam);
2363 infoPtr->fSizeSet = TRUE;
2365 return lResult;
2368 static LRESULT
2369 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2371 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2372 TCITEMA *tabItem;
2373 TAB_ITEM *wineItem;
2374 INT iItem;
2376 iItem = (INT)wParam;
2377 tabItem = (LPTCITEMA)lParam;
2379 TRACE("%d %p\n", iItem, tabItem);
2380 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2382 wineItem = &infoPtr->items[iItem];
2384 if (tabItem->mask & TCIF_IMAGE)
2385 wineItem->iImage = tabItem->iImage;
2387 if (tabItem->mask & TCIF_PARAM)
2388 wineItem->lParam = tabItem->lParam;
2390 if (tabItem->mask & TCIF_RTLREADING)
2391 FIXME("TCIF_RTLREADING\n");
2393 if (tabItem->mask & TCIF_STATE)
2394 wineItem->dwState = tabItem->dwState;
2396 if (tabItem->mask & TCIF_TEXT)
2397 Str_SetPtrAtoW(&wineItem->pszText, tabItem->pszText);
2399 /* Update and repaint tabs */
2400 TAB_SetItemBounds(hwnd);
2401 TAB_InvalidateTabArea(hwnd,infoPtr);
2403 return TRUE;
2407 static LRESULT
2408 TAB_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2410 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2411 TCITEMW *tabItem;
2412 TAB_ITEM *wineItem;
2413 INT iItem;
2415 iItem = (INT)wParam;
2416 tabItem = (LPTCITEMW)lParam;
2418 TRACE("%d %p\n", iItem, tabItem);
2419 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
2421 wineItem = &infoPtr->items[iItem];
2423 if (tabItem->mask & TCIF_IMAGE)
2424 wineItem->iImage = tabItem->iImage;
2426 if (tabItem->mask & TCIF_PARAM)
2427 wineItem->lParam = tabItem->lParam;
2429 if (tabItem->mask & TCIF_RTLREADING)
2430 FIXME("TCIF_RTLREADING\n");
2432 if (tabItem->mask & TCIF_STATE)
2433 wineItem->dwState = tabItem->dwState;
2435 if (tabItem->mask & TCIF_TEXT)
2436 Str_SetPtrW(&wineItem->pszText, tabItem->pszText);
2438 /* Update and repaint tabs */
2439 TAB_SetItemBounds(hwnd);
2440 TAB_InvalidateTabArea(hwnd,infoPtr);
2442 return TRUE;
2446 static LRESULT
2447 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
2449 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2451 return infoPtr->uNumItem;
2455 static LRESULT
2456 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2458 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2459 TCITEMA *tabItem;
2460 TAB_ITEM *wineItem;
2461 INT iItem;
2463 iItem = (INT)wParam;
2464 tabItem = (LPTCITEMA)lParam;
2465 TRACE("\n");
2466 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2467 return FALSE;
2469 wineItem = &infoPtr->items[iItem];
2471 if (tabItem->mask & TCIF_IMAGE)
2472 tabItem->iImage = wineItem->iImage;
2474 if (tabItem->mask & TCIF_PARAM)
2475 tabItem->lParam = wineItem->lParam;
2477 if (tabItem->mask & TCIF_RTLREADING)
2478 FIXME("TCIF_RTLREADING\n");
2480 if (tabItem->mask & TCIF_STATE)
2481 tabItem->dwState = wineItem->dwState;
2483 if (tabItem->mask & TCIF_TEXT)
2484 Str_GetPtrWtoA (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2486 return TRUE;
2490 static LRESULT
2491 TAB_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2493 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2494 TCITEMW *tabItem;
2495 TAB_ITEM *wineItem;
2496 INT iItem;
2498 iItem = (INT)wParam;
2499 tabItem = (LPTCITEMW)lParam;
2500 TRACE("\n");
2501 if ((iItem<0) || (iItem>=infoPtr->uNumItem))
2502 return FALSE;
2504 wineItem=& infoPtr->items[iItem];
2506 if (tabItem->mask & TCIF_IMAGE)
2507 tabItem->iImage = wineItem->iImage;
2509 if (tabItem->mask & TCIF_PARAM)
2510 tabItem->lParam = wineItem->lParam;
2512 if (tabItem->mask & TCIF_RTLREADING)
2513 FIXME("TCIF_RTLREADING\n");
2515 if (tabItem->mask & TCIF_STATE)
2516 tabItem->dwState = wineItem->dwState;
2518 if (tabItem->mask & TCIF_TEXT)
2519 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax);
2521 return TRUE;
2525 static LRESULT
2526 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2528 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2529 INT iItem = (INT) wParam;
2530 BOOL bResult = FALSE;
2532 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2534 TAB_ITEM *oldItems = infoPtr->items;
2536 infoPtr->uNumItem--;
2537 infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
2539 if (iItem > 0)
2540 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
2542 if (iItem < infoPtr->uNumItem)
2543 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
2544 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
2546 COMCTL32_Free(oldItems);
2548 /* Readjust the selected index */
2549 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2550 infoPtr->iSelected--;
2552 if (iItem < infoPtr->iSelected)
2553 infoPtr->iSelected--;
2555 if (infoPtr->uNumItem == 0)
2556 infoPtr->iSelected = -1;
2558 /* Reposition and repaint tabs */
2559 TAB_SetItemBounds(hwnd);
2560 TAB_InvalidateTabArea(hwnd,infoPtr);
2562 bResult = TRUE;
2565 return bResult;
2568 static LRESULT
2569 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2571 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2573 COMCTL32_Free (infoPtr->items);
2574 infoPtr->uNumItem = 0;
2575 infoPtr->iSelected = -1;
2576 if (infoPtr->iHotTracked >= 0)
2577 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2578 infoPtr->iHotTracked = -1;
2580 TAB_SetItemBounds(hwnd);
2581 TAB_InvalidateTabArea(hwnd,infoPtr);
2582 return TRUE;
2586 static LRESULT
2587 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2589 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2591 TRACE("\n");
2592 return (LRESULT)infoPtr->hFont;
2595 static LRESULT
2596 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2599 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2601 TRACE("%x %lx\n",wParam, lParam);
2603 infoPtr->hFont = (HFONT)wParam;
2605 TAB_SetItemBounds(hwnd);
2607 TAB_InvalidateTabArea(hwnd, infoPtr);
2609 return 0;
2613 static LRESULT
2614 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2616 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2618 TRACE("\n");
2619 return (LRESULT)infoPtr->himl;
2622 static LRESULT
2623 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2625 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2626 HIMAGELIST himlPrev;
2628 TRACE("\n");
2629 himlPrev = infoPtr->himl;
2630 infoPtr->himl= (HIMAGELIST)lParam;
2631 return (LRESULT)himlPrev;
2634 static LRESULT
2635 TAB_GetUnicodeFormat (HWND hwnd)
2637 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2638 return infoPtr->bUnicode;
2641 static LRESULT
2642 TAB_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
2644 TAB_INFO *infoPtr = TAB_GetInfoPtr (hwnd);
2645 BOOL bTemp = infoPtr->bUnicode;
2647 infoPtr->bUnicode = (BOOL)wParam;
2649 return bTemp;
2652 static LRESULT
2653 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2656 /* I'm not really sure what the following code was meant to do.
2657 This is what it is doing:
2658 When WM_SIZE is sent with SIZE_RESTORED, the control
2659 gets positioned in the top left corner.
2661 RECT parent_rect;
2662 HWND parent;
2663 UINT uPosFlags,cx,cy;
2665 uPosFlags=0;
2666 if (!wParam) {
2667 parent = GetParent (hwnd);
2668 GetClientRect(parent, &parent_rect);
2669 cx=LOWORD (lParam);
2670 cy=HIWORD (lParam);
2671 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2672 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2674 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2675 cx, cy, uPosFlags | SWP_NOZORDER);
2676 } else {
2677 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2678 } */
2680 /* Recompute the size/position of the tabs. */
2681 TAB_SetItemBounds (hwnd);
2683 /* Force a repaint of the control. */
2684 InvalidateRect(hwnd, NULL, TRUE);
2686 return 0;
2690 static LRESULT
2691 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2693 TAB_INFO *infoPtr;
2694 TEXTMETRICA fontMetrics;
2695 HDC hdc;
2696 HFONT hOldFont;
2697 DWORD dwStyle;
2699 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
2701 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
2703 infoPtr->uNumItem = 0;
2704 infoPtr->uNumRows = 0;
2705 infoPtr->hFont = 0;
2706 infoPtr->items = 0;
2707 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
2708 infoPtr->iSelected = -1;
2709 infoPtr->iHotTracked = -1;
2710 infoPtr->uFocus = -1;
2711 infoPtr->hwndToolTip = 0;
2712 infoPtr->DoRedraw = TRUE;
2713 infoPtr->needsScrolling = FALSE;
2714 infoPtr->hwndUpDown = 0;
2715 infoPtr->leftmostVisible = 0;
2716 infoPtr->fSizeSet = FALSE;
2717 infoPtr->bUnicode = IsWindowUnicode (hwnd);
2719 TRACE("Created tab control, hwnd [%04x]\n", hwnd);
2721 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2722 if you don't specify it in CreateWindow. This is necessary in
2723 order for paint to work correctly. This follows windows behaviour. */
2724 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
2725 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2727 if (dwStyle & TCS_TOOLTIPS) {
2728 /* Create tooltip control */
2729 infoPtr->hwndToolTip =
2730 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2731 CW_USEDEFAULT, CW_USEDEFAULT,
2732 CW_USEDEFAULT, CW_USEDEFAULT,
2733 hwnd, 0, 0, 0);
2735 /* Send NM_TOOLTIPSCREATED notification */
2736 if (infoPtr->hwndToolTip) {
2737 NMTOOLTIPSCREATED nmttc;
2739 nmttc.hdr.hwndFrom = hwnd;
2740 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
2741 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2742 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2744 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2745 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
2750 * We need to get text information so we need a DC and we need to select
2751 * a font.
2753 hdc = GetDC(hwnd);
2754 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
2756 /* Use the system font to determine the initial height of a tab. */
2757 GetTextMetricsA(hdc, &fontMetrics);
2760 * Make sure there is enough space for the letters + growing the
2761 * selected item + extra space for the selected item.
2763 infoPtr->tabHeight = fontMetrics.tmHeight + 2 * VERTICAL_ITEM_PADDING +
2764 SELECTED_TAB_OFFSET;
2766 /* Initialize the width of a tab. */
2767 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
2769 SelectObject (hdc, hOldFont);
2770 ReleaseDC(hwnd, hdc);
2772 return 0;
2775 static LRESULT
2776 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
2778 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2779 INT iItem;
2781 if (!infoPtr)
2782 return 0;
2784 if (infoPtr->items) {
2785 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
2786 if (infoPtr->items[iItem].pszText)
2787 COMCTL32_Free (infoPtr->items[iItem].pszText);
2789 COMCTL32_Free (infoPtr->items);
2792 if (infoPtr->hwndToolTip)
2793 DestroyWindow (infoPtr->hwndToolTip);
2795 if (infoPtr->hwndUpDown)
2796 DestroyWindow(infoPtr->hwndUpDown);
2798 if (infoPtr->iHotTracked >= 0)
2799 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2801 COMCTL32_Free (infoPtr);
2802 SetWindowLongA(hwnd, 0, 0);
2803 return 0;
2806 static LRESULT WINAPI
2807 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2810 TRACE("hwnd=%x msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
2811 if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
2812 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
2814 switch (uMsg)
2816 case TCM_GETIMAGELIST:
2817 return TAB_GetImageList (hwnd, wParam, lParam);
2819 case TCM_SETIMAGELIST:
2820 return TAB_SetImageList (hwnd, wParam, lParam);
2822 case TCM_GETITEMCOUNT:
2823 return TAB_GetItemCount (hwnd, wParam, lParam);
2825 case TCM_GETITEMA:
2826 return TAB_GetItemA (hwnd, wParam, lParam);
2828 case TCM_GETITEMW:
2829 return TAB_GetItemW (hwnd, wParam, lParam);
2831 case TCM_SETITEMA:
2832 return TAB_SetItemA (hwnd, wParam, lParam);
2834 case TCM_SETITEMW:
2835 return TAB_SetItemW (hwnd, wParam, lParam);
2837 case TCM_DELETEITEM:
2838 return TAB_DeleteItem (hwnd, wParam, lParam);
2840 case TCM_DELETEALLITEMS:
2841 return TAB_DeleteAllItems (hwnd, wParam, lParam);
2843 case TCM_GETITEMRECT:
2844 return TAB_GetItemRect (hwnd, wParam, lParam);
2846 case TCM_GETCURSEL:
2847 return TAB_GetCurSel (hwnd);
2849 case TCM_HITTEST:
2850 return TAB_HitTest (hwnd, wParam, lParam);
2852 case TCM_SETCURSEL:
2853 return TAB_SetCurSel (hwnd, wParam);
2855 case TCM_INSERTITEMA:
2856 return TAB_InsertItemA (hwnd, wParam, lParam);
2858 case TCM_INSERTITEMW:
2859 return TAB_InsertItemW (hwnd, wParam, lParam);
2861 case TCM_SETITEMEXTRA:
2862 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
2863 return 0;
2865 case TCM_ADJUSTRECT:
2866 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
2868 case TCM_SETITEMSIZE:
2869 return TAB_SetItemSize (hwnd, wParam, lParam);
2871 case TCM_REMOVEIMAGE:
2872 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
2873 return 0;
2875 case TCM_SETPADDING:
2876 FIXME("Unimplemented msg TCM_SETPADDING\n");
2877 return 0;
2879 case TCM_GETROWCOUNT:
2880 return TAB_GetRowCount(hwnd);
2882 case TCM_GETUNICODEFORMAT:
2883 return TAB_GetUnicodeFormat (hwnd);
2885 case TCM_SETUNICODEFORMAT:
2886 return TAB_SetUnicodeFormat (hwnd, wParam);
2888 case TCM_HIGHLIGHTITEM:
2889 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
2890 return 0;
2892 case TCM_GETTOOLTIPS:
2893 return TAB_GetToolTips (hwnd, wParam, lParam);
2895 case TCM_SETTOOLTIPS:
2896 return TAB_SetToolTips (hwnd, wParam, lParam);
2898 case TCM_GETCURFOCUS:
2899 return TAB_GetCurFocus (hwnd);
2901 case TCM_SETCURFOCUS:
2902 return TAB_SetCurFocus (hwnd, wParam);
2904 case TCM_SETMINTABWIDTH:
2905 FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
2906 return 0;
2908 case TCM_DESELECTALL:
2909 FIXME("Unimplemented msg TCM_DESELECTALL\n");
2910 return 0;
2912 case TCM_GETEXTENDEDSTYLE:
2913 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
2914 return 0;
2916 case TCM_SETEXTENDEDSTYLE:
2917 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
2918 return 0;
2920 case WM_GETFONT:
2921 return TAB_GetFont (hwnd, wParam, lParam);
2923 case WM_SETFONT:
2924 return TAB_SetFont (hwnd, wParam, lParam);
2926 case WM_CREATE:
2927 return TAB_Create (hwnd, wParam, lParam);
2929 case WM_NCDESTROY:
2930 return TAB_Destroy (hwnd, wParam, lParam);
2932 case WM_GETDLGCODE:
2933 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2935 case WM_LBUTTONDOWN:
2936 return TAB_LButtonDown (hwnd, wParam, lParam);
2938 case WM_LBUTTONUP:
2939 return TAB_LButtonUp (hwnd, wParam, lParam);
2941 case WM_RBUTTONDOWN:
2942 return TAB_RButtonDown (hwnd, wParam, lParam);
2944 case WM_MOUSEMOVE:
2945 return TAB_MouseMove (hwnd, wParam, lParam);
2947 case WM_ERASEBKGND:
2948 return TAB_EraseBackground (hwnd, (HDC)wParam);
2950 case WM_PAINT:
2951 return TAB_Paint (hwnd, wParam);
2953 case WM_SIZE:
2954 return TAB_Size (hwnd, wParam, lParam);
2956 case WM_SETREDRAW:
2957 return TAB_SetRedraw (hwnd, wParam);
2959 case WM_HSCROLL:
2960 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
2962 case WM_STYLECHANGED:
2963 TAB_SetItemBounds (hwnd);
2964 InvalidateRect(hwnd, NULL, TRUE);
2965 return 0;
2967 case WM_KILLFOCUS:
2968 case WM_SETFOCUS:
2969 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
2971 case WM_KEYUP:
2972 return TAB_KeyUp(hwnd, wParam);
2973 case WM_NCHITTEST:
2974 return TAB_NCHitTest(hwnd, lParam);
2976 default:
2977 if (uMsg >= WM_USER)
2978 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
2979 uMsg, wParam, lParam);
2980 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
2983 return 0;
2987 VOID
2988 TAB_Register (void)
2990 WNDCLASSA wndClass;
2992 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
2993 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
2994 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
2995 wndClass.cbClsExtra = 0;
2996 wndClass.cbWndExtra = sizeof(TAB_INFO *);
2997 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
2998 wndClass.hbrBackground = (HBRUSH)NULL;
2999 wndClass.lpszClassName = WC_TABCONTROLA;
3001 RegisterClassA (&wndClass);
3005 VOID
3006 TAB_Unregister (void)
3008 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);