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