Removed some unnecessary #includes and dll dependencies.
[wine/multimedia.git] / dlls / comctl32 / tab.c
blobe6553c682d4a71469752a298f2dfab58e4568297
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 * Multiline support
11 * Unicode support
14 #include <string.h>
16 #include "winbase.h"
17 #include "commctrl.h"
18 #include "debugtools.h"
19 #include "cache.h"
21 DEFAULT_DEBUG_CHANNEL(tab);
23 typedef struct
25 UINT mask;
26 DWORD dwState;
27 LPSTR pszText;
28 INT cchTextMax;
29 INT iImage;
30 LPARAM lParam;
31 RECT rect; /* bounding rectangle of the item relative to the
32 * leftmost item (the leftmost item, 0, would have a
33 * "left" member of 0 in this rectangle)
35 * additionally the top member hold the row number
36 * and bottom is unused and should be 0 */
37 } TAB_ITEM;
39 typedef struct
41 UINT uNumItem; /* number of tab items */
42 UINT uNumRows; /* number of tab rows */
43 INT tabHeight; /* height of the tab row */
44 INT tabWidth; /* width of tabs */
45 HFONT hFont; /* handle to the current font */
46 HCURSOR hcurArrow; /* handle to the current cursor */
47 HIMAGELIST himl; /* handle to a image list (may be 0) */
48 HWND hwndToolTip; /* handle to tab's tooltip */
49 UINT cchTextMax;
50 INT leftmostVisible; /* Used for scrolling, this member contains
51 * the index of the first visible item */
52 INT iSelected; /* the currently selected item */
53 INT iHotTracked; /* the highlighted item under the mouse */
54 INT uFocus; /* item which has the focus */
55 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */
56 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/
57 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than
58 * the size of the control */
59 BOOL fSizeSet; /* was the size of the tabs explicitly set? */
60 HWND hwndUpDown; /* Updown control used for scrolling */
61 } TAB_INFO;
63 /******************************************************************************
64 * Positioning constants
66 #define SELECTED_TAB_OFFSET 2
67 #define HORIZONTAL_ITEM_PADDING 5
68 #define VERTICAL_ITEM_PADDING 3
69 #define ROUND_CORNER_SIZE 2
70 #define FOCUS_RECT_HOFFSET 2
71 #define FOCUS_RECT_VOFFSET 1
72 #define DISPLAY_AREA_PADDINGX 2
73 #define DISPLAY_AREA_PADDINGY 2
74 #define CONTROL_BORDER_SIZEX 2
75 #define CONTROL_BORDER_SIZEY 2
76 #define BUTTON_SPACINGX 10
77 #define DEFAULT_TAB_WIDTH 96
79 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0))
81 /******************************************************************************
82 * Hot-tracking timer constants
84 #define TAB_HOTTRACK_TIMER 1
85 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */
87 /******************************************************************************
88 * Prototypes
90 static void TAB_Refresh (HWND hwnd, HDC hdc);
91 static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr);
92 static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr);
93 static void TAB_DrawItem(HWND hwnd, HDC hdc, INT iItem);
94 static void TAB_DrawItemInterior(HWND hwnd, HDC hdc, INT iItem, RECT* drawRect);
96 static BOOL
97 TAB_SendSimpleNotify (HWND hwnd, UINT code)
99 NMHDR nmhdr;
101 nmhdr.hwndFrom = hwnd;
102 nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
103 nmhdr.code = code;
105 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
106 (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr);
110 static VOID
111 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
112 WPARAM wParam, LPARAM lParam)
114 MSG msg;
116 msg.hwnd = hwndMsg;
117 msg.message = uMsg;
118 msg.wParam = wParam;
119 msg.lParam = lParam;
120 msg.time = GetMessageTime ();
121 msg.pt.x = LOWORD(GetMessagePos ());
122 msg.pt.y = HIWORD(GetMessagePos ());
124 SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
129 static LRESULT
130 TAB_GetCurSel (HWND hwnd)
132 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
134 return infoPtr->iSelected;
137 static LRESULT
138 TAB_GetCurFocus (HWND hwnd)
140 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
142 return infoPtr->uFocus;
145 static LRESULT
146 TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
148 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
150 if (infoPtr == NULL) return 0;
151 return infoPtr->hwndToolTip;
155 static LRESULT
156 TAB_SetCurSel (HWND hwnd,WPARAM wParam)
158 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
159 INT iItem=(INT) wParam;
160 INT prevItem;
162 prevItem=-1;
163 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) {
164 prevItem=infoPtr->iSelected;
165 infoPtr->iSelected=iItem;
166 TAB_EnsureSelectionVisible(hwnd, infoPtr);
167 TAB_InvalidateTabArea(hwnd, infoPtr);
169 return prevItem;
172 static LRESULT
173 TAB_SetCurFocus (HWND hwnd,WPARAM wParam)
175 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
176 INT iItem=(INT) wParam;
178 if ((iItem < 0) || (iItem >= infoPtr->uNumItem)) return 0;
180 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) {
181 FIXME("Should set input focus\n");
182 } else {
183 if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) {
184 infoPtr->uFocus=iItem;
185 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) {
186 infoPtr->iSelected = iItem;
187 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
189 TAB_EnsureSelectionVisible(hwnd, infoPtr);
190 TAB_InvalidateTabArea(hwnd, infoPtr);
194 return 0;
197 static LRESULT
198 TAB_SetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam)
200 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
202 if (infoPtr == NULL) return 0;
203 infoPtr->hwndToolTip = (HWND)wParam;
204 return 0;
207 /******************************************************************************
208 * TAB_InternalGetItemRect
210 * This method will calculate the rectangle representing a given tab item in
211 * client coordinates. This method takes scrolling into account.
213 * This method returns TRUE if the item is visible in the window and FALSE
214 * if it is completely outside the client area.
216 static BOOL TAB_InternalGetItemRect(
217 HWND hwnd,
218 TAB_INFO* infoPtr,
219 INT itemIndex,
220 RECT* itemRect,
221 RECT* selectedRect)
223 RECT tmpItemRect,clientRect;
224 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
227 * Perform a sanity check and a trivial visibility check.
229 if ( (infoPtr->uNumItem <=0) ||
230 (itemIndex >= infoPtr->uNumItem) ||
231 (!(lStyle &TCS_MULTILINE) && (itemIndex < infoPtr->leftmostVisible)) )
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;
242 * Retrieve the unmodified item rect.
244 *itemRect = infoPtr->items[itemIndex].rect;
247 * calculate the times bottom and top based on the row
249 GetClientRect(hwnd, &clientRect);
251 if (lStyle & TCS_BOTTOM)
253 itemRect->bottom = clientRect.bottom -
254 SELECTED_TAB_OFFSET -
255 itemRect->top * (infoPtr->tabHeight - 2);
257 itemRect->top = clientRect.bottom -
258 infoPtr->tabHeight -
259 itemRect->top * ( infoPtr->tabHeight - 2);
261 else
263 itemRect->bottom = clientRect.top +
264 infoPtr->tabHeight +
265 itemRect->top * (infoPtr->tabHeight - 2);
266 itemRect->top = clientRect.top +
267 SELECTED_TAB_OFFSET+
268 itemRect->top * (infoPtr->tabHeight - 2);
272 * "scroll" it to make sure the item at the very left of the
273 * tab control is the leftmost visible tab.
275 OffsetRect(itemRect,
276 -infoPtr->items[infoPtr->leftmostVisible].rect.left,
280 * Move the rectangle so the first item is slightly offset from
281 * the left of the tab control.
283 OffsetRect(itemRect,
284 SELECTED_TAB_OFFSET,
289 * Now, calculate the position of the item as if it were selected.
291 if (selectedRect!=NULL)
293 CopyRect(selectedRect, itemRect);
296 * The rectangle of a selected item is a bit wider.
298 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
301 * If it also a bit higher.
303 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
305 selectedRect->top -=2; /* the border is thicker on the bottom */
306 selectedRect->bottom +=SELECTED_TAB_OFFSET;
308 else
310 selectedRect->top -=SELECTED_TAB_OFFSET;
311 selectedRect->bottom+=1;
315 return TRUE;
318 static BOOL TAB_GetItemRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
320 return TAB_InternalGetItemRect(hwnd, TAB_GetInfoPtr(hwnd), (INT)wParam,
321 (LPRECT)lParam, (LPRECT)NULL);
324 /******************************************************************************
325 * TAB_KeyUp
327 * This method is called to handle keyboard input
329 static LRESULT TAB_KeyUp(
330 HWND hwnd,
331 WPARAM keyCode)
333 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
334 int newItem = -1;
336 switch (keyCode)
338 case VK_LEFT:
339 newItem = infoPtr->uFocus-1;
340 break;
341 case VK_RIGHT:
342 newItem = infoPtr->uFocus+1;
343 break;
347 * If we changed to a valid item, change the selection
349 if ( (newItem >= 0) &&
350 (newItem < infoPtr->uNumItem) &&
351 (infoPtr->uFocus != newItem) )
353 if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING))
355 infoPtr->iSelected = newItem;
356 infoPtr->uFocus = newItem;
357 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
359 TAB_EnsureSelectionVisible(hwnd, infoPtr);
360 TAB_InvalidateTabArea(hwnd, infoPtr);
364 return 0;
367 /******************************************************************************
368 * TAB_FocusChanging
370 * This method is called whenever the focus goes in or out of this control
371 * it is used to update the visual state of the control.
373 static LRESULT TAB_FocusChanging(
374 HWND hwnd,
375 UINT uMsg,
376 WPARAM wParam,
377 LPARAM lParam)
379 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
380 RECT selectedRect;
381 BOOL isVisible;
384 * Get the rectangle for the item.
386 isVisible = TAB_InternalGetItemRect(hwnd,
387 infoPtr,
388 infoPtr->uFocus,
389 NULL,
390 &selectedRect);
393 * If the rectangle is not completely invisible, invalidate that
394 * portion of the window.
396 if (isVisible)
398 InvalidateRect(hwnd, &selectedRect, TRUE);
402 * Don't otherwise disturb normal behavior.
404 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
407 static HWND TAB_InternalHitTest (
408 HWND hwnd,
409 TAB_INFO* infoPtr,
410 POINT pt,
411 UINT* flags)
414 RECT rect;
415 int iCount;
417 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
419 TAB_InternalGetItemRect(hwnd,
420 infoPtr,
421 iCount,
422 &rect,
423 NULL);
425 if (PtInRect (&rect, pt))
427 *flags = TCHT_ONITEM;
428 return iCount;
432 *flags=TCHT_NOWHERE;
433 return -1;
436 static LRESULT
437 TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
439 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
440 LPTCHITTESTINFO lptest=(LPTCHITTESTINFO) lParam;
442 return TAB_InternalHitTest (hwnd, infoPtr,lptest->pt,&lptest->flags);
446 static LRESULT
447 TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
449 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
450 POINT pt;
451 INT newItem,dummy;
453 if (infoPtr->hwndToolTip)
454 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
455 WM_LBUTTONDOWN, wParam, lParam);
457 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
458 SetFocus (hwnd);
461 if (infoPtr->hwndToolTip)
462 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
463 WM_LBUTTONDOWN, wParam, lParam);
465 pt.x = (INT)LOWORD(lParam);
466 pt.y = (INT)HIWORD(lParam);
468 newItem=TAB_InternalHitTest (hwnd, infoPtr,pt,&dummy);
470 TRACE("On Tab, item %d\n", newItem);
472 if ( (newItem!=-1) &&
473 (infoPtr->iSelected != newItem) )
475 if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE)
477 infoPtr->iSelected = newItem;
478 infoPtr->uFocus = newItem;
479 TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE);
481 TAB_EnsureSelectionVisible(hwnd, infoPtr);
483 TAB_InvalidateTabArea(hwnd, infoPtr);
486 return 0;
489 static LRESULT
490 TAB_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
492 TAB_SendSimpleNotify(hwnd, NM_CLICK);
494 return 0;
497 static LRESULT
498 TAB_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
500 TAB_SendSimpleNotify(hwnd, NM_RCLICK);
501 return 0;
504 /******************************************************************************
505 * TAB_DrawLoneItemInterior
507 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
508 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets
509 * up the device context and font. This routine does the same setup but
510 * only calls TAB_DrawItemInterior for the single specified item.
512 static void
513 TAB_DrawLoneItemInterior(HWND hwnd, TAB_INFO* infoPtr, int iItem)
515 HDC hdc = GetDC(hwnd);
516 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
517 TAB_DrawItemInterior(hwnd, hdc, iItem, NULL);
518 SelectObject(hdc, hOldFont);
519 ReleaseDC(hwnd, hdc);
522 /******************************************************************************
523 * TAB_HotTrackTimerProc
525 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a
526 * timer is setup so we can check if the mouse is moved out of our window.
527 * (We don't get an event when the mouse leaves, the mouse-move events just
528 * stop being delivered to our window and just start being delivered to
529 * another window.) This function is called when the timer triggers so
530 * we can check if the mouse has left our window. If so, we un-highlight
531 * the hot-tracked tab.
533 static VOID CALLBACK
534 TAB_HotTrackTimerProc
536 HWND hwnd, /* handle of window for timer messages */
537 UINT uMsg, /* WM_TIMER message */
538 UINT idEvent, /* timer identifier */
539 DWORD dwTime /* current system time */
542 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
544 if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
546 POINT pt;
549 ** If we can't get the cursor position, or if the cursor is outside our
550 ** window, we un-highlight the hot-tracked tab. Note that the cursor is
551 ** "outside" even if it is within our bounding rect if another window
552 ** overlaps. Note also that the case where the cursor stayed within our
553 ** window but has moved off the hot-tracked tab will be handled by the
554 ** WM_MOUSEMOVE event.
556 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
558 /* Redraw iHotTracked to look normal */
559 INT iRedraw = infoPtr->iHotTracked;
560 infoPtr->iHotTracked = -1;
561 TAB_DrawLoneItemInterior(hwnd, infoPtr, iRedraw);
563 /* Kill this timer */
564 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
569 /******************************************************************************
570 * TAB_RecalcHotTrack
572 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
573 * should be highlighted. This function determines which tab in a tab control,
574 * if any, is under the mouse and records that information. The caller may
575 * supply output parameters to receive the item number of the tab item which
576 * was highlighted but isn't any longer and of the tab item which is now
577 * highlighted but wasn't previously. The caller can use this information to
578 * selectively redraw those tab items.
580 * If the caller has a mouse position, it can supply it through the pos
581 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller
582 * supplies NULL and this function determines the current mouse position
583 * itself.
585 static void
586 TAB_RecalcHotTrack
588 HWND hwnd,
589 const LPARAM* pos,
590 int* out_redrawLeave,
591 int* out_redrawEnter
594 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
596 int item = -1;
599 if (out_redrawLeave != NULL)
600 *out_redrawLeave = -1;
601 if (out_redrawEnter != NULL)
602 *out_redrawEnter = -1;
604 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_HOTTRACK)
606 POINT pt;
607 UINT flags;
609 if (pos == NULL)
611 GetCursorPos(&pt);
612 ScreenToClient(hwnd, &pt);
614 else
616 pt.x = LOWORD(*pos);
617 pt.y = HIWORD(*pos);
620 item = TAB_InternalHitTest(hwnd, infoPtr, pt, &flags);
623 if (item != infoPtr->iHotTracked)
625 if (infoPtr->iHotTracked >= 0)
627 /* Mark currently hot-tracked to be redrawn to look normal */
628 if (out_redrawLeave != NULL)
629 *out_redrawLeave = infoPtr->iHotTracked;
631 if (item < 0)
633 /* Kill timer which forces recheck of mouse pos */
634 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
637 else
639 /* Start timer so we recheck mouse pos */
640 UINT timerID = SetTimer
642 hwnd,
643 TAB_HOTTRACK_TIMER,
644 TAB_HOTTRACK_TIMER_INTERVAL,
645 TAB_HotTrackTimerProc
648 if (timerID == 0)
649 return; /* Hot tracking not available */
652 infoPtr->iHotTracked = item;
654 if (item >= 0)
656 /* Mark new hot-tracked to be redrawn to look highlighted */
657 if (out_redrawEnter != NULL)
658 *out_redrawEnter = item;
663 /******************************************************************************
664 * TAB_MouseMove
666 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
668 static LRESULT
669 TAB_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
671 int redrawLeave;
672 int redrawEnter;
674 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
676 if (infoPtr->hwndToolTip)
677 TAB_RelayEvent (infoPtr->hwndToolTip, hwnd,
678 WM_LBUTTONDOWN, wParam, lParam);
680 /* Determine which tab to highlight. Redraw tabs which change highlight
681 ** status. */
682 TAB_RecalcHotTrack(hwnd, &lParam, &redrawLeave, &redrawEnter);
684 if (redrawLeave != -1)
685 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawLeave);
686 if (redrawEnter != -1)
687 TAB_DrawLoneItemInterior(hwnd, infoPtr, redrawEnter);
689 return 0;
692 /******************************************************************************
693 * TAB_AdjustRect
695 * Calculates the tab control's display area given the windows rectangle or
696 * the window rectangle given the requested display rectangle.
698 static LRESULT TAB_AdjustRect(
699 HWND hwnd,
700 WPARAM fLarger,
701 LPRECT prc)
703 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
705 if (fLarger)
708 * Go from display rectangle
712 * Add the height of the tabs.
714 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
715 prc->bottom += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
716 else
717 prc->top -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
720 * Inflate the rectangle for the padding
722 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY);
725 * Inflate for the border
727 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX);
729 else
732 * Go from window rectangle.
736 * Deflate the rectangle for the border
738 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX);
741 * Deflate the rectangle for the padding
743 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY);
746 * Remove the height of the tabs.
748 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
749 prc->bottom -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
750 else
751 prc->top += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
755 return 0;
758 /******************************************************************************
759 * TAB_OnHScroll
761 * This method will handle the notification from the scroll control and
762 * perform the scrolling operation on the tab control.
764 static LRESULT TAB_OnHScroll(
765 HWND hwnd,
766 int nScrollCode,
767 int nPos,
768 HWND hwndScroll)
770 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
772 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible)
774 if(nPos < infoPtr->leftmostVisible)
775 infoPtr->leftmostVisible--;
776 else
777 infoPtr->leftmostVisible++;
779 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
780 TAB_InvalidateTabArea(hwnd, infoPtr);
781 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
782 MAKELONG(infoPtr->leftmostVisible, 0));
785 return 0;
788 /******************************************************************************
789 * TAB_SetupScroling
791 * This method will check the current scrolling state and make sure the
792 * scrolling control is displayed (or not).
794 static void TAB_SetupScrolling(
795 HWND hwnd,
796 TAB_INFO* infoPtr,
797 const RECT* clientRect)
799 INT maxRange = 0;
800 if (infoPtr->needsScrolling)
802 RECT controlPos;
803 INT vsize, tabwidth;
806 * Calculate the position of the scroll control.
808 controlPos.right = clientRect->right;
809 controlPos.left = controlPos.right - 2*GetSystemMetrics(SM_CXHSCROLL);
811 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
813 controlPos.top = clientRect->bottom - infoPtr->tabHeight;
814 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL);
816 else
818 controlPos.bottom = clientRect->top + infoPtr->tabHeight;
819 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL);
823 * If we don't have a scroll control yet, we want to create one.
824 * If we have one, we want to make sure it's positioned right.
826 if (infoPtr->hwndUpDown==0)
829 * I use a scrollbar since it seems to be more stable than the Updown
830 * control.
832 infoPtr->hwndUpDown = CreateWindowA("msctls_updown32",
834 WS_VISIBLE | WS_CHILD | UDS_HORZ,
835 controlPos.left, controlPos.top,
836 controlPos.right - controlPos.left,
837 controlPos.bottom - controlPos.top,
838 hwnd,
839 (HMENU)NULL,
840 (HINSTANCE)NULL,
841 NULL);
843 else
845 SetWindowPos(infoPtr->hwndUpDown,
846 (HWND)NULL,
847 controlPos.left, controlPos.top,
848 controlPos.right - controlPos.left,
849 controlPos.bottom - controlPos.top,
850 SWP_SHOWWINDOW | SWP_NOZORDER);
853 /* Now calculate upper limit of the updown control range.
854 * We do this by calculating how many tabs will be offscreen when the
855 * last tab is visible.
857 if(infoPtr->uNumItem)
859 vsize = clientRect->right - (controlPos.right - controlPos.left + 1);
860 maxRange = infoPtr->uNumItem;
861 tabwidth = infoPtr->items[maxRange-1].rect.right;
863 for(; maxRange > 0; maxRange--)
865 if(tabwidth - infoPtr->items[maxRange - 1].rect.left > vsize)
866 break;
869 if(maxRange == infoPtr->uNumItem)
870 maxRange--;
873 else
876 * If we once had a scroll control... hide it.
878 if (infoPtr->hwndUpDown!=0)
880 ShowWindow(infoPtr->hwndUpDown, SW_HIDE);
883 if (infoPtr->hwndUpDown)
884 SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange);
887 /******************************************************************************
888 * TAB_SetItemBounds
890 * This method will calculate the position rectangles of all the items in the
891 * control. The rectangle calculated starts at 0 for the first item in the
892 * list and ignores scrolling and selection.
893 * It also uses the current font to determine the height of the tab row and
894 * it checks if all the tabs fit in the client area of the window. If they
895 * dont, a scrolling control is added.
897 static void TAB_SetItemBounds (HWND hwnd)
899 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
900 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
901 TEXTMETRICA fontMetrics;
902 INT curItem;
903 INT curItemLeftPos;
904 INT curItemRowCount;
905 HFONT hFont, hOldFont;
906 HDC hdc;
907 RECT clientRect;
908 SIZE size;
911 * We need to get text information so we need a DC and we need to select
912 * a font.
914 hdc = GetDC(hwnd);
916 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
917 hOldFont = SelectObject (hdc, hFont);
920 * We will base the rectangle calculations on the client rectangle
921 * of the control.
923 GetClientRect(hwnd, &clientRect);
926 * The leftmost item will be "0" aligned
928 curItemLeftPos = 0;
929 curItemRowCount = 0;
931 if ( !(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet) )
933 int item_height;
934 int icon_height = 0;
937 * Use the current font to determine the height of a tab.
939 GetTextMetricsA(hdc, &fontMetrics);
942 * Get the icon height
944 if (infoPtr->himl)
945 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height);
948 * Take the highest between font or icon
950 if (fontMetrics.tmHeight > icon_height)
951 item_height = fontMetrics.tmHeight;
952 else
953 item_height = icon_height;
956 * Make sure there is enough space for the letters + icon + growing the
957 * selected item + extra space for the selected item.
959 infoPtr->tabHeight = item_height + 2*VERTICAL_ITEM_PADDING +
960 SELECTED_TAB_OFFSET;
963 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++)
966 * Set the leftmost position of the tab.
968 infoPtr->items[curItem].rect.left = curItemLeftPos;
970 if ( (lStyle & TCS_FIXEDWIDTH) || ((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
972 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
973 infoPtr->tabWidth +
974 2*HORIZONTAL_ITEM_PADDING;
976 else
978 int icon_width = 0;
979 int num = 2;
982 * Calculate how wide the tab is depending on the text it contains
984 GetTextExtentPoint32A(hdc, infoPtr->items[curItem].pszText,
985 lstrlenA(infoPtr->items[curItem].pszText), &size);
988 * Add the icon width
990 if (infoPtr->himl)
992 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0);
993 num++;
996 infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left +
997 size.cx + icon_width +
998 num*HORIZONTAL_ITEM_PADDING;
1002 * Check if this is a multiline tab control and if so
1003 * check to see if we should wrap the tabs
1005 * Because we are going to arange all these tabs evenly
1006 * really we are basically just counting rows at this point
1010 if ((lStyle & TCS_MULTILINE)&&
1011 (infoPtr->items[curItem].rect.right > clientRect.right))
1013 infoPtr->items[curItem].rect.right -=
1014 infoPtr->items[curItem].rect.left;
1015 infoPtr->items[curItem].rect.left = 0;
1016 curItemRowCount ++;
1019 infoPtr->items[curItem].rect.bottom = 0;
1020 infoPtr->items[curItem].rect.top = curItemRowCount;
1022 TRACE("TextSize: %i\n ", size.cx);
1023 TRACE("Rect: T %i, L %i, B %i, R %i\n",
1024 infoPtr->items[curItem].rect.top,
1025 infoPtr->items[curItem].rect.left,
1026 infoPtr->items[curItem].rect.bottom,
1027 infoPtr->items[curItem].rect.right);
1030 * The leftmost position of the next item is the rightmost position
1031 * of this one.
1033 if (lStyle & TCS_BUTTONS)
1034 curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX;
1035 else
1036 curItemLeftPos = infoPtr->items[curItem].rect.right;
1039 if (!(lStyle & TCS_MULTILINE))
1042 * Check if we need a scrolling control.
1044 infoPtr->needsScrolling = (curItemLeftPos + (2*SELECTED_TAB_OFFSET) >
1045 clientRect.right);
1047 /* Don't need scrolling, then update infoPtr->leftmostVisible */
1048 if(!infoPtr->needsScrolling)
1049 infoPtr->leftmostVisible = 0;
1051 TAB_SetupScrolling(hwnd, infoPtr, &clientRect);
1055 * Set the number of rows
1058 infoPtr->uNumRows = curItemRowCount;
1060 if ((lStyle & TCS_MULTILINE)&&(infoPtr->uNumItem > 0))
1062 INT widthDiff,remainder;
1063 INT tabPerRow,remTab;
1064 INT iRow,iItm;
1065 INT iIndexStart=0,iIndexEnd=0, iCount=0;
1068 * Ok Microsoft trys to even out the rows. place the same
1069 * number of tabs in each row. So lets give that a shot
1073 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows + 1);
1074 remTab = infoPtr->uNumItem % (infoPtr->uNumRows + 1);
1076 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0;
1077 iItm<infoPtr->uNumItem;
1078 iItm++,iCount++)
1080 if (iCount >= ((iRow<remTab)?tabPerRow+1:tabPerRow))
1082 iRow++;
1083 curItemLeftPos = 0;
1084 iCount = 0;
1087 * normalize the current rect
1089 infoPtr->items[iItm].rect.right -=
1090 infoPtr->items[iItm].rect.left;
1091 infoPtr->items[iItm].rect.left = 0;
1093 infoPtr->items[iItm].rect.top = iRow;
1094 infoPtr->items[iItm].rect.left +=curItemLeftPos;
1095 infoPtr->items[iItm].rect.right +=curItemLeftPos;
1096 if (lStyle & TCS_BUTTONS)
1097 curItemLeftPos = infoPtr->items[iItm].rect.right +
1098 BUTTON_SPACINGX;
1099 else
1100 curItemLeftPos = infoPtr->items[iItm].rect.right;
1104 * Justify the rows
1108 while(iIndexStart < infoPtr->uNumItem)
1111 * find the indexs of the row
1113 for (iIndexEnd=iIndexStart;
1114 (iIndexEnd < infoPtr->uNumItem) &&
1115 (infoPtr->items[iIndexEnd].rect.top ==
1116 infoPtr->items[iIndexStart].rect.top) ;
1117 iIndexEnd++)
1118 /* intentionaly blank */;
1121 * we need to justify these tabs so they fill the whole given
1122 * client area
1125 widthDiff = clientRect.right - (2*SELECTED_TAB_OFFSET) -
1126 infoPtr->items[iIndexEnd-1].rect.right;
1128 iCount = iIndexEnd-iIndexStart;
1130 if (iCount>1)
1132 INT iIndex;
1133 remainder = widthDiff % iCount;
1134 widthDiff = widthDiff / iCount;
1135 for (iIndex=iIndexStart,iCount=0; iIndex < iIndexEnd;
1136 iIndex++,iCount++)
1138 infoPtr->items[iIndex].rect.left +=iCount*widthDiff;
1139 infoPtr->items[iIndex].rect.right +=(iCount+1)*widthDiff;
1141 infoPtr->items[iIndex-1].rect.right += remainder;
1144 iIndexStart=iIndexEnd;
1149 TAB_EnsureSelectionVisible(hwnd,infoPtr);
1150 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1152 * Cleanup
1154 SelectObject (hdc, hOldFont);
1155 ReleaseDC (hwnd, hdc);
1158 /******************************************************************************
1159 * TAB_DrawItemInterior
1161 * This method is used to draw the interior (text and icon) of a single tab
1162 * into the tab control.
1164 static void
1165 TAB_DrawItemInterior
1167 HWND hwnd,
1168 HDC hdc,
1169 INT iItem,
1170 RECT* drawRect
1173 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
1174 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1176 RECT localRect;
1178 HPEN htextPen = GetSysColorPen (COLOR_BTNTEXT);
1179 HPEN holdPen;
1180 INT oldBkMode;
1182 if (drawRect == NULL)
1184 BOOL isVisible;
1185 RECT itemRect;
1186 RECT selectedRect;
1189 * Get the rectangle for the item.
1191 isVisible = TAB_InternalGetItemRect
1193 hwnd,
1194 infoPtr,
1195 iItem,
1196 &itemRect,
1197 &selectedRect
1199 if (!isVisible)
1200 return;
1203 * Make sure drawRect points to something valid; simplifies code.
1205 drawRect = &localRect;
1208 * This logic copied from the part of TAB_DrawItem which draws
1209 * the tab background. It's important to keep it in sync. I
1210 * would have liked to avoid code duplication, but couldn't figure
1211 * out how without making spaghetti of TAB_DrawItem.
1213 if (lStyle & TCS_BUTTONS)
1215 *drawRect = itemRect;
1216 if (iItem == infoPtr->iSelected)
1218 drawRect->right--;
1219 drawRect->bottom--;
1222 else
1224 if (iItem == infoPtr->iSelected)
1225 *drawRect = selectedRect;
1226 else
1227 *drawRect = itemRect;
1228 drawRect->right--;
1229 drawRect->bottom--;
1234 * Text pen
1236 holdPen = SelectObject(hdc, htextPen);
1238 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1239 SetTextColor
1241 hdc,
1242 GetSysColor
1244 (iItem == infoPtr->iHotTracked) ? COLOR_HIGHLIGHT : COLOR_BTNTEXT
1249 * Deflate the rectangle to acount for the padding
1251 InflateRect(drawRect, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING);
1254 * if owner draw, tell the owner to draw
1256 if ( (lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd) )
1258 DRAWITEMSTRUCT dis;
1259 UINT id;
1262 * get the control id
1264 id = GetWindowLongA( hwnd, GWL_ID );
1267 * put together the DRAWITEMSTRUCT
1269 dis.CtlType = ODT_TAB;
1270 dis.CtlID = id;
1271 dis.itemID = iItem;
1272 dis.itemAction = ODA_DRAWENTIRE;
1273 if ( iItem == infoPtr->iSelected )
1274 dis.itemState = ODS_SELECTED;
1275 else
1276 dis.itemState = 0;
1277 dis.hwndItem = hwnd; /* */
1278 dis.hDC = hdc;
1279 dis.rcItem = *drawRect; /* */
1280 dis.itemData = infoPtr->items[iItem].lParam;
1283 * send the draw message
1285 SendMessageA( GetParent(hwnd), WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis );
1287 else
1289 UINT uHorizAlign;
1292 * If not owner draw, then do the drawing ourselves.
1294 * Draw the icon.
1296 if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE))
1298 INT cx;
1299 INT cy;
1301 ImageList_Draw
1303 infoPtr->himl,
1304 infoPtr->items[iItem].iImage,
1305 hdc,
1306 drawRect->left,
1307 drawRect->top + 1,
1308 ILD_NORMAL
1310 ImageList_GetIconSize(infoPtr->himl, &cx, &cy);
1311 drawRect->left += (cx + HORIZONTAL_ITEM_PADDING);
1315 * Draw the text;
1317 if (lStyle & TCS_RIGHTJUSTIFY)
1318 uHorizAlign = DT_CENTER;
1319 else
1320 uHorizAlign = DT_LEFT;
1322 DrawTextA
1324 hdc,
1325 infoPtr->items[iItem].pszText,
1326 lstrlenA(infoPtr->items[iItem].pszText),
1327 drawRect,
1328 uHorizAlign | DT_SINGLELINE | DT_VCENTER
1333 * Cleanup
1335 SetBkMode(hdc, oldBkMode);
1336 SelectObject(hdc, holdPen);
1339 /******************************************************************************
1340 * TAB_DrawItem
1342 * This method is used to draw a single tab into the tab control.
1344 static void TAB_DrawItem(
1345 HWND hwnd,
1346 HDC hdc,
1347 INT iItem)
1349 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1350 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1351 RECT itemRect;
1352 RECT selectedRect;
1353 BOOL isVisible;
1354 RECT r;
1357 * Get the rectangle for the item.
1359 isVisible = TAB_InternalGetItemRect(hwnd,
1360 infoPtr,
1361 iItem,
1362 &itemRect,
1363 &selectedRect);
1365 if (isVisible)
1367 HBRUSH hbr = CreateSolidBrush (GetSysColor(COLOR_BTNFACE));
1368 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
1369 HPEN hbPen = GetSysColorPen (COLOR_BTNSHADOW);
1370 HPEN hfocusPen = CreatePen(PS_DOT, 1, GetSysColor(COLOR_BTNTEXT));
1371 HPEN holdPen;
1372 INT oldBkMode;
1373 BOOL deleteBrush = TRUE;
1375 if (lStyle & TCS_BUTTONS)
1378 * Get item rectangle.
1380 r = itemRect;
1382 holdPen = SelectObject (hdc, hwPen);
1384 if (iItem == infoPtr->iSelected)
1387 * Background color.
1389 if (!((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet))
1391 COLORREF bk = GetSysColor(COLOR_3DHILIGHT);
1392 DeleteObject(hbr);
1393 hbr = GetSysColorBrush(COLOR_SCROLLBAR);
1394 SetTextColor(hdc, GetSysColor(COLOR_3DFACE));
1395 SetBkColor(hdc, bk);
1397 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
1398 * we better use 0x55aa bitmap brush to make scrollbar's background
1399 * look different from the window background.
1401 if (bk == GetSysColor(COLOR_WINDOW))
1402 hbr = CACHE_GetPattern55AABrush();
1404 deleteBrush = FALSE;
1408 * Erase the background.
1410 FillRect(hdc, &r, hbr);
1413 * Draw the tab now.
1414 * The rectangles calculated exclude the right and bottom
1415 * borders of the rectangle. To simply the following code, those
1416 * borders are shaved-off beforehand.
1418 r.right--;
1419 r.bottom--;
1421 /* highlight */
1422 MoveToEx (hdc, r.left, r.bottom, NULL);
1423 LineTo (hdc, r.right, r.bottom);
1424 LineTo (hdc, r.right, r.top);
1426 /* shadow */
1427 SelectObject(hdc, hbPen);
1428 LineTo (hdc, r.left, r.top);
1429 LineTo (hdc, r.left, r.bottom);
1431 else
1434 * Erase the background.
1436 FillRect(hdc, &r, hbr);
1438 /* highlight */
1439 MoveToEx (hdc, r.left, r.bottom, NULL);
1440 LineTo (hdc, r.left, r.top);
1441 LineTo (hdc, r.right, r.top);
1443 /* shadow */
1444 SelectObject(hdc, hbPen);
1445 LineTo (hdc, r.right, r.bottom);
1446 LineTo (hdc, r.left, r.bottom);
1449 else
1452 * Background color.
1454 DeleteObject(hbr);
1455 hbr = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1458 * We draw a rectangle of different sizes depending on the selection
1459 * state.
1461 if (iItem == infoPtr->iSelected)
1462 r = selectedRect;
1463 else
1464 r = itemRect;
1467 * Erase the background.
1468 * This is necessary when drawing the selected item since it is larger
1469 * than the others, it might overlap with stuff already drawn by the
1470 * other tabs
1472 FillRect(hdc, &r, hbr);
1475 * Draw the tab now.
1476 * The rectangles calculated exclude the right and bottom
1477 * borders of the rectangle. To simply the following code, those
1478 * borders are shaved-off beforehand.
1480 r.right--;
1481 r.bottom--;
1483 holdPen = SelectObject (hdc, hwPen);
1485 if (lStyle & TCS_BOTTOM)
1487 /* highlight */
1488 MoveToEx (hdc, r.left, r.top, NULL);
1489 LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE);
1490 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom);
1492 /* shadow */
1493 SelectObject(hdc, hbPen);
1494 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom);
1495 LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE);
1496 LineTo (hdc, r.right, r.top);
1498 else
1500 /* highlight */
1501 MoveToEx (hdc, r.left, r.bottom, NULL);
1502 LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE);
1503 LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top);
1504 LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top);
1506 /* shadow */
1507 SelectObject(hdc, hbPen);
1508 LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE);
1509 LineTo (hdc, r.right, r.bottom);
1513 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1515 /* This modifies r to be the text rectangle. */
1516 TAB_DrawItemInterior(hwnd, hdc, iItem, &r);
1519 * Draw the focus rectangle
1521 if (((lStyle & TCS_FOCUSNEVER) == 0) &&
1522 (GetFocus() == hwnd) &&
1523 (iItem == infoPtr->uFocus) )
1525 InflateRect(&r, FOCUS_RECT_HOFFSET, FOCUS_RECT_VOFFSET);
1527 SelectObject(hdc, hfocusPen);
1529 MoveToEx (hdc, r.left, r.top, NULL);
1530 LineTo (hdc, r.right-1, r.top);
1531 LineTo (hdc, r.right-1, r.bottom -1);
1532 LineTo (hdc, r.left, r.bottom -1);
1533 LineTo (hdc, r.left, r.top);
1537 * Cleanup
1539 SetBkMode(hdc, oldBkMode);
1540 SelectObject(hdc, holdPen);
1541 DeleteObject(hfocusPen);
1542 if (deleteBrush) DeleteObject(hbr);
1546 /******************************************************************************
1547 * TAB_DrawBorder
1549 * This method is used to draw the raised border around the tab control
1550 * "content" area.
1552 static void TAB_DrawBorder (HWND hwnd, HDC hdc)
1554 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1555 HPEN htmPen;
1556 HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT);
1557 HPEN hbPen = GetSysColorPen (COLOR_3DDKSHADOW);
1558 HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW);
1559 RECT rect;
1561 GetClientRect (hwnd, &rect);
1564 * Adjust for the style
1566 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1568 rect.bottom -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
1570 else
1572 rect.top += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2;
1576 * Shave-off the right and bottom margins (exluded in the
1577 * rect)
1579 rect.right--;
1580 rect.bottom--;
1582 /* highlight */
1583 htmPen = SelectObject (hdc, hwPen);
1585 MoveToEx (hdc, rect.left, rect.bottom, NULL);
1586 LineTo (hdc, rect.left, rect.top);
1587 LineTo (hdc, rect.right, rect.top);
1589 /* Dark Shadow */
1590 SelectObject (hdc, hbPen);
1591 LineTo (hdc, rect.right, rect.bottom );
1592 LineTo (hdc, rect.left, rect.bottom);
1594 /* shade */
1595 SelectObject (hdc, hShade );
1596 MoveToEx (hdc, rect.right-1, rect.top, NULL);
1597 LineTo (hdc, rect.right-1, rect.bottom-1);
1598 LineTo (hdc, rect.left, rect.bottom-1);
1600 SelectObject(hdc, htmPen);
1603 /******************************************************************************
1604 * TAB_Refresh
1606 * This method repaints the tab control..
1608 static void TAB_Refresh (HWND hwnd, HDC hdc)
1610 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1611 HFONT hOldFont;
1612 INT i;
1614 if (!infoPtr->DoRedraw)
1615 return;
1617 hOldFont = SelectObject (hdc, infoPtr->hFont);
1619 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS)
1621 for (i = 0; i < infoPtr->uNumItem; i++)
1623 TAB_DrawItem (hwnd, hdc, i);
1626 else
1629 * Draw all the non selected item first.
1631 for (i = 0; i < infoPtr->uNumItem; i++)
1633 if (i != infoPtr->iSelected)
1634 TAB_DrawItem (hwnd, hdc, i);
1638 * Now, draw the border, draw it before the selected item
1639 * since the selected item overwrites part of the border.
1641 TAB_DrawBorder (hwnd, hdc);
1644 * Then, draw the selected item
1646 TAB_DrawItem (hwnd, hdc, infoPtr->iSelected);
1649 * If we haven't set the current focus yet, set it now.
1650 * Only happens when we first paint the tab controls.
1652 if (infoPtr->uFocus == -1)
1653 TAB_SetCurFocus(hwnd, infoPtr->iSelected);
1656 SelectObject (hdc, hOldFont);
1659 static LRESULT
1660 TAB_SetRedraw (HWND hwnd, WPARAM wParam)
1662 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1664 infoPtr->DoRedraw=(BOOL) wParam;
1665 return 0;
1668 static LRESULT TAB_EraseBackground(
1669 HWND hwnd,
1670 HDC givenDC)
1672 HDC hdc;
1673 RECT clientRect;
1675 HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1677 hdc = givenDC ? givenDC : GetDC(hwnd);
1679 GetClientRect(hwnd, &clientRect);
1681 FillRect(hdc, &clientRect, brush);
1683 if (givenDC==0)
1684 ReleaseDC(hwnd, hdc);
1686 DeleteObject(brush);
1688 return 0;
1691 /******************************************************************************
1692 * TAB_EnsureSelectionVisible
1694 * This method will make sure that the current selection is completely
1695 * visible by scrolling until it is.
1697 static void TAB_EnsureSelectionVisible(
1698 HWND hwnd,
1699 TAB_INFO* infoPtr)
1701 INT iSelected = infoPtr->iSelected;
1703 INT iOrigLeftmostVisible = infoPtr->leftmostVisible;
1706 * set the items row to the bottommost row or topmost row depending on
1707 * style
1710 if (infoPtr->uNumRows > 0)
1712 INT newselected=infoPtr->items[iSelected].rect.top;
1713 INT iTargetRow;
1714 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1716 if (lStyle & TCS_BOTTOM)
1717 iTargetRow = 0;
1718 else
1719 iTargetRow = infoPtr->uNumRows;
1721 if (newselected != iTargetRow)
1723 INT i;
1724 for (i=0; i < infoPtr->uNumItem; i++)
1725 if (infoPtr->items[i].rect.top == newselected )
1726 infoPtr->items[i].rect.top = iTargetRow;
1727 else if (lStyle&TCS_BOTTOM)
1729 if (infoPtr->items[i].rect.top < newselected)
1730 infoPtr->items[i].rect.top+=1;
1732 else
1734 if (infoPtr->items[i].rect.top > newselected)
1735 infoPtr->items[i].rect.top-=1;
1738 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1743 * Do the trivial cases first.
1745 if ( (!infoPtr->needsScrolling) ||
1746 (infoPtr->hwndUpDown==0) )
1747 return;
1749 if (infoPtr->leftmostVisible >= iSelected)
1751 infoPtr->leftmostVisible = iSelected;
1753 else
1755 RECT r;
1756 INT width, i;
1758 * Calculate the part of the client area that is visible.
1760 GetClientRect(hwnd, &r);
1761 width = r.right;
1763 GetClientRect(infoPtr->hwndUpDown, &r);
1764 width -= r.right;
1766 if ((infoPtr->items[iSelected].rect.right -
1767 infoPtr->items[iSelected].rect.left) >= width )
1769 /* Special case: width of selected item is greater than visible
1770 * part of control.
1772 infoPtr->leftmostVisible = iSelected;
1774 else
1776 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++)
1778 if ((infoPtr->items[iSelected].rect.right -
1779 infoPtr->items[i].rect.left) < width)
1780 break;
1782 infoPtr->leftmostVisible = i;
1786 if (infoPtr->leftmostVisible != iOrigLeftmostVisible)
1787 TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL);
1789 SendMessageA(infoPtr->hwndUpDown, UDM_SETPOS, 0,
1790 MAKELONG(infoPtr->leftmostVisible, 0));
1793 /******************************************************************************
1794 * TAB_InvalidateTabArea
1796 * This method will invalidate the portion of the control that contains the
1797 * tabs. It is called when the state of the control changes and needs
1798 * to be redisplayed
1800 static void TAB_InvalidateTabArea(
1801 HWND hwnd,
1802 TAB_INFO* infoPtr)
1804 RECT clientRect;
1806 GetClientRect(hwnd, &clientRect);
1808 if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM)
1810 clientRect.top = clientRect.bottom - (infoPtr->tabHeight *
1811 (infoPtr->uNumRows + 1) + 3);
1813 else
1815 clientRect.bottom = clientRect.top + (infoPtr->tabHeight *
1816 (infoPtr->uNumRows + 1) + 1);
1819 InvalidateRect(hwnd, &clientRect, TRUE);
1822 static LRESULT
1823 TAB_Paint (HWND hwnd, WPARAM wParam)
1825 HDC hdc;
1826 PAINTSTRUCT ps;
1828 hdc = wParam== 0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
1829 TAB_Refresh (hwnd, hdc);
1831 if(!wParam)
1832 EndPaint (hwnd, &ps);
1834 return 0;
1837 static LRESULT
1838 TAB_InsertItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1840 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1841 TCITEMA *pti;
1842 INT iItem, len;
1843 RECT rect;
1845 GetClientRect (hwnd, &rect);
1846 TRACE("Rect: %x T %i, L %i, B %i, R %i\n", hwnd,
1847 rect.top, rect.left, rect.bottom, rect.right);
1849 pti = (TCITEMA *)lParam;
1850 iItem = (INT)wParam;
1852 if (iItem < 0) return -1;
1853 if (iItem > infoPtr->uNumItem)
1854 iItem = infoPtr->uNumItem;
1856 if (infoPtr->uNumItem == 0) {
1857 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM));
1858 infoPtr->uNumItem++;
1859 infoPtr->iSelected = 0;
1861 else {
1862 TAB_ITEM *oldItems = infoPtr->items;
1864 infoPtr->uNumItem++;
1865 infoPtr->items = COMCTL32_Alloc (sizeof (TAB_ITEM) * infoPtr->uNumItem);
1867 /* pre insert copy */
1868 if (iItem > 0) {
1869 memcpy (&infoPtr->items[0], &oldItems[0],
1870 iItem * sizeof(TAB_ITEM));
1873 /* post insert copy */
1874 if (iItem < infoPtr->uNumItem - 1) {
1875 memcpy (&infoPtr->items[iItem+1], &oldItems[iItem],
1876 (infoPtr->uNumItem - iItem - 1) * sizeof(TAB_ITEM));
1880 if (iItem <= infoPtr->iSelected)
1881 infoPtr->iSelected++;
1883 COMCTL32_Free (oldItems);
1886 infoPtr->items[iItem].mask = pti->mask;
1887 if (pti->mask & TCIF_TEXT) {
1888 len = lstrlenA (pti->pszText);
1889 infoPtr->items[iItem].pszText = COMCTL32_Alloc (len+1);
1890 lstrcpyA (infoPtr->items[iItem].pszText, pti->pszText);
1891 infoPtr->items[iItem].cchTextMax = pti->cchTextMax;
1894 if (pti->mask & TCIF_IMAGE)
1895 infoPtr->items[iItem].iImage = pti->iImage;
1897 if (pti->mask & TCIF_PARAM)
1898 infoPtr->items[iItem].lParam = pti->lParam;
1900 TAB_SetItemBounds(hwnd);
1901 TAB_InvalidateTabArea(hwnd, infoPtr);
1903 TRACE("[%04x]: added item %d '%s'\n",
1904 hwnd, iItem, infoPtr->items[iItem].pszText);
1906 return iItem;
1909 static LRESULT
1910 TAB_SetItemSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
1912 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1913 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1914 LONG lResult = 0;
1916 if ((lStyle & TCS_FIXEDWIDTH) || (lStyle & TCS_OWNERDRAWFIXED))
1918 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight);
1919 infoPtr->tabWidth = (INT)LOWORD(lParam);
1920 infoPtr->tabHeight = (INT)HIWORD(lParam);
1922 infoPtr->fSizeSet = TRUE;
1924 return lResult;
1927 static LRESULT
1928 TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1930 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1931 TCITEMA *tabItem;
1932 TAB_ITEM *wineItem;
1933 INT iItem,len;
1935 iItem=(INT) wParam;
1936 tabItem=(LPTCITEMA ) lParam;
1937 TRACE("%d %p\n",iItem, tabItem);
1938 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
1940 wineItem=& infoPtr->items[iItem];
1942 if (tabItem->mask & TCIF_IMAGE)
1943 wineItem->iImage=tabItem->iImage;
1945 if (tabItem->mask & TCIF_PARAM)
1946 wineItem->lParam=tabItem->lParam;
1948 if (tabItem->mask & TCIF_RTLREADING)
1949 FIXME("TCIF_RTLREADING\n");
1951 if (tabItem->mask & TCIF_STATE)
1952 wineItem->dwState=tabItem->dwState;
1954 if (tabItem->mask & TCIF_TEXT) {
1955 len=lstrlenA (tabItem->pszText);
1956 if (len>wineItem->cchTextMax)
1957 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
1958 lstrcpyA (wineItem->pszText, tabItem->pszText);
1962 * Update and repaint tabs.
1964 TAB_SetItemBounds(hwnd);
1965 TAB_InvalidateTabArea(hwnd,infoPtr);
1967 return TRUE;
1970 static LRESULT
1971 TAB_GetItemCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1973 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1975 return infoPtr->uNumItem;
1979 static LRESULT
1980 TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1982 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
1983 TCITEMA *tabItem;
1984 TAB_ITEM *wineItem;
1985 INT iItem;
1987 iItem=(INT) wParam;
1988 tabItem=(LPTCITEMA) lParam;
1989 TRACE("\n");
1990 if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE;
1992 wineItem=& infoPtr->items[iItem];
1994 if (tabItem->mask & TCIF_IMAGE)
1995 tabItem->iImage=wineItem->iImage;
1997 if (tabItem->mask & TCIF_PARAM)
1998 tabItem->lParam=wineItem->lParam;
2000 if (tabItem->mask & TCIF_RTLREADING)
2001 FIXME("TCIF_RTLREADING\n");
2003 if (tabItem->mask & TCIF_STATE)
2004 tabItem->dwState=wineItem->dwState;
2006 if (tabItem->mask & TCIF_TEXT)
2007 lstrcpynA (tabItem->pszText, wineItem->pszText, tabItem->cchTextMax);
2009 return TRUE;
2012 static LRESULT
2013 TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2015 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2016 INT iItem = (INT) wParam;
2017 BOOL bResult = FALSE;
2019 if ((iItem >= 0) && (iItem < infoPtr->uNumItem))
2021 TAB_ITEM *oldItems = infoPtr->items;
2023 infoPtr->uNumItem--;
2024 infoPtr->items = COMCTL32_Alloc(sizeof (TAB_ITEM) * infoPtr->uNumItem);
2026 if (iItem > 0)
2027 memcpy(&infoPtr->items[0], &oldItems[0], iItem * sizeof(TAB_ITEM));
2029 if (iItem < infoPtr->uNumItem)
2030 memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1],
2031 (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM));
2033 COMCTL32_Free (oldItems);
2036 * Readjust the selected index.
2038 if ((iItem == infoPtr->iSelected) && (iItem > 0))
2039 infoPtr->iSelected--;
2041 if (iItem < infoPtr->iSelected)
2042 infoPtr->iSelected--;
2044 if (infoPtr->uNumItem == 0)
2045 infoPtr->iSelected = -1;
2048 * Reposition and repaint tabs.
2050 TAB_SetItemBounds(hwnd);
2051 TAB_InvalidateTabArea(hwnd,infoPtr);
2053 bResult = TRUE;
2056 return bResult;
2059 static LRESULT
2060 TAB_DeleteAllItems (HWND hwnd, WPARAM wParam, LPARAM lParam)
2062 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2064 COMCTL32_Free (infoPtr->items);
2065 infoPtr->uNumItem = 0;
2066 infoPtr->iSelected = -1;
2067 if (infoPtr->iHotTracked >= 0)
2068 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2069 infoPtr->iHotTracked = -1;
2071 TAB_SetItemBounds(hwnd);
2072 TAB_InvalidateTabArea(hwnd,infoPtr);
2073 return TRUE;
2077 static LRESULT
2078 TAB_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2080 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2082 TRACE("\n");
2083 return (LRESULT)infoPtr->hFont;
2086 static LRESULT
2087 TAB_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2090 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2092 TRACE("%x %lx\n",wParam, lParam);
2094 infoPtr->hFont = (HFONT)wParam;
2096 TAB_SetItemBounds(hwnd);
2098 TAB_InvalidateTabArea(hwnd, infoPtr);
2100 return 0;
2104 static LRESULT
2105 TAB_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2107 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2109 TRACE("\n");
2110 return (LRESULT)infoPtr->himl;
2113 static LRESULT
2114 TAB_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
2116 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2117 HIMAGELIST himlPrev;
2119 TRACE("\n");
2120 himlPrev = infoPtr->himl;
2121 infoPtr->himl= (HIMAGELIST)lParam;
2122 return (LRESULT)himlPrev;
2126 static LRESULT
2127 TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2130 /* I'm not really sure what the following code was meant to do.
2131 This is what it is doing:
2132 When WM_SIZE is sent with SIZE_RESTORED, the control
2133 gets positioned in the top left corner.
2135 RECT parent_rect;
2136 HWND parent;
2137 UINT uPosFlags,cx,cy;
2139 uPosFlags=0;
2140 if (!wParam) {
2141 parent = GetParent (hwnd);
2142 GetClientRect(parent, &parent_rect);
2143 cx=LOWORD (lParam);
2144 cy=HIWORD (lParam);
2145 if (GetWindowLongA(hwnd, GWL_STYLE) & CCS_NORESIZE)
2146 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE);
2148 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top,
2149 cx, cy, uPosFlags | SWP_NOZORDER);
2150 } else {
2151 FIXME (tab,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2152 } */
2155 * Recompute the size/position of the tabs.
2157 TAB_SetItemBounds (hwnd);
2160 * Force a repaint of the control.
2162 InvalidateRect(hwnd, NULL, TRUE);
2164 return 0;
2168 static LRESULT
2169 TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2171 TAB_INFO *infoPtr;
2172 TEXTMETRICA fontMetrics;
2173 HDC hdc;
2174 HFONT hOldFont;
2175 DWORD dwStyle;
2177 infoPtr = (TAB_INFO *)COMCTL32_Alloc (sizeof(TAB_INFO));
2179 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
2181 infoPtr->uNumItem = 0;
2182 infoPtr->uNumRows = 0;
2183 infoPtr->hFont = 0;
2184 infoPtr->items = 0;
2185 infoPtr->hcurArrow = LoadCursorA (0, IDC_ARROWA);
2186 infoPtr->iSelected = -1;
2187 infoPtr->iHotTracked = -1;
2188 infoPtr->uFocus = -1;
2189 infoPtr->hwndToolTip = 0;
2190 infoPtr->DoRedraw = TRUE;
2191 infoPtr->needsScrolling = FALSE;
2192 infoPtr->hwndUpDown = 0;
2193 infoPtr->leftmostVisible = 0;
2194 infoPtr->fSizeSet = FALSE;
2196 TRACE("Created tab control, hwnd [%04x]\n", hwnd);
2198 /* The tab control always has the WS_CLIPSIBLINGS style. Even
2199 if you don't specify in CreateWindow. This is necesary in
2200 order for paint to work correctly. This follows windows behaviour. */
2201 dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
2202 SetWindowLongA(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS);
2204 if (dwStyle & TCS_TOOLTIPS) {
2205 /* Create tooltip control */
2206 infoPtr->hwndToolTip =
2207 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2208 CW_USEDEFAULT, CW_USEDEFAULT,
2209 CW_USEDEFAULT, CW_USEDEFAULT,
2210 hwnd, 0, 0, 0);
2212 /* Send NM_TOOLTIPSCREATED notification */
2213 if (infoPtr->hwndToolTip) {
2214 NMTOOLTIPSCREATED nmttc;
2216 nmttc.hdr.hwndFrom = hwnd;
2217 nmttc.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
2218 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2219 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2221 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2222 (WPARAM)GetWindowLongA(hwnd, GWL_ID), (LPARAM)&nmttc);
2227 * We need to get text information so we need a DC and we need to select
2228 * a font.
2230 hdc = GetDC(hwnd);
2231 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
2234 * Use the system font to determine the initial height of a tab.
2236 GetTextMetricsA(hdc, &fontMetrics);
2239 * Make sure there is enough space for the letters + growing the
2240 * selected item + extra space for the selected item.
2242 infoPtr->tabHeight = fontMetrics.tmHeight + 2*VERTICAL_ITEM_PADDING +
2243 SELECTED_TAB_OFFSET;
2246 * Initialize the width of a tab.
2248 infoPtr->tabWidth = DEFAULT_TAB_WIDTH;
2250 SelectObject (hdc, hOldFont);
2251 ReleaseDC(hwnd, hdc);
2253 return 0;
2256 static LRESULT
2257 TAB_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
2259 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd);
2260 INT iItem;
2262 if (!infoPtr)
2263 return 0;
2265 if (infoPtr->items) {
2266 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) {
2267 if (infoPtr->items[iItem].pszText)
2268 COMCTL32_Free (infoPtr->items[iItem].pszText);
2270 COMCTL32_Free (infoPtr->items);
2273 if (infoPtr->hwndToolTip)
2274 DestroyWindow (infoPtr->hwndToolTip);
2276 if (infoPtr->hwndUpDown)
2277 DestroyWindow(infoPtr->hwndUpDown);
2279 if (infoPtr->iHotTracked >= 0)
2280 KillTimer(hwnd, TAB_HOTTRACK_TIMER);
2282 COMCTL32_Free (infoPtr);
2283 SetWindowLongA(hwnd, 0, 0);
2284 return 0;
2287 static LRESULT WINAPI
2288 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2291 TRACE("hwnd=%x msg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
2292 if (!TAB_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
2293 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
2295 switch (uMsg)
2297 case TCM_GETIMAGELIST:
2298 return TAB_GetImageList (hwnd, wParam, lParam);
2300 case TCM_SETIMAGELIST:
2301 return TAB_SetImageList (hwnd, wParam, lParam);
2303 case TCM_GETITEMCOUNT:
2304 return TAB_GetItemCount (hwnd, wParam, lParam);
2306 case TCM_GETITEMA:
2307 return TAB_GetItemA (hwnd, wParam, lParam);
2309 case TCM_GETITEMW:
2310 FIXME("Unimplemented msg TCM_GETITEMW\n");
2311 return 0;
2313 case TCM_SETITEMA:
2314 return TAB_SetItemA (hwnd, wParam, lParam);
2316 case TCM_SETITEMW:
2317 FIXME("Unimplemented msg TCM_SETITEMW\n");
2318 return 0;
2320 case TCM_DELETEITEM:
2321 return TAB_DeleteItem (hwnd, wParam, lParam);
2323 case TCM_DELETEALLITEMS:
2324 return TAB_DeleteAllItems (hwnd, wParam, lParam);
2326 case TCM_GETITEMRECT:
2327 return TAB_GetItemRect (hwnd, wParam, lParam);
2329 case TCM_GETCURSEL:
2330 return TAB_GetCurSel (hwnd);
2332 case TCM_HITTEST:
2333 return TAB_HitTest (hwnd, wParam, lParam);
2335 case TCM_SETCURSEL:
2336 return TAB_SetCurSel (hwnd, wParam);
2338 case TCM_INSERTITEMA:
2339 return TAB_InsertItem (hwnd, wParam, lParam);
2341 case TCM_INSERTITEMW:
2342 FIXME("Unimplemented msg TCM_INSERTITEMW\n");
2343 return 0;
2345 case TCM_SETITEMEXTRA:
2346 FIXME("Unimplemented msg TCM_SETITEMEXTRA\n");
2347 return 0;
2349 case TCM_ADJUSTRECT:
2350 return TAB_AdjustRect (hwnd, (BOOL)wParam, (LPRECT)lParam);
2352 case TCM_SETITEMSIZE:
2353 return TAB_SetItemSize (hwnd, wParam, lParam);
2355 case TCM_REMOVEIMAGE:
2356 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n");
2357 return 0;
2359 case TCM_SETPADDING:
2360 FIXME("Unimplemented msg TCM_SETPADDING\n");
2361 return 0;
2363 case TCM_GETROWCOUNT:
2364 FIXME("Unimplemented msg TCM_GETROWCOUNT\n");
2365 return 0;
2367 case TCM_GETUNICODEFORMAT:
2368 FIXME("Unimplemented msg TCM_GETUNICODEFORMAT\n");
2369 return 0;
2371 case TCM_SETUNICODEFORMAT:
2372 FIXME("Unimplemented msg TCM_SETUNICODEFORMAT\n");
2373 return 0;
2375 case TCM_HIGHLIGHTITEM:
2376 FIXME("Unimplemented msg TCM_HIGHLIGHTITEM\n");
2377 return 0;
2379 case TCM_GETTOOLTIPS:
2380 return TAB_GetToolTips (hwnd, wParam, lParam);
2382 case TCM_SETTOOLTIPS:
2383 return TAB_SetToolTips (hwnd, wParam, lParam);
2385 case TCM_GETCURFOCUS:
2386 return TAB_GetCurFocus (hwnd);
2388 case TCM_SETCURFOCUS:
2389 return TAB_SetCurFocus (hwnd, wParam);
2391 case TCM_SETMINTABWIDTH:
2392 FIXME("Unimplemented msg TCM_SETMINTABWIDTH\n");
2393 return 0;
2395 case TCM_DESELECTALL:
2396 FIXME("Unimplemented msg TCM_DESELECTALL\n");
2397 return 0;
2399 case TCM_GETEXTENDEDSTYLE:
2400 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n");
2401 return 0;
2403 case TCM_SETEXTENDEDSTYLE:
2404 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n");
2405 return 0;
2407 case WM_GETFONT:
2408 return TAB_GetFont (hwnd, wParam, lParam);
2410 case WM_SETFONT:
2411 return TAB_SetFont (hwnd, wParam, lParam);
2413 case WM_CREATE:
2414 return TAB_Create (hwnd, wParam, lParam);
2416 case WM_NCDESTROY:
2417 return TAB_Destroy (hwnd, wParam, lParam);
2419 case WM_GETDLGCODE:
2420 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2422 case WM_LBUTTONDOWN:
2423 return TAB_LButtonDown (hwnd, wParam, lParam);
2425 case WM_LBUTTONUP:
2426 return TAB_LButtonUp (hwnd, wParam, lParam);
2428 case WM_RBUTTONDOWN:
2429 return TAB_RButtonDown (hwnd, wParam, lParam);
2431 case WM_MOUSEMOVE:
2432 return TAB_MouseMove (hwnd, wParam, lParam);
2434 case WM_ERASEBKGND:
2435 return TAB_EraseBackground (hwnd, (HDC)wParam);
2437 case WM_PAINT:
2438 return TAB_Paint (hwnd, wParam);
2440 case WM_SIZE:
2441 return TAB_Size (hwnd, wParam, lParam);
2443 case WM_SETREDRAW:
2444 return TAB_SetRedraw (hwnd, wParam);
2446 case WM_HSCROLL:
2447 return TAB_OnHScroll(hwnd, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam);
2449 case WM_STYLECHANGED:
2450 TAB_SetItemBounds (hwnd);
2451 InvalidateRect(hwnd, NULL, TRUE);
2452 return 0;
2454 case WM_KILLFOCUS:
2455 case WM_SETFOCUS:
2456 return TAB_FocusChanging(hwnd, uMsg, wParam, lParam);
2458 case WM_KEYUP:
2459 return TAB_KeyUp(hwnd, wParam);
2461 default:
2462 if (uMsg >= WM_USER)
2463 WARN("unknown msg %04x wp=%08x lp=%08lx\n",
2464 uMsg, wParam, lParam);
2465 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
2468 return 0;
2472 VOID
2473 TAB_Register (void)
2475 WNDCLASSA wndClass;
2477 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
2478 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
2479 wndClass.lpfnWndProc = (WNDPROC)TAB_WindowProc;
2480 wndClass.cbClsExtra = 0;
2481 wndClass.cbWndExtra = sizeof(TAB_INFO *);
2482 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
2483 wndClass.hbrBackground = (HBRUSH)NULL;
2484 wndClass.lpszClassName = WC_TABCONTROLA;
2486 RegisterClassA (&wndClass);
2490 VOID
2491 TAB_Unregister (void)
2493 UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL);