1 /* 2 * Tab control 3 * 4 * Copyright 1998 Anders Carlsson 5 * Copyright 1999 Alex Priem <alexp@sci.kun.nl> 6 * Copyright 1999 Francis Beaudet 7 * Copyright 2003 Vitaliy Margolen 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2.1 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 22 * 23 * NOTES 24 * 25 * This code was audited for completeness against the documented features 26 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins. 27 * 28 * Unless otherwise noted, we believe this code to be complete, as per 29 * the specification mentioned above. 30 * If you discover missing features, or bugs, please note them below. 31 * 32 * TODO: 33 * 34 * Styles: 35 * TCS_MULTISELECT 36 * TCS_RIGHT 37 * TCS_RIGHTJUSTIFY 38 * TCS_SCROLLOPPOSITE 39 * TCS_SINGLELINE 40 * TCIF_RTLREADING 41 * 42 * Extended Styles: 43 * TCS_EX_FLATSEPARATORS 44 * TCS_EX_REGISTERDROP 45 * 46 * States: 47 * TCIS_BUTTONPRESSED 48 * 49 * Notifications: 50 * NM_RELEASEDCAPTURE 51 * TCN_FOCUSCHANGE 52 * TCN_GETOBJECT 53 * TCN_KEYDOWN 54 * 55 * Messages: 56 * TCM_REMOVEIMAGE 57 * TCM_DESELECTALL 58 * TCM_GETEXTENDEDSTYLE 59 * TCM_SETEXTENDEDSTYLE 60 * 61 * Macros: 62 * TabCtrl_AdjustRect 63 * 64 */ 65 66 #include <stdarg.h> 67 #include <string.h> 68 69 #include "windef.h" 70 #include "winbase.h" 71 #include "wingdi.h" 72 #include "winuser.h" 73 #include "winnls.h" 74 #include "commctrl.h" 75 #include "comctl32.h" 76 #include "uxtheme.h" 77 #include "tmschema.h" 78 #include "wine/debug.h" 79 #include <math.h> 80 81 WINE_DEFAULT_DEBUG_CHANNEL(tab); 82 83 typedef struct 84 { 85 DWORD dwState; 86 LPWSTR pszText; 87 INT iImage; 88 RECT rect; /* bounding rectangle of the item relative to the 89 * leftmost item (the leftmost item, 0, would have a 90 * "left" member of 0 in this rectangle) 91 * 92 * additionally the top member holds the row number 93 * and bottom is unused and should be 0 */ 94 BYTE extra[1]; /* Space for caller supplied info, variable size */ 95 } TAB_ITEM; 96 97 /* The size of a tab item depends on how much extra data is requested */ 98 #define TAB_ITEM_SIZE(infoPtr) (FIELD_OFFSET(TAB_ITEM, extra[(infoPtr)->cbInfo])) 99 100 typedef struct 101 { 102 HWND hwnd; /* Tab control window */ 103 HWND hwndNotify; /* notification window (parent) */ 104 UINT uNumItem; /* number of tab items */ 105 UINT uNumRows; /* number of tab rows */ 106 INT tabHeight; /* height of the tab row */ 107 INT tabWidth; /* width of tabs */ 108 INT tabMinWidth; /* minimum width of items */ 109 USHORT uHItemPadding; /* amount of horizontal padding, in pixels */ 110 USHORT uVItemPadding; /* amount of vertical padding, in pixels */ 111 USHORT uHItemPadding_s; /* Set amount of horizontal padding, in pixels */ 112 USHORT uVItemPadding_s; /* Set amount of vertical padding, in pixels */ 113 HFONT hFont; /* handle to the current font */ 114 HCURSOR hcurArrow; /* handle to the current cursor */ 115 HIMAGELIST himl; /* handle to an image list (may be 0) */ 116 HWND hwndToolTip; /* handle to tab's tooltip */ 117 INT leftmostVisible; /* Used for scrolling, this member contains 118 * the index of the first visible item */ 119 INT iSelected; /* the currently selected item */ 120 INT iHotTracked; /* the highlighted item under the mouse */ 121 INT uFocus; /* item which has the focus */ 122 TAB_ITEM* items; /* pointer to an array of TAB_ITEM's */ 123 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/ 124 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than 125 * the size of the control */ 126 BOOL fHeightSet; /* was the height of the tabs explicitly set? */ 127 BOOL bUnicode; /* Unicode control? */ 128 HWND hwndUpDown; /* Updown control used for scrolling */ 129 INT cbInfo; /* Number of bytes of caller supplied info per tab */ 130 } TAB_INFO; 131 132 /****************************************************************************** 133 * Positioning constants 134 */ 135 #define SELECTED_TAB_OFFSET 2 136 #define ROUND_CORNER_SIZE 2 137 #define DISPLAY_AREA_PADDINGX 2 138 #define DISPLAY_AREA_PADDINGY 2 139 #define CONTROL_BORDER_SIZEX 2 140 #define CONTROL_BORDER_SIZEY 2 141 #define BUTTON_SPACINGX 3 142 #define BUTTON_SPACINGY 3 143 #define FLAT_BTN_SPACINGX 8 144 #define DEFAULT_MIN_TAB_WIDTH 54 145 #define DEFAULT_TAB_WIDTH_FIXED 96 146 #define DEFAULT_PADDING_X 6 147 #define EXTRA_ICON_PADDING 3 148 149 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongPtrW(hwnd,0)) 150 /* Since items are variable sized, cannot directly access them */ 151 #define TAB_GetItem(info,i) \ 152 ((TAB_ITEM*)((LPBYTE)info->items + (i) * TAB_ITEM_SIZE(info))) 153 154 #define GET_DEFAULT_MIN_TAB_WIDTH(infoPtr) (DEFAULT_MIN_TAB_WIDTH - (DEFAULT_PADDING_X - (infoPtr)->uHItemPadding) * 2) 155 156 /****************************************************************************** 157 * Hot-tracking timer constants 158 */ 159 #define TAB_HOTTRACK_TIMER 1 160 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */ 161 162 static const WCHAR themeClass[] = { 'T','a','b',0 }; 163 164 /****************************************************************************** 165 * Prototypes 166 */ 167 static void TAB_InvalidateTabArea(const TAB_INFO *); 168 static void TAB_EnsureSelectionVisible(TAB_INFO *); 169 static void TAB_DrawItemInterior(const TAB_INFO *, HDC, INT, RECT*); 170 171 static BOOL 172 TAB_SendSimpleNotify (const TAB_INFO *infoPtr, UINT code) 173 { 174 NMHDR nmhdr; 175 176 nmhdr.hwndFrom = infoPtr->hwnd; 177 nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID); 178 nmhdr.code = code; 179 180 return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, 181 nmhdr.idFrom, (LPARAM) &nmhdr); 182 } 183 184 static void 185 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg, 186 WPARAM wParam, LPARAM lParam) 187 { 188 MSG msg; 189 190 msg.hwnd = hwndMsg; 191 msg.message = uMsg; 192 msg.wParam = wParam; 193 msg.lParam = lParam; 194 msg.time = GetMessageTime (); 195 msg.pt.x = (short)LOWORD(GetMessagePos ()); 196 msg.pt.y = (short)HIWORD(GetMessagePos ()); 197 198 SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg); 199 } 200 201 static void 202 TAB_DumpItemExternalT(const TCITEMW *pti, UINT iItem, BOOL isW) 203 { 204 if (TRACE_ON(tab)) { 205 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n", 206 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax); 207 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n", 208 iItem, pti->iImage, pti->lParam, isW ? debugstr_w(pti->pszText) : debugstr_a((LPSTR)pti->pszText)); 209 } 210 } 211 212 static void 213 TAB_DumpItemInternal(const TAB_INFO *infoPtr, UINT iItem) 214 { 215 if (TRACE_ON(tab)) { 216 TAB_ITEM *ti; 217 218 ti = TAB_GetItem(infoPtr, iItem); 219 TRACE("tab %d, dwState=0x%08x, pszText=%s, iImage=%d\n", 220 iItem, ti->dwState, debugstr_w(ti->pszText), ti->iImage); 221 TRACE("tab %d, rect.left=%d, rect.top(row)=%d\n", 222 iItem, ti->rect.left, ti->rect.top); 223 } 224 } 225 226 /* RETURNS 227 * the index of the selected tab, or -1 if no tab is selected. */ 228 static inline LRESULT TAB_GetCurSel (const TAB_INFO *infoPtr) 229 { 230 return infoPtr->iSelected; 231 } 232 233 /* RETURNS 234 * the index of the tab item that has the focus. */ 235 static inline LRESULT 236 TAB_GetCurFocus (const TAB_INFO *infoPtr) 237 { 238 return infoPtr->uFocus; 239 } 240 241 static inline LRESULT TAB_GetToolTips (const TAB_INFO *infoPtr) 242 { 243 if (infoPtr == NULL) return 0; 244 return (LRESULT)infoPtr->hwndToolTip; 245 } 246 247 static inline LRESULT TAB_SetCurSel (TAB_INFO *infoPtr, INT iItem) 248 { 249 INT prevItem = infoPtr->iSelected; 250 251 if (iItem < 0) 252 infoPtr->iSelected=-1; 253 else if (iItem >= infoPtr->uNumItem) 254 return -1; 255 else { 256 if (infoPtr->iSelected != iItem) { 257 infoPtr->iSelected=iItem; 258 infoPtr->uFocus=iItem; 259 TAB_EnsureSelectionVisible(infoPtr); 260 TAB_InvalidateTabArea(infoPtr); 261 } 262 } 263 return prevItem; 264 } 265 266 static LRESULT TAB_SetCurFocus (TAB_INFO *infoPtr, INT iItem) 267 { 268 if (iItem < 0) 269 infoPtr->uFocus = -1; 270 else if (iItem < infoPtr->uNumItem) { 271 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS) { 272 FIXME("Should set input focus\n"); 273 } else { 274 int oldFocus = infoPtr->uFocus; 275 if (infoPtr->iSelected != iItem || oldFocus == -1 ) { 276 infoPtr->uFocus = iItem; 277 if (oldFocus != -1) { 278 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING)) { 279 infoPtr->iSelected = iItem; 280 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE); 281 } 282 else 283 infoPtr->iSelected = iItem; 284 TAB_EnsureSelectionVisible(infoPtr); 285 TAB_InvalidateTabArea(infoPtr); 286 } 287 } 288 } 289 } 290 return 0; 291 } 292 293 static inline LRESULT 294 TAB_SetToolTips (TAB_INFO *infoPtr, HWND hwndToolTip) 295 { 296 if (infoPtr) 297 infoPtr->hwndToolTip = hwndToolTip; 298 return 0; 299 } 300 301 static inline LRESULT 302 TAB_SetPadding (TAB_INFO *infoPtr, LPARAM lParam) 303 { 304 if (infoPtr) 305 { 306 infoPtr->uHItemPadding_s=LOWORD(lParam); 307 infoPtr->uVItemPadding_s=HIWORD(lParam); 308 } 309 return 0; 310 } 311 312 /****************************************************************************** 313 * TAB_InternalGetItemRect 314 * 315 * This method will calculate the rectangle representing a given tab item in 316 * client coordinates. This method takes scrolling into account. 317 * 318 * This method returns TRUE if the item is visible in the window and FALSE 319 * if it is completely outside the client area. 320 */ 321 static BOOL TAB_InternalGetItemRect( 322 const TAB_INFO* infoPtr, 323 INT itemIndex, 324 RECT* itemRect, 325 RECT* selectedRect) 326 { 327 RECT tmpItemRect,clientRect; 328 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE); 329 330 /* Perform a sanity check and a trivial visibility check. */ 331 if ( (infoPtr->uNumItem <= 0) || 332 (itemIndex >= infoPtr->uNumItem) || 333 (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) ) 334 { 335 TRACE("Not Visible\n"); 336 /* need to initialize these to empty rects */ 337 if (itemRect) 338 { 339 memset(itemRect,0,sizeof(RECT)); 340 itemRect->bottom = infoPtr->tabHeight; 341 } 342 if (selectedRect) 343 memset(selectedRect,0,sizeof(RECT)); 344 return FALSE; 345 } 346 347 /* 348 * Avoid special cases in this procedure by assigning the "out" 349 * parameters if the caller didn't supply them 350 */ 351 if (itemRect == NULL) 352 itemRect = &tmpItemRect; 353 354 /* Retrieve the unmodified item rect. */ 355 *itemRect = TAB_GetItem(infoPtr,itemIndex)->rect; 356 357 /* calculate the times bottom and top based on the row */ 358 GetClientRect(infoPtr->hwnd, &clientRect); 359 360 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL)) 361 { 362 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * infoPtr->tabHeight - 363 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0); 364 itemRect->left = itemRect->right - infoPtr->tabHeight; 365 } 366 else if (lStyle & TCS_VERTICAL) 367 { 368 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * infoPtr->tabHeight + 369 ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0); 370 itemRect->right = itemRect->left + infoPtr->tabHeight; 371 } 372 else if (lStyle & TCS_BOTTOM) 373 { 374 itemRect->bottom = clientRect.bottom - itemRect->top * infoPtr->tabHeight - 375 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET); 376 itemRect->top = itemRect->bottom - infoPtr->tabHeight; 377 } 378 else /* not TCS_BOTTOM and not TCS_VERTICAL */ 379 { 380 itemRect->top = clientRect.top + itemRect->top * infoPtr->tabHeight + 381 ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET); 382 itemRect->bottom = itemRect->top + infoPtr->tabHeight; 383 } 384 385 /* 386 * "scroll" it to make sure the item at the very left of the 387 * tab control is the leftmost visible tab. 388 */ 389 if(lStyle & TCS_VERTICAL) 390 { 391 OffsetRect(itemRect, 392 0, 393 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.top); 394 395 /* 396 * Move the rectangle so the first item is slightly offset from 397 * the bottom of the tab control. 398 */ 399 OffsetRect(itemRect, 400 0, 401 SELECTED_TAB_OFFSET); 402 403 } else 404 { 405 OffsetRect(itemRect, 406 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.left, 407 0); 408 409 /* 410 * Move the rectangle so the first item is slightly offset from 411 * the left of the tab control. 412 */ 413 OffsetRect(itemRect, 414 SELECTED_TAB_OFFSET, 415 0); 416 } 417 TRACE("item %d tab h=%d, rect=(%s)\n", 418 itemIndex, infoPtr->tabHeight, wine_dbgstr_rect(itemRect)); 419 420 /* Now, calculate the position of the item as if it were selected. */ 421 if (selectedRect!=NULL) 422 { 423 CopyRect(selectedRect, itemRect); 424 425 /* The rectangle of a selected item is a bit wider. */ 426 if(lStyle & TCS_VERTICAL) 427 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET); 428 else 429 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0); 430 431 /* If it also a bit higher. */ 432 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL)) 433 { 434 selectedRect->left -= 2; /* the border is thicker on the right */ 435 selectedRect->right += SELECTED_TAB_OFFSET; 436 } 437 else if (lStyle & TCS_VERTICAL) 438 { 439 selectedRect->left -= SELECTED_TAB_OFFSET; 440 selectedRect->right += 1; 441 } 442 else if (lStyle & TCS_BOTTOM) 443 { 444 selectedRect->bottom += SELECTED_TAB_OFFSET; 445 } 446 else /* not TCS_BOTTOM and not TCS_VERTICAL */ 447 { 448 selectedRect->top -= SELECTED_TAB_OFFSET; 449 selectedRect->bottom -= 1; 450 } 451 } 452 453 /* Check for visibility */ 454 if (lStyle & TCS_VERTICAL) 455 return (itemRect->top < clientRect.bottom) && (itemRect->bottom > clientRect.top); 456 else 457 return (itemRect->left < clientRect.right) && (itemRect->right > clientRect.left); 458 } 459 460 static inline BOOL 461 TAB_GetItemRect(const TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 462 { 463 return TAB_InternalGetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam, (LPRECT)NULL); 464 } 465 466 /****************************************************************************** 467 * TAB_KeyUp 468 * 469 * This method is called to handle keyboard input 470 */ 471 static LRESULT TAB_KeyUp(TAB_INFO* infoPtr, WPARAM keyCode) 472 { 473 int newItem = -1; 474 475 switch (keyCode) 476 { 477 case VK_LEFT: 478 newItem = infoPtr->uFocus - 1; 479 break; 480 case VK_RIGHT: 481 newItem = infoPtr->uFocus + 1; 482 break; 483 } 484 485 /* 486 * If we changed to a valid item, change the selection 487 */ 488 if (newItem >= 0 && 489 newItem < infoPtr->uNumItem && 490 infoPtr->uFocus != newItem) 491 { 492 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING)) 493 { 494 infoPtr->iSelected = newItem; 495 infoPtr->uFocus = newItem; 496 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE); 497 498 TAB_EnsureSelectionVisible(infoPtr); 499 TAB_InvalidateTabArea(infoPtr); 500 } 501 } 502 503 return 0; 504 } 505 506 /****************************************************************************** 507 * TAB_FocusChanging 508 * 509 * This method is called whenever the focus goes in or out of this control 510 * it is used to update the visual state of the control. 511 */ 512 static void TAB_FocusChanging(const TAB_INFO *infoPtr) 513 { 514 RECT selectedRect; 515 BOOL isVisible; 516 517 /* 518 * Get the rectangle for the item. 519 */ 520 isVisible = TAB_InternalGetItemRect(infoPtr, 521 infoPtr->uFocus, 522 NULL, 523 &selectedRect); 524 525 /* 526 * If the rectangle is not completely invisible, invalidate that 527 * portion of the window. 528 */ 529 if (isVisible) 530 { 531 TRACE("invalidate (%s)\n", wine_dbgstr_rect(&selectedRect)); 532 InvalidateRect(infoPtr->hwnd, &selectedRect, TRUE); 533 } 534 } 535 536 static INT TAB_InternalHitTest (const TAB_INFO *infoPtr, POINT pt, UINT *flags) 537 { 538 RECT rect; 539 INT iCount; 540 541 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++) 542 { 543 TAB_InternalGetItemRect(infoPtr, iCount, &rect, NULL); 544 545 if (PtInRect(&rect, pt)) 546 { 547 *flags = TCHT_ONITEM; 548 return iCount; 549 } 550 } 551 552 *flags = TCHT_NOWHERE; 553 return -1; 554 } 555 556 static inline LRESULT 557 TAB_HitTest (const TAB_INFO *infoPtr, LPTCHITTESTINFO lptest) 558 { 559 return TAB_InternalHitTest (infoPtr, lptest->pt, &lptest->flags); 560 } 561 562 /****************************************************************************** 563 * TAB_NCHitTest 564 * 565 * Napster v2b5 has a tab control for its main navigation which has a client 566 * area that covers the whole area of the dialog pages. 567 * That's why it receives all msgs for that area and the underlying dialog ctrls 568 * are dead. 569 * So I decided that we should handle WM_NCHITTEST here and return 570 * HTTRANSPARENT if we don't hit the tab control buttons. 571 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows 572 * doesn't do it that way. Maybe depends on tab control styles ? 573 */ 574 static inline LRESULT 575 TAB_NCHitTest (const TAB_INFO *infoPtr, LPARAM lParam) 576 { 577 POINT pt; 578 UINT dummyflag; 579 580 pt.x = (short)LOWORD(lParam); 581 pt.y = (short)HIWORD(lParam); 582 ScreenToClient(infoPtr->hwnd, &pt); 583 584 if (TAB_InternalHitTest(infoPtr, pt, &dummyflag) == -1) 585 return HTTRANSPARENT; 586 else 587 return HTCLIENT; 588 } 589 590 static LRESULT 591 TAB_LButtonDown (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 592 { 593 POINT pt; 594 INT newItem; 595 UINT dummy; 596 597 if (infoPtr->hwndToolTip) 598 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd, 599 WM_LBUTTONDOWN, wParam, lParam); 600 601 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) { 602 SetFocus (infoPtr->hwnd); 603 } 604 605 if (infoPtr->hwndToolTip) 606 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd, 607 WM_LBUTTONDOWN, wParam, lParam); 608 609 pt.x = (short)LOWORD(lParam); 610 pt.y = (short)HIWORD(lParam); 611 612 newItem = TAB_InternalHitTest (infoPtr, pt, &dummy); 613 614 TRACE("On Tab, item %d\n", newItem); 615 616 if (newItem != -1 && infoPtr->iSelected != newItem) 617 { 618 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING)) 619 { 620 infoPtr->iSelected = newItem; 621 infoPtr->uFocus = newItem; 622 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE); 623 624 TAB_EnsureSelectionVisible(infoPtr); 625 626 TAB_InvalidateTabArea(infoPtr); 627 } 628 } 629 return 0; 630 } 631 632 static inline LRESULT 633 TAB_LButtonUp (const TAB_INFO *infoPtr) 634 { 635 TAB_SendSimpleNotify(infoPtr, NM_CLICK); 636 637 return 0; 638 } 639 640 static inline LRESULT 641 TAB_RButtonDown (const TAB_INFO *infoPtr) 642 { 643 TAB_SendSimpleNotify(infoPtr, NM_RCLICK); 644 return 0; 645 } 646 647 /****************************************************************************** 648 * TAB_DrawLoneItemInterior 649 * 650 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally 651 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets 652 * up the device context and font. This routine does the same setup but 653 * only calls TAB_DrawItemInterior for the single specified item. 654 */ 655 static void 656 TAB_DrawLoneItemInterior(const TAB_INFO* infoPtr, int iItem) 657 { 658 HDC hdc = GetDC(infoPtr->hwnd); 659 RECT r, rC; 660 661 /* Clip UpDown control to not draw over it */ 662 if (infoPtr->needsScrolling) 663 { 664 GetWindowRect(infoPtr->hwnd, &rC); 665 GetWindowRect(infoPtr->hwndUpDown, &r); 666 ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top); 667 } 668 TAB_DrawItemInterior(infoPtr, hdc, iItem, NULL); 669 ReleaseDC(infoPtr->hwnd, hdc); 670 } 671 672 /* update a tab after hottracking - invalidate it or just redraw the interior, 673 * based on whether theming is used or not */ 674 static inline void hottrack_refresh(const TAB_INFO *infoPtr, int tabIndex) 675 { 676 if (tabIndex == -1) return; 677 678 if (GetWindowTheme (infoPtr->hwnd)) 679 { 680 RECT rect; 681 TAB_InternalGetItemRect(infoPtr, tabIndex, &rect, NULL); 682 InvalidateRect (infoPtr->hwnd, &rect, FALSE); 683 } 684 else 685 TAB_DrawLoneItemInterior(infoPtr, tabIndex); 686 } 687 688 /****************************************************************************** 689 * TAB_HotTrackTimerProc 690 * 691 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a 692 * timer is setup so we can check if the mouse is moved out of our window. 693 * (We don't get an event when the mouse leaves, the mouse-move events just 694 * stop being delivered to our window and just start being delivered to 695 * another window.) This function is called when the timer triggers so 696 * we can check if the mouse has left our window. If so, we un-highlight 697 * the hot-tracked tab. 698 */ 699 static void CALLBACK 700 TAB_HotTrackTimerProc 701 ( 702 HWND hwnd, /* handle of window for timer messages */ 703 UINT uMsg, /* WM_TIMER message */ 704 UINT_PTR idEvent, /* timer identifier */ 705 DWORD dwTime /* current system time */ 706 ) 707 { 708 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd); 709 710 if (infoPtr != NULL && infoPtr->iHotTracked >= 0) 711 { 712 POINT pt; 713 714 /* 715 ** If we can't get the cursor position, or if the cursor is outside our 716 ** window, we un-highlight the hot-tracked tab. Note that the cursor is 717 ** "outside" even if it is within our bounding rect if another window 718 ** overlaps. Note also that the case where the cursor stayed within our 719 ** window but has moved off the hot-tracked tab will be handled by the 720 ** WM_MOUSEMOVE event. 721 */ 722 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd) 723 { 724 /* Redraw iHotTracked to look normal */ 725 INT iRedraw = infoPtr->iHotTracked; 726 infoPtr->iHotTracked = -1; 727 hottrack_refresh (infoPtr, iRedraw); 728 729 /* Kill this timer */ 730 KillTimer(hwnd, TAB_HOTTRACK_TIMER); 731 } 732 } 733 } 734 735 /****************************************************************************** 736 * TAB_RecalcHotTrack 737 * 738 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse 739 * should be highlighted. This function determines which tab in a tab control, 740 * if any, is under the mouse and records that information. The caller may 741 * supply output parameters to receive the item number of the tab item which 742 * was highlighted but isn't any longer and of the tab item which is now 743 * highlighted but wasn't previously. The caller can use this information to 744 * selectively redraw those tab items. 745 * 746 * If the caller has a mouse position, it can supply it through the pos 747 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller 748 * supplies NULL and this function determines the current mouse position 749 * itself. 750 */ 751 static void 752 TAB_RecalcHotTrack 753 ( 754 TAB_INFO* infoPtr, 755 const LPARAM* pos, 756 int* out_redrawLeave, 757 int* out_redrawEnter 758 ) 759 { 760 int item = -1; 761 762 763 if (out_redrawLeave != NULL) 764 *out_redrawLeave = -1; 765 if (out_redrawEnter != NULL) 766 *out_redrawEnter = -1; 767 768 if ((GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_HOTTRACK) 769 || GetWindowTheme (infoPtr->hwnd)) 770 { 771 POINT pt; 772 UINT flags; 773 774 if (pos == NULL) 775 { 776 GetCursorPos(&pt); 777 ScreenToClient(infoPtr->hwnd, &pt); 778 } 779 else 780 { 781 pt.x = (short)LOWORD(*pos); 782 pt.y = (short)HIWORD(*pos); 783 } 784 785 item = TAB_InternalHitTest(infoPtr, pt, &flags); 786 } 787 788 if (item != infoPtr->iHotTracked) 789 { 790 if (infoPtr->iHotTracked >= 0) 791 { 792 /* Mark currently hot-tracked to be redrawn to look normal */ 793 if (out_redrawLeave != NULL) 794 *out_redrawLeave = infoPtr->iHotTracked; 795 796 if (item < 0) 797 { 798 /* Kill timer which forces recheck of mouse pos */ 799 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER); 800 } 801 } 802 else 803 { 804 /* Start timer so we recheck mouse pos */ 805 UINT timerID = SetTimer 806 ( 807 infoPtr->hwnd, 808 TAB_HOTTRACK_TIMER, 809 TAB_HOTTRACK_TIMER_INTERVAL, 810 TAB_HotTrackTimerProc 811 ); 812 813 if (timerID == 0) 814 return; /* Hot tracking not available */ 815 } 816 817 infoPtr->iHotTracked = item; 818 819 if (item >= 0) 820 { 821 /* Mark new hot-tracked to be redrawn to look highlighted */ 822 if (out_redrawEnter != NULL) 823 *out_redrawEnter = item; 824 } 825 } 826 } 827 828 /****************************************************************************** 829 * TAB_MouseMove 830 * 831 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking. 832 */ 833 static LRESULT 834 TAB_MouseMove (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 835 { 836 int redrawLeave; 837 int redrawEnter; 838 839 if (infoPtr->hwndToolTip) 840 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd, 841 WM_LBUTTONDOWN, wParam, lParam); 842 843 /* Determine which tab to highlight. Redraw tabs which change highlight 844 ** status. */ 845 TAB_RecalcHotTrack(infoPtr, &lParam, &redrawLeave, &redrawEnter); 846 847 hottrack_refresh (infoPtr, redrawLeave); 848 hottrack_refresh (infoPtr, redrawEnter); 849 850 return 0; 851 } 852 853 /****************************************************************************** 854 * TAB_AdjustRect 855 * 856 * Calculates the tab control's display area given the window rectangle or 857 * the window rectangle given the requested display rectangle. 858 */ 859 static LRESULT TAB_AdjustRect(const TAB_INFO *infoPtr, WPARAM fLarger, LPRECT prc) 860 { 861 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE); 862 LONG *iRightBottom, *iLeftTop; 863 864 TRACE ("hwnd=%p fLarger=%ld (%s)\n", infoPtr->hwnd, fLarger, 865 wine_dbgstr_rect(prc)); 866 867 if(lStyle & TCS_VERTICAL) 868 { 869 iRightBottom = &(prc->right); 870 iLeftTop = &(prc->left); 871 } 872 else 873 { 874 iRightBottom = &(prc->bottom); 875 iLeftTop = &(prc->top); 876 } 877 878 if (fLarger) /* Go from display rectangle */ 879 { 880 /* Add the height of the tabs. */ 881 if (lStyle & TCS_BOTTOM) 882 *iRightBottom += infoPtr->tabHeight * infoPtr->uNumRows; 883 else 884 *iLeftTop -= infoPtr->tabHeight * infoPtr->uNumRows + 885 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0); 886 887 /* Inflate the rectangle for the padding */ 888 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY); 889 890 /* Inflate for the border */ 891 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEY); 892 } 893 else /* Go from window rectangle. */ 894 { 895 /* Deflate the rectangle for the border */ 896 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEY); 897 898 /* Deflate the rectangle for the padding */ 899 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY); 900 901 /* Remove the height of the tabs. */ 902 if (lStyle & TCS_BOTTOM) 903 *iRightBottom -= infoPtr->tabHeight * infoPtr->uNumRows; 904 else 905 *iLeftTop += (infoPtr->tabHeight) * infoPtr->uNumRows + 906 ((lStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0); 907 } 908 909 return 0; 910 } 911 912 /****************************************************************************** 913 * TAB_OnHScroll 914 * 915 * This method will handle the notification from the scroll control and 916 * perform the scrolling operation on the tab control. 917 */ 918 static LRESULT TAB_OnHScroll(TAB_INFO *infoPtr, int nScrollCode, int nPos, HWND hwndScroll) 919 { 920 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible) 921 { 922 if(nPos < infoPtr->leftmostVisible) 923 infoPtr->leftmostVisible--; 924 else 925 infoPtr->leftmostVisible++; 926 927 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL); 928 TAB_InvalidateTabArea(infoPtr); 929 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0, 930 MAKELONG(infoPtr->leftmostVisible, 0)); 931 } 932 933 return 0; 934 } 935 936 /****************************************************************************** 937 * TAB_SetupScrolling 938 * 939 * This method will check the current scrolling state and make sure the 940 * scrolling control is displayed (or not). 941 */ 942 static void TAB_SetupScrolling( 943 HWND hwnd, 944 TAB_INFO* infoPtr, 945 const RECT* clientRect) 946 { 947 static const WCHAR msctls_updown32W[] = { 'm','s','c','t','l','s','_','u','p','d','o','w','n','3','2',0 }; 948 static const WCHAR emptyW[] = { 0 }; 949 INT maxRange = 0; 950 DWORD lStyle = GetWindowLongW(hwnd, GWL_STYLE); 951 952 if (infoPtr->needsScrolling) 953 { 954 RECT controlPos; 955 INT vsize, tabwidth; 956 957 /* 958 * Calculate the position of the scroll control. 959 */ 960 if(lStyle & TCS_VERTICAL) 961 { 962 controlPos.right = clientRect->right; 963 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL); 964 965 if (lStyle & TCS_BOTTOM) 966 { 967 controlPos.top = clientRect->bottom - infoPtr->tabHeight; 968 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL); 969 } 970 else 971 { 972 controlPos.bottom = clientRect->top + infoPtr->tabHeight; 973 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL); 974 } 975 } 976 else 977 { 978 controlPos.right = clientRect->right; 979 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL); 980 981 if (lStyle & TCS_BOTTOM) 982 { 983 controlPos.top = clientRect->bottom - infoPtr->tabHeight; 984 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL); 985 } 986 else 987 { 988 controlPos.bottom = clientRect->top + infoPtr->tabHeight; 989 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL); 990 } 991 } 992 993 /* 994 * If we don't have a scroll control yet, we want to create one. 995 * If we have one, we want to make sure it's positioned properly. 996 */ 997 if (infoPtr->hwndUpDown==0) 998 { 999 infoPtr->hwndUpDown = CreateWindowW(msctls_updown32W, emptyW, 1000 WS_VISIBLE | WS_CHILD | UDS_HORZ, 1001 controlPos.left, controlPos.top, 1002 controlPos.right - controlPos.left, 1003 controlPos.bottom - controlPos.top, 1004 hwnd, NULL, NULL, NULL); 1005 } 1006 else 1007 { 1008 SetWindowPos(infoPtr->hwndUpDown, 1009 NULL, 1010 controlPos.left, controlPos.top, 1011 controlPos.right - controlPos.left, 1012 controlPos.bottom - controlPos.top, 1013 SWP_SHOWWINDOW | SWP_NOZORDER); 1014 } 1015 1016 /* Now calculate upper limit of the updown control range. 1017 * We do this by calculating how many tabs will be offscreen when the 1018 * last tab is visible. 1019 */ 1020 if(infoPtr->uNumItem) 1021 { 1022 vsize = clientRect->right - (controlPos.right - controlPos.left + 1); 1023 maxRange = infoPtr->uNumItem; 1024 tabwidth = TAB_GetItem(infoPtr, infoPtr->uNumItem - 1)->rect.right; 1025 1026 for(; maxRange > 0; maxRange--) 1027 { 1028 if(tabwidth - TAB_GetItem(infoPtr,maxRange - 1)->rect.left > vsize) 1029 break; 1030 } 1031 1032 if(maxRange == infoPtr->uNumItem) 1033 maxRange--; 1034 } 1035 } 1036 else 1037 { 1038 /* If we once had a scroll control... hide it */ 1039 if (infoPtr->hwndUpDown!=0) 1040 ShowWindow(infoPtr->hwndUpDown, SW_HIDE); 1041 } 1042 if (infoPtr->hwndUpDown) 1043 SendMessageW(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange); 1044 } 1045 1046 /****************************************************************************** 1047 * TAB_SetItemBounds 1048 * 1049 * This method will calculate the position rectangles of all the items in the 1050 * control. The rectangle calculated starts at 0 for the first item in the 1051 * list and ignores scrolling and selection. 1052 * It also uses the current font to determine the height of the tab row and 1053 * it checks if all the tabs fit in the client area of the window. If they 1054 * don't, a scrolling control is added. 1055 */ 1056 static void TAB_SetItemBounds (TAB_INFO *infoPtr) 1057 { 1058 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE); 1059 TEXTMETRICW fontMetrics; 1060 UINT curItem; 1061 INT curItemLeftPos; 1062 INT curItemRowCount; 1063 HFONT hFont, hOldFont; 1064 HDC hdc; 1065 RECT clientRect; 1066 INT iTemp; 1067 RECT* rcItem; 1068 INT iIndex; 1069 INT icon_width = 0; 1070 1071 /* 1072 * We need to get text information so we need a DC and we need to select 1073 * a font. 1074 */ 1075 hdc = GetDC(infoPtr->hwnd); 1076 1077 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT); 1078 hOldFont = SelectObject (hdc, hFont); 1079 1080 /* 1081 * We will base the rectangle calculations on the client rectangle 1082 * of the control. 1083 */ 1084 GetClientRect(infoPtr->hwnd, &clientRect); 1085 1086 /* if TCS_VERTICAL then swap the height and width so this code places the 1087 tabs along the top of the rectangle and we can just rotate them after 1088 rather than duplicate all of the below code */ 1089 if(lStyle & TCS_VERTICAL) 1090 { 1091 iTemp = clientRect.bottom; 1092 clientRect.bottom = clientRect.right; 1093 clientRect.right = iTemp; 1094 } 1095 1096 /* Now use hPadding and vPadding */ 1097 infoPtr->uHItemPadding = infoPtr->uHItemPadding_s; 1098 infoPtr->uVItemPadding = infoPtr->uVItemPadding_s; 1099 1100 /* The leftmost item will be "0" aligned */ 1101 curItemLeftPos = 0; 1102 curItemRowCount = infoPtr->uNumItem ? 1 : 0; 1103 1104 if (!(infoPtr->fHeightSet)) 1105 { 1106 int item_height; 1107 int icon_height = 0; 1108 1109 /* Use the current font to determine the height of a tab. */ 1110 GetTextMetricsW(hdc, &fontMetrics); 1111 1112 /* Get the icon height */ 1113 if (infoPtr->himl) 1114 ImageList_GetIconSize(infoPtr->himl, 0, &icon_height); 1115 1116 /* Take the highest between font or icon */ 1117 if (fontMetrics.tmHeight > icon_height) 1118 item_height = fontMetrics.tmHeight + 2; 1119 else 1120 item_height = icon_height; 1121 1122 /* 1123 * Make sure there is enough space for the letters + icon + growing the 1124 * selected item + extra space for the selected item. 1125 */ 1126 infoPtr->tabHeight = item_height + 1127 ((lStyle & TCS_BUTTONS) ? 2 : 1) * 1128 infoPtr->uVItemPadding; 1129 1130 TRACE("tabH=%d, tmH=%d, iconh=%d\n", 1131 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height); 1132 } 1133 1134 TRACE("client right=%d\n", clientRect.right); 1135 1136 /* Get the icon width */ 1137 if (infoPtr->himl) 1138 { 1139 ImageList_GetIconSize(infoPtr->himl, &icon_width, 0); 1140 1141 if (lStyle & TCS_FIXEDWIDTH) 1142 icon_width += 4; 1143 else 1144 /* Add padding if icon is present */ 1145 icon_width += infoPtr->uHItemPadding; 1146 } 1147 1148 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++) 1149 { 1150 TAB_ITEM *curr = TAB_GetItem(infoPtr, curItem); 1151 1152 /* Set the leftmost position of the tab. */ 1153 curr->rect.left = curItemLeftPos; 1154 1155 if (lStyle & TCS_FIXEDWIDTH) 1156 { 1157 curr->rect.right = curr->rect.left + 1158 max(infoPtr->tabWidth, icon_width); 1159 } 1160 else if (!curr->pszText) 1161 { 1162 /* If no text use minimum tab width including padding. */ 1163 if (infoPtr->tabMinWidth < 0) 1164 curr->rect.right = curr->rect.left + GET_DEFAULT_MIN_TAB_WIDTH(infoPtr); 1165 else 1166 { 1167 curr->rect.right = curr->rect.left + infoPtr->tabMinWidth; 1168 1169 /* Add extra padding if icon is present */ 1170 if (infoPtr->himl && infoPtr->tabMinWidth > 0 && infoPtr->tabMinWidth < DEFAULT_MIN_TAB_WIDTH 1171 && infoPtr->uHItemPadding > 1) 1172 curr->rect.right += EXTRA_ICON_PADDING * (infoPtr->uHItemPadding-1); 1173 } 1174 } 1175 else 1176 { 1177 int tabwidth; 1178 SIZE size; 1179 /* Calculate how wide the tab is depending on the text it contains */ 1180 GetTextExtentPoint32W(hdc, curr->pszText, 1181 lstrlenW(curr->pszText), &size); 1182 1183 tabwidth = size.cx + icon_width + 2 * infoPtr->uHItemPadding; 1184 1185 if (infoPtr->tabMinWidth < 0) 1186 tabwidth = max(tabwidth, GET_DEFAULT_MIN_TAB_WIDTH(infoPtr)); 1187 else 1188 tabwidth = max(tabwidth, infoPtr->tabMinWidth); 1189 1190 curr->rect.right = curr->rect.left + tabwidth; 1191 TRACE("for <%s>, l,r=%d,%d\n", 1192 debugstr_w(curr->pszText), curr->rect.left, curr->rect.right); 1193 } 1194 1195 /* 1196 * Check if this is a multiline tab control and if so 1197 * check to see if we should wrap the tabs 1198 * 1199 * Wrap all these tabs. We will arrange them evenly later. 1200 * 1201 */ 1202 1203 if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && 1204 (curr->rect.right > 1205 (clientRect.right - CONTROL_BORDER_SIZEX - DISPLAY_AREA_PADDINGX))) 1206 { 1207 curr->rect.right -= curr->rect.left; 1208 1209 curr->rect.left = 0; 1210 curItemRowCount++; 1211 TRACE("wrapping <%s>, l,r=%d,%d\n", debugstr_w(curr->pszText), 1212 curr->rect.left, curr->rect.right); 1213 } 1214 1215 curr->rect.bottom = 0; 1216 curr->rect.top = curItemRowCount - 1; 1217 1218 TRACE("Rect: %s\n", wine_dbgstr_rect(&curr->rect)); 1219 1220 /* 1221 * The leftmost position of the next item is the rightmost position 1222 * of this one. 1223 */ 1224 if (lStyle & TCS_BUTTONS) 1225 { 1226 curItemLeftPos = curr->rect.right + BUTTON_SPACINGX; 1227 if (lStyle & TCS_FLATBUTTONS) 1228 curItemLeftPos += FLAT_BTN_SPACINGX; 1229 } 1230 else 1231 curItemLeftPos = curr->rect.right; 1232 } 1233 1234 if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL))) 1235 { 1236 /* 1237 * Check if we need a scrolling control. 1238 */ 1239 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) > 1240 clientRect.right); 1241 1242 /* Don't need scrolling, then update infoPtr->leftmostVisible */ 1243 if(!infoPtr->needsScrolling) 1244 infoPtr->leftmostVisible = 0; 1245 } 1246 else 1247 { 1248 /* 1249 * No scrolling in Multiline or Vertical styles. 1250 */ 1251 infoPtr->needsScrolling = FALSE; 1252 infoPtr->leftmostVisible = 0; 1253 } 1254 TAB_SetupScrolling(infoPtr->hwnd, infoPtr, &clientRect); 1255 1256 /* Set the number of rows */ 1257 infoPtr->uNumRows = curItemRowCount; 1258 1259 /* Arrange all tabs evenly if style says so */ 1260 if (!(lStyle & TCS_RAGGEDRIGHT) && 1261 ((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && 1262 (infoPtr->uNumItem > 0) && 1263 (infoPtr->uNumRows > 1)) 1264 { 1265 INT tabPerRow,remTab,iRow; 1266 UINT iItm; 1267 INT iCount=0; 1268 1269 /* 1270 * Ok windows tries to even out the rows. place the same 1271 * number of tabs in each row. So lets give that a shot 1272 */ 1273 1274 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows); 1275 remTab = infoPtr->uNumItem % (infoPtr->uNumRows); 1276 1277 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0; 1278 iItm<infoPtr->uNumItem; 1279 iItm++,iCount++) 1280 { 1281 /* normalize the current rect */ 1282 TAB_ITEM *curr = TAB_GetItem(infoPtr, iItm); 1283 1284 /* shift the item to the left side of the clientRect */ 1285 curr->rect.right -= curr->rect.left; 1286 curr->rect.left = 0; 1287 1288 TRACE("r=%d, cl=%d, cl.r=%d, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n", 1289 curr->rect.right, curItemLeftPos, clientRect.right, 1290 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow); 1291 1292 /* if we have reached the maximum number of tabs on this row */ 1293 /* move to the next row, reset our current item left position and */ 1294 /* the count of items on this row */ 1295 1296 if (lStyle & TCS_VERTICAL) { 1297 /* Vert: Add the remaining tabs in the *last* remainder rows */ 1298 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) { 1299 iRow++; 1300 curItemLeftPos = 0; 1301 iCount = 0; 1302 } 1303 } else { 1304 /* Horz: Add the remaining tabs in the *first* remainder rows */ 1305 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) { 1306 iRow++; 1307 curItemLeftPos = 0; 1308 iCount = 0; 1309 } 1310 } 1311 1312 /* shift the item to the right to place it as the next item in this row */ 1313 curr->rect.left += curItemLeftPos; 1314 curr->rect.right += curItemLeftPos; 1315 curr->rect.top = iRow; 1316 if (lStyle & TCS_BUTTONS) 1317 { 1318 curItemLeftPos = curr->rect.right + 1; 1319 if (lStyle & TCS_FLATBUTTONS) 1320 curItemLeftPos += FLAT_BTN_SPACINGX; 1321 } 1322 else 1323 curItemLeftPos = curr->rect.right; 1324 1325 TRACE("arranging <%s>, l,r=%d,%d, row=%d\n", 1326 debugstr_w(curr->pszText), curr->rect.left, 1327 curr->rect.right, curr->rect.top); 1328 } 1329 1330 /* 1331 * Justify the rows 1332 */ 1333 { 1334 INT widthDiff, iIndexStart=0, iIndexEnd=0; 1335 INT remainder; 1336 INT iCount=0; 1337 1338 while(iIndexStart < infoPtr->uNumItem) 1339 { 1340 TAB_ITEM *start = TAB_GetItem(infoPtr, iIndexStart); 1341 1342 /* 1343 * find the index of the row 1344 */ 1345 /* find the first item on the next row */ 1346 for (iIndexEnd=iIndexStart; 1347 (iIndexEnd < infoPtr->uNumItem) && 1348 (TAB_GetItem(infoPtr, iIndexEnd)->rect.top == 1349 start->rect.top) ; 1350 iIndexEnd++) 1351 /* intentionally blank */; 1352 1353 /* 1354 * we need to justify these tabs so they fill the whole given 1355 * client area 1356 * 1357 */ 1358 /* find the amount of space remaining on this row */ 1359 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) - 1360 TAB_GetItem(infoPtr, iIndexEnd - 1)->rect.right; 1361 1362 /* iCount is the number of tab items on this row */ 1363 iCount = iIndexEnd - iIndexStart; 1364 1365 if (iCount > 1) 1366 { 1367 remainder = widthDiff % iCount; 1368 widthDiff = widthDiff / iCount; 1369 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */ 1370 for (iIndex=iIndexStart, iCount=0; iIndex < iIndexEnd; iIndex++, iCount++) 1371 { 1372 TAB_ITEM *item = TAB_GetItem(infoPtr, iIndex); 1373 1374 item->rect.left += iCount * widthDiff; 1375 item->rect.right += (iCount + 1) * widthDiff; 1376 1377 TRACE("adjusting 1 <%s>, l,r=%d,%d\n", 1378 debugstr_w(item->pszText), 1379 item->rect.left, item->rect.right); 1380 1381 } 1382 TAB_GetItem(infoPtr, iIndex - 1)->rect.right += remainder; 1383 } 1384 else /* we have only one item on this row, make it take up the entire row */ 1385 { 1386 start->rect.left = clientRect.left; 1387 start->rect.right = clientRect.right - 4; 1388 1389 TRACE("adjusting 2 <%s>, l,r=%d,%d\n", 1390 debugstr_w(start->pszText), 1391 start->rect.left, start->rect.right); 1392 1393 } 1394 1395 1396 iIndexStart = iIndexEnd; 1397 } 1398 } 1399 } 1400 1401 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */ 1402 if(lStyle & TCS_VERTICAL) 1403 { 1404 RECT rcOriginal; 1405 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++) 1406 { 1407 rcItem = &TAB_GetItem(infoPtr, iIndex)->rect; 1408 1409 rcOriginal = *rcItem; 1410 1411 /* this is rotating the items by 90 degrees clockwise around the center of the control */ 1412 rcItem->top = (rcOriginal.left - clientRect.left); 1413 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left); 1414 rcItem->left = rcOriginal.top; 1415 rcItem->right = rcOriginal.bottom; 1416 } 1417 } 1418 1419 TAB_EnsureSelectionVisible(infoPtr); 1420 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL); 1421 1422 /* Cleanup */ 1423 SelectObject (hdc, hOldFont); 1424 ReleaseDC (infoPtr->hwnd, hdc); 1425 } 1426 1427 1428 static void 1429 TAB_EraseTabInterior(const TAB_INFO *infoPtr, HDC hdc, INT iItem, RECT *drawRect) 1430 { 1431 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE); 1432 HBRUSH hbr = CreateSolidBrush (comctl32_color.clrBtnFace); 1433 BOOL deleteBrush = TRUE; 1434 RECT rTemp = *drawRect; 1435 1436 InflateRect(&rTemp, -2, -2); 1437 if (lStyle & TCS_BUTTONS) 1438 { 1439 if (iItem == infoPtr->iSelected) 1440 { 1441 /* Background color */ 1442 if (!(lStyle & TCS_OWNERDRAWFIXED)) 1443 { 1444 DeleteObject(hbr); 1445 hbr = GetSysColorBrush(COLOR_SCROLLBAR); 1446 1447 SetTextColor(hdc, comctl32_color.clr3dFace); 1448 SetBkColor(hdc, comctl32_color.clr3dHilight); 1449 1450 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT 1451 * we better use 0x55aa bitmap brush to make scrollbar's background 1452 * look different from the window background. 1453 */ 1454 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow) 1455 hbr = COMCTL32_hPattern55AABrush; 1456 1457 deleteBrush = FALSE; 1458 } 1459 FillRect(hdc, &rTemp, hbr); 1460 } 1461 else /* ! selected */ 1462 { 1463 if (lStyle & TCS_FLATBUTTONS) 1464 { 1465 FillRect(hdc, drawRect, hbr); 1466 if (iItem == infoPtr->iHotTracked) 1467 DrawEdge(hdc, drawRect, EDGE_RAISED, BF_SOFT|BF_RECT); 1468 } 1469 else 1470 FillRect(hdc, &rTemp, hbr); 1471 } 1472 1473 } 1474 else /* !TCS_BUTTONS */ 1475 { 1476 if (!GetWindowTheme (infoPtr->hwnd)) 1477 FillRect(hdc, &rTemp, hbr); 1478 } 1479 1480 /* Cleanup */ 1481 if (deleteBrush) DeleteObject(hbr); 1482 } 1483 1484 /****************************************************************************** 1485 * TAB_DrawItemInterior 1486 * 1487 * This method is used to draw the interior (text and icon) of a single tab 1488 * into the tab control. 1489 */ 1490 static void 1491 TAB_DrawItemInterior(const TAB_INFO *infoPtr, HDC hdc, INT iItem, RECT *drawRect) 1492 { 1493 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE); 1494 1495 RECT localRect; 1496 1497 HPEN htextPen; 1498 HPEN holdPen; 1499 INT oldBkMode; 1500 HFONT hOldFont; 1501 1502 /* if (drawRect == NULL) */ 1503 { 1504 BOOL isVisible; 1505 RECT itemRect; 1506 RECT selectedRect; 1507 1508 /* 1509 * Get the rectangle for the item. 1510 */ 1511 isVisible = TAB_InternalGetItemRect(infoPtr, iItem, &itemRect, &selectedRect); 1512 if (!isVisible) 1513 return; 1514 1515 /* 1516 * Make sure drawRect points to something valid; simplifies code. 1517 */ 1518 drawRect = &localRect; 1519 1520 /* 1521 * This logic copied from the part of TAB_DrawItem which draws 1522 * the tab background. It's important to keep it in sync. I 1523 * would have liked to avoid code duplication, but couldn't figure 1524 * out how without making spaghetti of TAB_DrawItem. 1525 */ 1526 if (iItem == infoPtr->iSelected) 1527 *drawRect = selectedRect; 1528 else 1529 *drawRect = itemRect; 1530 1531 if (lStyle & TCS_BUTTONS) 1532 { 1533 if (iItem == infoPtr->iSelected) 1534 { 1535 drawRect->left += 4; 1536 drawRect->top += 4; 1537 drawRect->right -= 4; 1538 drawRect->bottom -= 1; 1539 } 1540 else 1541 { 1542 drawRect->left += 2; 1543 drawRect->top += 2; 1544 drawRect->right -= 2; 1545 drawRect->bottom -= 2; 1546 } 1547 } 1548 else 1549 { 1550 if ((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM)) 1551 { 1552 if (iItem != infoPtr->iSelected) 1553 { 1554 drawRect->left += 2; 1555 drawRect->top += 2; 1556 drawRect->bottom -= 2; 1557 } 1558 } 1559 else if (lStyle & TCS_VERTICAL) 1560 { 1561 if (iItem == infoPtr->iSelected) 1562 { 1563 drawRect->right += 1; 1564 } 1565 else 1566 { 1567 drawRect->top += 2; 1568 drawRect->right -= 2; 1569 drawRect->bottom -= 2; 1570 } 1571 } 1572 else if (lStyle & TCS_BOTTOM) 1573 { 1574 if (iItem == infoPtr->iSelected) 1575 { 1576 drawRect->top -= 2; 1577 } 1578 else 1579 { 1580 InflateRect(drawRect, -2, -2); 1581 drawRect->bottom += 2; 1582 } 1583 } 1584 else 1585 { 1586 if (iItem == infoPtr->iSelected) 1587 { 1588 drawRect->bottom += 3; 1589 } 1590 else 1591 { 1592 drawRect->bottom -= 2; 1593 InflateRect(drawRect, -2, 0); 1594 } 1595 } 1596 } 1597 } 1598 TRACE("drawRect=(%s)\n", wine_dbgstr_rect(drawRect)); 1599 1600 /* Clear interior */ 1601 TAB_EraseTabInterior (infoPtr, hdc, iItem, drawRect); 1602 1603 /* Draw the focus rectangle */ 1604 if (!(lStyle & TCS_FOCUSNEVER) && 1605 (GetFocus() == infoPtr->hwnd) && 1606 (iItem == infoPtr->uFocus) ) 1607 { 1608 RECT rFocus = *drawRect; 1609 InflateRect(&rFocus, -3, -3); 1610 if (lStyle & TCS_BOTTOM && !(lStyle & TCS_VERTICAL)) 1611 rFocus.top -= 3; 1612 if (lStyle & TCS_BUTTONS) 1613 { 1614 rFocus.left -= 3; 1615 rFocus.top -= 3; 1616 } 1617 1618 DrawFocusRect(hdc, &rFocus); 1619 } 1620 1621 /* 1622 * Text pen 1623 */ 1624 htextPen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT) ); 1625 holdPen = SelectObject(hdc, htextPen); 1626 hOldFont = SelectObject(hdc, infoPtr->hFont); 1627 1628 /* 1629 * Setup for text output 1630 */ 1631 oldBkMode = SetBkMode(hdc, TRANSPARENT); 1632 if (!GetWindowTheme (infoPtr->hwnd) || (lStyle & TCS_BUTTONS)) 1633 SetTextColor(hdc, (((lStyle & TCS_HOTTRACK) && (iItem == infoPtr->iHotTracked) 1634 && !(lStyle & TCS_FLATBUTTONS)) 1635 | (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED)) ? 1636 comctl32_color.clrHighlight : comctl32_color.clrBtnText); 1637 1638 /* 1639 * if owner draw, tell the owner to draw 1640 */ 1641 if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(infoPtr->hwnd)) 1642 { 1643 DRAWITEMSTRUCT dis; 1644 UINT id; 1645 1646 drawRect->top += 2; 1647 drawRect->right -= 1; 1648 if ( iItem == infoPtr->iSelected ) 1649 { 1650 drawRect->right -= 1; 1651 drawRect->left += 1; 1652 } 1653 1654 /* 1655 * get the control id 1656 */ 1657 id = (UINT)GetWindowLongPtrW( infoPtr->hwnd, GWLP_ID ); 1658 1659 /* 1660 * put together the DRAWITEMSTRUCT 1661 */ 1662 dis.CtlType = ODT_TAB; 1663 dis.CtlID = id; 1664 dis.itemID = iItem; 1665 dis.itemAction = ODA_DRAWENTIRE; 1666 dis.itemState = 0; 1667 if ( iItem == infoPtr->iSelected ) 1668 dis.itemState |= ODS_SELECTED; 1669 if (infoPtr->uFocus == iItem) 1670 dis.itemState |= ODS_FOCUS; 1671 dis.hwndItem = infoPtr->hwnd; 1672 dis.hDC = hdc; 1673 CopyRect(&dis.rcItem,drawRect); 1674 dis.itemData = (ULONG_PTR)TAB_GetItem(infoPtr, iItem)->extra; 1675 1676 /* 1677 * send the draw message 1678 */ 1679 SendMessageW( infoPtr->hwndNotify, WM_DRAWITEM, (WPARAM)id, (LPARAM)&dis ); 1680 } 1681 else 1682 { 1683 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem); 1684 RECT rcTemp; 1685 RECT rcImage; 1686 1687 /* used to center the icon and text in the tab */ 1688 RECT rcText; 1689 INT center_offset_h, center_offset_v; 1690 1691 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */ 1692 rcImage = *drawRect; 1693 1694 rcTemp = *drawRect; 1695 1696 rcText.left = rcText.top = rcText.right = rcText.bottom = 0; 1697 1698 /* get the rectangle that the text fits in */ 1699 if (item->pszText) 1700 { 1701 DrawTextW(hdc, item->pszText, -1, &rcText, DT_CALCRECT); 1702 } 1703 /* 1704 * If not owner draw, then do the drawing ourselves. 1705 * 1706 * Draw the icon. 1707 */ 1708 if (infoPtr->himl && item->iImage != -1) 1709 { 1710 INT cx; 1711 INT cy; 1712 1713 ImageList_GetIconSize(infoPtr->himl, &cx, &cy); 1714 1715 if(lStyle & TCS_VERTICAL) 1716 { 1717 center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2; 1718 center_offset_v = ((drawRect->right - drawRect->left) - cx) / 2; 1719 } 1720 else 1721 { 1722 center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2; 1723 center_offset_v = ((drawRect->bottom - drawRect->top) - cy) / 2; 1724 } 1725 1726 /* if an item is selected, the icon is shifted up instead of down */ 1727 if (iItem == infoPtr->iSelected) 1728 center_offset_v -= infoPtr->uVItemPadding / 2; 1729 else 1730 center_offset_v += infoPtr->uVItemPadding / 2; 1731 1732 if (lStyle & TCS_FIXEDWIDTH && lStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT)) 1733 center_offset_h = infoPtr->uHItemPadding; 1734 1735 if (center_offset_h < 2) 1736 center_offset_h = 2; 1737 1738 if (center_offset_v < 0) 1739 center_offset_v = 0; 1740 1741 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%s), textlen=%d\n", 1742 debugstr_w(item->pszText), center_offset_h, center_offset_v, 1743 wine_dbgstr_rect(drawRect), (rcText.right-rcText.left)); 1744 1745 if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM)) 1746 { 1747 rcImage.top = drawRect->top + center_offset_h; 1748 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */ 1749 /* right side of the tab, but the image still uses the left as its x position */ 1750 /* this keeps the image always drawn off of the same side of the tab */ 1751 rcImage.left = drawRect->right - cx - center_offset_v; 1752 drawRect->top += cy + infoPtr->uHItemPadding; 1753 } 1754 else if(lStyle & TCS_VERTICAL) 1755 { 1756 rcImage.top = drawRect->bottom - cy - center_offset_h; 1757 rcImage.left = drawRect->left + center_offset_v; 1758 drawRect->bottom -= cy + infoPtr->uHItemPadding; 1759 } 1760 else /* normal style, whether TCS_BOTTOM or not */ 1761 { 1762 rcImage.left = drawRect->left + center_offset_h; 1763 rcImage.top = drawRect->top + center_offset_v; 1764 drawRect->left += cx + infoPtr->uHItemPadding; 1765 } 1766 1767 TRACE("drawing image=%d, left=%d, top=%d\n", 1768 item->iImage, rcImage.left, rcImage.top-1); 1769 ImageList_Draw 1770 ( 1771 infoPtr->himl, 1772 item->iImage, 1773 hdc, 1774 rcImage.left, 1775 rcImage.top, 1776 ILD_NORMAL 1777 ); 1778 } 1779 1780 /* Now position text */ 1781 if (lStyle & TCS_FIXEDWIDTH && lStyle & TCS_FORCELABELLEFT) 1782 center_offset_h = infoPtr->uHItemPadding; 1783 else 1784 if(lStyle & TCS_VERTICAL) 1785 center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2; 1786 else 1787 center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2; 1788 1789 if(lStyle & TCS_VERTICAL) 1790 { 1791 if(lStyle & TCS_BOTTOM) 1792 drawRect->top+=center_offset_h; 1793 else 1794 drawRect->bottom-=center_offset_h; 1795 1796 center_offset_v = ((drawRect->right - drawRect->left) - (rcText.bottom - rcText.top)) / 2; 1797 } 1798 else 1799 { 1800 drawRect->left += center_offset_h; 1801 center_offset_v = ((drawRect->bottom - drawRect->top) - (rcText.bottom - rcText.top)) / 2; 1802 } 1803 1804 /* if an item is selected, the text is shifted up instead of down */ 1805 if (iItem == infoPtr->iSelected) 1806 center_offset_v -= infoPtr->uVItemPadding / 2; 1807 else 1808 center_offset_v += infoPtr->uVItemPadding / 2; 1809 1810 if (center_offset_v < 0) 1811 center_offset_v = 0; 1812 1813 if(lStyle & TCS_VERTICAL) 1814 drawRect->left += center_offset_v; 1815 else 1816 drawRect->top += center_offset_v; 1817 1818 /* Draw the text */ 1819 if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */ 1820 { 1821 static const WCHAR ArialW[] = { 'A','r','i','a','l',0 }; 1822 LOGFONTW logfont; 1823 HFONT hFont = 0; 1824 INT nEscapement = 900; 1825 INT nOrientation = 900; 1826 1827 if(lStyle & TCS_BOTTOM) 1828 { 1829 nEscapement = -900; 1830 nOrientation = -900; 1831 } 1832 1833 /* to get a font with the escapement and orientation we are looking for, we need to */ 1834 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */ 1835 if (!GetObjectW((infoPtr->hFont) ? 1836 infoPtr->hFont : GetStockObject(SYSTEM_FONT), 1837 sizeof(LOGFONTW),&logfont)) 1838 { 1839 INT iPointSize = 9; 1840 1841 lstrcpyW(logfont.lfFaceName, ArialW); 1842 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY), 1843 72); 1844 logfont.lfWeight = FW_NORMAL; 1845 logfont.lfItalic = 0; 1846 logfont.lfUnderline = 0; 1847 logfont.lfStrikeOut = 0; 1848 } 1849 1850 logfont.lfEscapement = nEscapement; 1851 logfont.lfOrientation = nOrientation; 1852 hFont = CreateFontIndirectW(&logfont); 1853 SelectObject(hdc, hFont); 1854 1855 if (item->pszText) 1856 { 1857 ExtTextOutW(hdc, 1858 (lStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left, 1859 (!(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top, 1860 ETO_CLIPPED, 1861 drawRect, 1862 item->pszText, 1863 lstrlenW(item->pszText), 1864 0); 1865 } 1866 1867 DeleteObject(hFont); 1868 } 1869 else 1870 { 1871 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%s), textlen=%d\n", 1872 debugstr_w(item->pszText), center_offset_h, center_offset_v, 1873 wine_dbgstr_rect(drawRect), (rcText.right-rcText.left)); 1874 if (item->pszText) 1875 { 1876 DrawTextW 1877 ( 1878 hdc, 1879 item->pszText, 1880 lstrlenW(item->pszText), 1881 drawRect, 1882 DT_LEFT | DT_SINGLELINE 1883 ); 1884 } 1885 } 1886 1887 *drawRect = rcTemp; /* restore drawRect */ 1888 } 1889 1890 /* 1891 * Cleanup 1892 */ 1893 SelectObject(hdc, hOldFont); 1894 SetBkMode(hdc, oldBkMode); 1895 SelectObject(hdc, holdPen); 1896 DeleteObject( htextPen ); 1897 } 1898 1899 /****************************************************************************** 1900 * TAB_DrawItem 1901 * 1902 * This method is used to draw a single tab into the tab control. 1903 */ 1904 static void TAB_DrawItem(const TAB_INFO *infoPtr, HDC hdc, INT iItem) 1905 { 1906 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE); 1907 RECT itemRect; 1908 RECT selectedRect; 1909 BOOL isVisible; 1910 RECT r, fillRect, r1; 1911 INT clRight = 0; 1912 INT clBottom = 0; 1913 COLORREF bkgnd, corner; 1914 HTHEME theme; 1915 1916 /* 1917 * Get the rectangle for the item. 1918 */ 1919 isVisible = TAB_InternalGetItemRect(infoPtr, 1920 iItem, 1921 &itemRect, 1922 &selectedRect); 1923 1924 if (isVisible) 1925 { 1926 RECT rUD, rC; 1927 1928 /* Clip UpDown control to not draw over it */ 1929 if (infoPtr->needsScrolling) 1930 { 1931 GetWindowRect(infoPtr->hwnd, &rC); 1932 GetWindowRect(infoPtr->hwndUpDown, &rUD); 1933 ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top); 1934 } 1935 1936 /* If you need to see what the control is doing, 1937 * then override these variables. They will change what 1938 * fill colors are used for filling the tabs, and the 1939 * corners when drawing the edge. 1940 */ 1941 bkgnd = comctl32_color.clrBtnFace; 1942 corner = comctl32_color.clrBtnFace; 1943 1944 if (lStyle & TCS_BUTTONS) 1945 { 1946 /* Get item rectangle */ 1947 r = itemRect; 1948 1949 /* Separators between flat buttons */ 1950 if (lStyle & TCS_FLATBUTTONS) 1951 { 1952 r1 = r; 1953 r1.right += (FLAT_BTN_SPACINGX -2); 1954 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT); 1955 } 1956 1957 if (iItem == infoPtr->iSelected) 1958 { 1959 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT); 1960 1961 OffsetRect(&r, 1, 1); 1962 } 1963 else /* ! selected */ 1964 { 1965 if (!(lStyle & TCS_FLATBUTTONS)) 1966 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT); 1967 } 1968 } 1969 else /* !TCS_BUTTONS */ 1970 { 1971 /* We draw a rectangle of different sizes depending on the selection 1972 * state. */ 1973 if (iItem == infoPtr->iSelected) { 1974 RECT rect; 1975 GetClientRect (infoPtr->hwnd, &rect); 1976 clRight = rect.right; 1977 clBottom = rect.bottom; 1978 r = selectedRect; 1979 } 1980 else 1981 r = itemRect; 1982 1983 /* 1984 * Erase the background. (Delay it but setup rectangle.) 1985 * This is necessary when drawing the selected item since it is larger 1986 * than the others, it might overlap with stuff already drawn by the 1987 * other tabs 1988 */ 1989 fillRect = r; 1990 1991 /* Draw themed tabs - but only if they are at the top. 1992 * Windows draws even side or bottom tabs themed, with wacky results. 1993 * However, since in Wine apps may get themed that did not opt in via 1994 * a manifest avoid theming when we know the result will be wrong */ 1995 if ((theme = GetWindowTheme (infoPtr->hwnd)) 1996 && ((lStyle & (TCS_VERTICAL | TCS_BOTTOM)) == 0)) 1997 { 1998 static const int partIds[8] = { 1999 /* Normal item */ 2000 TABP_TABITEM, 2001 TABP_TABITEMLEFTEDGE, 2002 TABP_TABITEMRIGHTEDGE, 2003 TABP_TABITEMBOTHEDGE, 2004 /* Selected tab */ 2005 TABP_TOPTABITEM, 2006 TABP_TOPTABITEMLEFTEDGE, 2007 TABP_TOPTABITEMRIGHTEDGE, 2008 TABP_TOPTABITEMBOTHEDGE, 2009 }; 2010 int partIndex = 0; 2011 int stateId = TIS_NORMAL; 2012 2013 /* selected and unselected tabs have different parts */ 2014 if (iItem == infoPtr->iSelected) 2015 partIndex += 4; 2016 /* The part also differs on the position of a tab on a line. 2017 * "Visually" determining the position works well enough. */ 2018 if(selectedRect.left == 0) 2019 partIndex += 1; 2020 if(selectedRect.right == clRight) 2021 partIndex += 2; 2022 2023 if (iItem == infoPtr->iSelected) 2024 stateId = TIS_SELECTED; 2025 else if (iItem == infoPtr->iHotTracked) 2026 stateId = TIS_HOT; 2027 else if (iItem == infoPtr->uFocus) 2028 stateId = TIS_FOCUSED; 2029 2030 /* Adjust rectangle for bottommost row */ 2031 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1) 2032 r.bottom += 3; 2033 2034 DrawThemeBackground (theme, hdc, partIds[partIndex], stateId, &r, NULL); 2035 GetThemeBackgroundContentRect (theme, hdc, partIds[partIndex], stateId, &r, &r); 2036 } 2037 else if(lStyle & TCS_VERTICAL) 2038 { 2039 /* These are for adjusting the drawing of a Selected tab */ 2040 /* The initial values are for the normal case of non-Selected */ 2041 int ZZ = 1; /* Do not stretch if selected */ 2042 if (iItem == infoPtr->iSelected) { 2043 ZZ = 0; 2044 2045 /* if leftmost draw the line longer */ 2046 if(selectedRect.top == 0) 2047 fillRect.top += CONTROL_BORDER_SIZEY; 2048 /* if rightmost draw the line longer */ 2049 if(selectedRect.bottom == clBottom) 2050 fillRect.bottom -= CONTROL_BORDER_SIZEY; 2051 } 2052 2053 if (lStyle & TCS_BOTTOM) 2054 { 2055 /* Adjust both rectangles to match native */ 2056 r.left += (1-ZZ); 2057 2058 TRACE("<right> item=%d, fill=(%s), edge=(%s)\n", 2059 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r)); 2060 2061 /* Clear interior */ 2062 SetBkColor(hdc, bkgnd); 2063 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0); 2064 2065 /* Draw rectangular edge around tab */ 2066 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM); 2067 2068 /* Now erase the top corner and draw diagonal edge */ 2069 SetBkColor(hdc, corner); 2070 r1.left = r.right - ROUND_CORNER_SIZE - 1; 2071 r1.top = r.top; 2072 r1.right = r.right; 2073 r1.bottom = r1.top + ROUND_CORNER_SIZE; 2074 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0); 2075 r1.right--; 2076 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT); 2077 2078 /* Now erase the bottom corner and draw diagonal edge */ 2079 r1.left = r.right - ROUND_CORNER_SIZE - 1; 2080 r1.bottom = r.bottom; 2081 r1.right = r.right; 2082 r1.top = r1.bottom - ROUND_CORNER_SIZE; 2083 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0); 2084 r1.right--; 2085 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT); 2086 2087 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) { 2088 r1 = r; 2089 r1.right = r1.left; 2090 r1.left--; 2091 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP); 2092 } 2093 2094 } 2095 else 2096 { 2097 TRACE("<left> item=%d, fill=(%s), edge=(%s)\n", 2098 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r)); 2099 2100 /* Clear interior */ 2101 SetBkColor(hdc, bkgnd); 2102 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0); 2103 2104 /* Draw rectangular edge around tab */ 2105 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM); 2106 2107 /* Now erase the top corner and draw diagonal edge */ 2108 SetBkColor(hdc, corner); 2109 r1.left = r.left; 2110 r1.top = r.top; 2111 r1.right = r1.left + ROUND_CORNER_SIZE + 1; 2112 r1.bottom = r1.top + ROUND_CORNER_SIZE; 2113 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0); 2114 r1.left++; 2115 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT); 2116 2117 /* Now erase the bottom corner and draw diagonal edge */ 2118 r1.left = r.left; 2119 r1.bottom = r.bottom; 2120 r1.right = r1.left + ROUND_CORNER_SIZE + 1; 2121 r1.top = r1.bottom - ROUND_CORNER_SIZE; 2122 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0); 2123 r1.left++; 2124 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT); 2125 } 2126 } 2127 else /* ! TCS_VERTICAL */ 2128 { 2129 /* These are for adjusting the drawing of a Selected tab */ 2130 /* The initial values are for the normal case of non-Selected */ 2131 if (iItem == infoPtr->iSelected) { 2132 /* if leftmost draw the line longer */ 2133 if(selectedRect.left == 0) 2134 fillRect.left += CONTROL_BORDER_SIZEX; 2135 /* if rightmost draw the line longer */ 2136 if(selectedRect.right == clRight) 2137 fillRect.right -= CONTROL_BORDER_SIZEX; 2138 } 2139 2140 if (lStyle & TCS_BOTTOM) 2141 { 2142 /* Adjust both rectangles for topmost row */ 2143 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1) 2144 { 2145 fillRect.top -= 2; 2146 r.top -= 1; 2147 } 2148 2149 TRACE("<bottom> item=%d, fill=(%s), edge=(%s)\n", 2150 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r)); 2151 2152 /* Clear interior */ 2153 SetBkColor(hdc, bkgnd); 2154 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0); 2155 2156 /* Draw rectangular edge around tab */ 2157 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT); 2158 2159 /* Now erase the righthand corner and draw diagonal edge */ 2160 SetBkColor(hdc, corner); 2161 r1.left = r.right - ROUND_CORNER_SIZE; 2162 r1.bottom = r.bottom; 2163 r1.right = r.right; 2164 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1; 2165 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0); 2166 r1.bottom--; 2167 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT); 2168 2169 /* Now erase the lefthand corner and draw diagonal edge */ 2170 r1.left = r.left; 2171 r1.bottom = r.bottom; 2172 r1.right = r1.left + ROUND_CORNER_SIZE; 2173 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1; 2174 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0); 2175 r1.bottom--; 2176 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT); 2177 2178 if (iItem == infoPtr->iSelected) 2179 { 2180 r.top += 2; 2181 r.left += 1; 2182 if (selectedRect.left == 0) 2183 { 2184 r1 = r; 2185 r1.bottom = r1.top; 2186 r1.top--; 2187 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT); 2188 } 2189 } 2190 2191 } 2192 else 2193 { 2194 /* Adjust both rectangles for bottommost row */ 2195 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1) 2196 { 2197 fillRect.bottom += 3; 2198 r.bottom += 2; 2199 } 2200 2201 TRACE("<top> item=%d, fill=(%s), edge=(%s)\n", 2202 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r)); 2203 2204 /* Clear interior */ 2205 SetBkColor(hdc, bkgnd); 2206 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0); 2207 2208 /* Draw rectangular edge around tab */ 2209 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT); 2210 2211 /* Now erase the righthand corner and draw diagonal edge */ 2212 SetBkColor(hdc, corner); 2213 r1.left = r.right - ROUND_CORNER_SIZE; 2214 r1.top = r.top; 2215 r1.right = r.right; 2216 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1; 2217 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0); 2218 r1.top++; 2219 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT); 2220 2221 /* Now erase the lefthand corner and draw diagonal edge */ 2222 r1.left = r.left; 2223 r1.top = r.top; 2224 r1.right = r1.left + ROUND_CORNER_SIZE; 2225 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1; 2226 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0); 2227 r1.top++; 2228 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT); 2229 } 2230 } 2231 } 2232 2233 TAB_DumpItemInternal(infoPtr, iItem); 2234 2235 /* This modifies r to be the text rectangle. */ 2236 TAB_DrawItemInterior(infoPtr, hdc, iItem, &r); 2237 } 2238 } 2239 2240 /****************************************************************************** 2241 * TAB_DrawBorder 2242 * 2243 * This method is used to draw the raised border around the tab control 2244 * "content" area. 2245 */ 2246 static void TAB_DrawBorder(const TAB_INFO *infoPtr, HDC hdc) 2247 { 2248 RECT rect; 2249 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE); 2250 HTHEME theme = GetWindowTheme (infoPtr->hwnd); 2251 2252 GetClientRect (infoPtr->hwnd, &rect); 2253 2254 /* 2255 * Adjust for the style 2256 */ 2257 2258 if (infoPtr->uNumItem) 2259 { 2260 if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL)) 2261 rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX; 2262 else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL)) 2263 rect.right -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX; 2264 else if(lStyle & TCS_VERTICAL) 2265 rect.left += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX; 2266 else /* not TCS_VERTICAL and not TCS_BOTTOM */ 2267 rect.top += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX; 2268 } 2269 2270 TRACE("border=(%s)\n", wine_dbgstr_rect(&rect)); 2271 2272 if (theme) 2273 DrawThemeBackground (theme, hdc, TABP_PANE, 0, &rect, NULL); 2274 else 2275 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT); 2276 } 2277 2278 /****************************************************************************** 2279 * TAB_Refresh 2280 * 2281 * This method repaints the tab control.. 2282 */ 2283 static void TAB_Refresh (TAB_INFO *infoPtr, HDC hdc) 2284 { 2285 HFONT hOldFont; 2286 INT i; 2287 2288 if (!infoPtr->DoRedraw) 2289 return; 2290 2291 hOldFont = SelectObject (hdc, infoPtr->hFont); 2292 2293 if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_BUTTONS) 2294 { 2295 for (i = 0; i < infoPtr->uNumItem; i++) 2296 TAB_DrawItem (infoPtr, hdc, i); 2297 } 2298 else 2299 { 2300 /* Draw all the non selected item first */ 2301 for (i = 0; i < infoPtr->uNumItem; i++) 2302 { 2303 if (i != infoPtr->iSelected) 2304 TAB_DrawItem (infoPtr, hdc, i); 2305 } 2306 2307 /* Now, draw the border, draw it before the selected item 2308 * since the selected item overwrites part of the border. */ 2309 TAB_DrawBorder (infoPtr, hdc); 2310 2311 /* Then, draw the selected item */ 2312 TAB_DrawItem (infoPtr, hdc, infoPtr->iSelected); 2313 } 2314 2315 SelectObject (hdc, hOldFont); 2316 } 2317 2318 static inline DWORD TAB_GetRowCount (const TAB_INFO *infoPtr) 2319 { 2320 return infoPtr->uNumRows; 2321 } 2322 2323 static inline LRESULT TAB_SetRedraw (TAB_INFO *infoPtr, BOOL doRedraw) 2324 { 2325 infoPtr->DoRedraw = doRedraw; 2326 return 0; 2327 } 2328 2329 /****************************************************************************** 2330 * TAB_EnsureSelectionVisible 2331 * 2332 * This method will make sure that the current selection is completely 2333 * visible by scrolling until it is. 2334 */ 2335 static void TAB_EnsureSelectionVisible( 2336 TAB_INFO* infoPtr) 2337 { 2338 INT iSelected = infoPtr->iSelected; 2339 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE); 2340 INT iOrigLeftmostVisible = infoPtr->leftmostVisible; 2341 2342 /* set the items row to the bottommost row or topmost row depending on 2343 * style */ 2344 if ((infoPtr->uNumRows > 1) && !(lStyle & TCS_BUTTONS)) 2345 { 2346 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected); 2347 INT newselected; 2348 INT iTargetRow; 2349 2350 if(lStyle & TCS_VERTICAL) 2351 newselected = selected->rect.left; 2352 else 2353 newselected = selected->rect.top; 2354 2355 /* the target row is always (number of rows - 1) 2356 as row 0 is furthest from the clientRect */ 2357 iTargetRow = infoPtr->uNumRows - 1; 2358 2359 if (newselected != iTargetRow) 2360 { 2361 UINT i; 2362 if(lStyle & TCS_VERTICAL) 2363 { 2364 for (i=0; i < infoPtr->uNumItem; i++) 2365 { 2366 /* move everything in the row of the selected item to the iTargetRow */ 2367 TAB_ITEM *item = TAB_GetItem(infoPtr, i); 2368 2369 if (item->rect.left == newselected ) 2370 item->rect.left = iTargetRow; 2371 else 2372 { 2373 if (item->rect.left > newselected) 2374 item->rect.left-=1; 2375 } 2376 } 2377 } 2378 else 2379 { 2380 for (i=0; i < infoPtr->uNumItem; i++) 2381 { 2382 TAB_ITEM *item = TAB_GetItem(infoPtr, i); 2383 2384 if (item->rect.top == newselected ) 2385 item->rect.top = iTargetRow; 2386 else 2387 { 2388 if (item->rect.top > newselected) 2389 item->rect.top-=1; 2390 } 2391 } 2392 } 2393 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL); 2394 } 2395 } 2396 2397 /* 2398 * Do the trivial cases first. 2399 */ 2400 if ( (!infoPtr->needsScrolling) || 2401 (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL)) 2402 return; 2403 2404 if (infoPtr->leftmostVisible >= iSelected) 2405 { 2406 infoPtr->leftmostVisible = iSelected; 2407 } 2408 else 2409 { 2410 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected); 2411 RECT r; 2412 INT width; 2413 UINT i; 2414 2415 /* Calculate the part of the client area that is visible */ 2416 GetClientRect(infoPtr->hwnd, &r); 2417 width = r.right; 2418 2419 GetClientRect(infoPtr->hwndUpDown, &r); 2420 width -= r.right; 2421 2422 if ((selected->rect.right - 2423 selected->rect.left) >= width ) 2424 { 2425 /* Special case: width of selected item is greater than visible 2426 * part of control. 2427 */ 2428 infoPtr->leftmostVisible = iSelected; 2429 } 2430 else 2431 { 2432 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++) 2433 { 2434 if ((selected->rect.right - TAB_GetItem(infoPtr, i)->rect.left) < width) 2435 break; 2436 } 2437 infoPtr->leftmostVisible = i; 2438 } 2439 } 2440 2441 if (infoPtr->leftmostVisible != iOrigLeftmostVisible) 2442 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL); 2443 2444 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0, 2445 MAKELONG(infoPtr->leftmostVisible, 0)); 2446 } 2447 2448 /****************************************************************************** 2449 * TAB_InvalidateTabArea 2450 * 2451 * This method will invalidate the portion of the control that contains the 2452 * tabs. It is called when the state of the control changes and needs 2453 * to be redisplayed 2454 */ 2455 static void TAB_InvalidateTabArea(const TAB_INFO *infoPtr) 2456 { 2457 RECT clientRect, rInvalidate, rAdjClient; 2458 DWORD lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE); 2459 INT lastRow = infoPtr->uNumRows - 1; 2460 RECT rect; 2461 2462 if (lastRow < 0) return; 2463 2464 GetClientRect(infoPtr->hwnd, &clientRect); 2465 rInvalidate = clientRect; 2466 rAdjClient = clientRect; 2467 2468 TAB_AdjustRect(infoPtr, 0, &rAdjClient); 2469 2470 TAB_InternalGetItemRect(infoPtr, infoPtr->uNumItem-1 , &rect, NULL); 2471 if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL)) 2472 { 2473 rInvalidate.left = rAdjClient.right; 2474 if (infoPtr->uNumRows == 1) 2475 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET; 2476 } 2477 else if(lStyle & TCS_VERTICAL) 2478 { 2479 rInvalidate.right = rAdjClient.left; 2480 if (infoPtr->uNumRows == 1) 2481 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET; 2482 } 2483 else if (lStyle & TCS_BOTTOM) 2484 { 2485 rInvalidate.top = rAdjClient.bottom; 2486 if (infoPtr->uNumRows == 1) 2487 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET; 2488 } 2489 else 2490 { 2491 rInvalidate.bottom = rAdjClient.top; 2492 if (infoPtr->uNumRows == 1) 2493 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET; 2494 } 2495 2496 /* Punch out the updown control */ 2497 if (infoPtr->needsScrolling && (rInvalidate.right > 0)) { 2498 RECT r; 2499 GetClientRect(infoPtr->hwndUpDown, &r); 2500 if (rInvalidate.right > clientRect.right - r.left) 2501 rInvalidate.right = rInvalidate.right - (r.right - r.left); 2502 else 2503 rInvalidate.right = clientRect.right - r.left; 2504 } 2505 2506 TRACE("invalidate (%s)\n", wine_dbgstr_rect(&rInvalidate)); 2507 2508 InvalidateRect(infoPtr->hwnd, &rInvalidate, TRUE); 2509 } 2510 2511 static inline LRESULT TAB_Paint (TAB_INFO *infoPtr, HDC hdcPaint) 2512 { 2513 HDC hdc; 2514 PAINTSTRUCT ps; 2515 2516 if (hdcPaint) 2517 hdc = hdcPaint; 2518 else 2519 { 2520 hdc = BeginPaint (infoPtr->hwnd, &ps); 2521 TRACE("erase %d, rect=(%s)\n", ps.fErase, wine_dbgstr_rect(&ps.rcPaint)); 2522 } 2523 2524 TAB_Refresh (infoPtr, hdc); 2525 2526 if (!hdcPaint) 2527 EndPaint (infoPtr->hwnd, &ps); 2528 2529 return 0; 2530 } 2531 2532 static LRESULT 2533 TAB_InsertItemT (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam, BOOL bUnicode) 2534 { 2535 TAB_ITEM *item; 2536 TCITEMW *pti; 2537 INT iItem; 2538 RECT rect; 2539 2540 GetClientRect (infoPtr->hwnd, &rect); 2541 TRACE("Rect: %p %s\n", infoPtr->hwnd, wine_dbgstr_rect(&rect)); 2542 2543 pti = (TCITEMW *)lParam; 2544 iItem = (INT)wParam; 2545 2546 if (iItem < 0) return -1; 2547 if (iItem > infoPtr->uNumItem) 2548 iItem = infoPtr->uNumItem; 2549 2550 TAB_DumpItemExternalT(pti, iItem, bUnicode); 2551 2552 2553 if (infoPtr->uNumItem == 0) { 2554 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr)); 2555 infoPtr->uNumItem++; 2556 infoPtr->iSelected = 0; 2557 } 2558 else { 2559 LPBYTE oldItems = (LPBYTE)infoPtr->items; 2560 2561 infoPtr->uNumItem++; 2562 infoPtr->items = Alloc (TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem); 2563 2564 /* pre insert copy */ 2565 if (iItem > 0) { 2566 memcpy (infoPtr->items, oldItems, 2567 iItem * TAB_ITEM_SIZE(infoPtr)); 2568 } 2569 2570 /* post insert copy */ 2571 if (iItem < infoPtr->uNumItem - 1) { 2572 memcpy (TAB_GetItem(infoPtr, iItem + 1), 2573 oldItems + iItem * TAB_ITEM_SIZE(infoPtr), 2574 (infoPtr->uNumItem - iItem - 1) * TAB_ITEM_SIZE(infoPtr)); 2575 2576 } 2577 2578 if (iItem <= infoPtr->iSelected) 2579 infoPtr->iSelected++; 2580 2581 Free (oldItems); 2582 } 2583 2584 item = TAB_GetItem(infoPtr, iItem); 2585 2586 item->pszText = NULL; 2587 2588 if (pti->mask & TCIF_TEXT) 2589 { 2590 if (bUnicode) 2591 Str_SetPtrW (&item->pszText, pti->pszText); 2592 else 2593 Str_SetPtrAtoW (&item->pszText, (LPSTR)pti->pszText); 2594 } 2595 2596 if (pti->mask & TCIF_IMAGE) 2597 item->iImage = pti->iImage; 2598 else 2599 item->iImage = -1; 2600 2601 if (pti->mask & TCIF_PARAM) 2602 memcpy(item->extra, &pti->lParam, infoPtr->cbInfo); 2603 else 2604 memset(item->extra, 0, infoPtr->cbInfo); 2605 2606 TAB_SetItemBounds(infoPtr); 2607 if (infoPtr->uNumItem > 1) 2608 TAB_InvalidateTabArea(infoPtr); 2609 else 2610 InvalidateRect(infoPtr->hwnd, NULL, TRUE); 2611 2612 TRACE("[%p]: added item %d %s\n", 2613 infoPtr->hwnd, iItem, debugstr_w(item->pszText)); 2614 2615 /* If we haven't set the current focus yet, set it now. */ 2616 if (infoPtr->uFocus == -1) 2617 TAB_SetCurFocus(infoPtr, iItem); 2618 2619 return iItem; 2620 } 2621 2622 static LRESULT 2623 TAB_SetItemSize (TAB_INFO *infoPtr, LPARAM lParam) 2624 { 2625 LONG lStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE); 2626 LONG lResult = 0; 2627 BOOL bNeedPaint = FALSE; 2628 2629 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight); 2630 2631 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */ 2632 if (lStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != (INT)LOWORD(lParam))) 2633 { 2634 infoPtr->tabWidth = (INT)LOWORD(lParam); 2635 bNeedPaint = TRUE; 2636 } 2637 2638 if (infoPtr->tabHeight != (INT)HIWORD(lParam)) 2639 { 2640 if ((infoPtr->fHeightSet = ((INT)HIWORD(lParam) != 0))) 2641 infoPtr->tabHeight = (INT)HIWORD(lParam); 2642 2643 bNeedPaint = TRUE; 2644 } 2645 TRACE("was h=%d,w=%d, now h=%d,w=%d\n", 2646 HIWORD(lResult), LOWORD(lResult), 2647 infoPtr->tabHeight, infoPtr->tabWidth); 2648 2649 if (bNeedPaint) 2650 { 2651 TAB_SetItemBounds(infoPtr); 2652 RedrawWindow(infoPtr->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW); 2653 } 2654 2655 return lResult; 2656 } 2657 2658 static inline LRESULT TAB_SetMinTabWidth (TAB_INFO *infoPtr, INT cx) 2659 { 2660 INT oldcx = 0; 2661 2662 TRACE("(%p,%d)\n", infoPtr, cx); 2663 2664 oldcx = infoPtr->tabMinWidth; 2665 infoPtr->tabMinWidth = cx; 2666 TAB_SetItemBounds(infoPtr); 2667 return oldcx; 2668 } 2669 2670 static inline LRESULT 2671 TAB_HighlightItem (TAB_INFO *infoPtr, INT iItem, BOOL fHighlight) 2672 { 2673 LPDWORD lpState; 2674 2675 TRACE("(%p,%d,%s)\n", infoPtr, iItem, fHighlight ? "true" : "false"); 2676 2677 if (!infoPtr || iItem < 0 || iItem >= infoPtr->uNumItem) 2678 return FALSE; 2679 2680 lpState = &TAB_GetItem(infoPtr, iItem)->dwState; 2681 2682 if (fHighlight) 2683 *lpState |= TCIS_HIGHLIGHTED; 2684 else 2685 *lpState &= ~TCIS_HIGHLIGHTED; 2686 2687 return TRUE; 2688 } 2689 2690 static LRESULT 2691 TAB_SetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode) 2692 { 2693 TAB_ITEM *wineItem; 2694 2695 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false"); 2696 2697 if (iItem < 0 || iItem >= infoPtr->uNumItem) 2698 return FALSE; 2699 2700 TAB_DumpItemExternalT(tabItem, iItem, bUnicode); 2701 2702 wineItem = TAB_GetItem(infoPtr, iItem); 2703 2704 if (tabItem->mask & TCIF_IMAGE) 2705 wineItem->iImage = tabItem->iImage; 2706 2707 if (tabItem->mask & TCIF_PARAM) 2708 memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo); 2709 2710 if (tabItem->mask & TCIF_RTLREADING) 2711 FIXME("TCIF_RTLREADING\n"); 2712 2713 if (tabItem->mask & TCIF_STATE) 2714 wineItem->dwState = tabItem->dwState; 2715 2716 if (tabItem->mask & TCIF_TEXT) 2717 { 2718 Free(wineItem->pszText); 2719 wineItem->pszText = NULL; 2720 if (bUnicode) 2721 Str_SetPtrW(&wineItem->pszText, tabItem->pszText); 2722 else 2723 Str_SetPtrAtoW(&wineItem->pszText, (LPSTR)tabItem->pszText); 2724 } 2725 2726 /* Update and repaint tabs */ 2727 TAB_SetItemBounds(infoPtr); 2728 TAB_InvalidateTabArea(infoPtr); 2729 2730 return TRUE; 2731 } 2732 2733 static inline LRESULT TAB_GetItemCount (const TAB_INFO *infoPtr) 2734 { 2735 return infoPtr->uNumItem; 2736 } 2737 2738 2739 static LRESULT 2740 TAB_GetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode) 2741 { 2742 TAB_ITEM *wineItem; 2743 2744 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false"); 2745 2746 if (iItem < 0 || iItem >= infoPtr->uNumItem) 2747 return FALSE; 2748 2749 wineItem = TAB_GetItem(infoPtr, iItem); 2750 2751 if (tabItem->mask & TCIF_IMAGE) 2752 tabItem->iImage = wineItem->iImage; 2753 2754 if (tabItem->mask & TCIF_PARAM) 2755 memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo); 2756 2757 if (tabItem->mask & TCIF_RTLREADING) 2758 FIXME("TCIF_RTLREADING\n"); 2759 2760 if (tabItem->mask & TCIF_STATE) 2761 tabItem->dwState = wineItem->dwState; 2762 2763 if (tabItem->mask & TCIF_TEXT) 2764 { 2765 if (bUnicode) 2766 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax); 2767 else 2768 Str_GetPtrWtoA (wineItem->pszText, (LPSTR)tabItem->pszText, tabItem->cchTextMax); 2769 } 2770 2771 TAB_DumpItemExternalT(tabItem, iItem, bUnicode); 2772 2773 return TRUE; 2774 } 2775 2776 2777 static LRESULT TAB_DeleteItem (TAB_INFO *infoPtr, INT iItem) 2778 { 2779 BOOL bResult = FALSE; 2780 2781 TRACE("(%p, %d)\n", infoPtr, iItem); 2782 2783 if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) 2784 { 2785 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem); 2786 LPBYTE oldItems = (LPBYTE)infoPtr->items; 2787 2788 TAB_InvalidateTabArea(infoPtr); 2789 Free(item->pszText); 2790 infoPtr->uNumItem--; 2791 2792 if (!infoPtr->uNumItem) 2793 { 2794 infoPtr->items = NULL; 2795 if (infoPtr->iHotTracked >= 0) 2796 { 2797 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER); 2798 infoPtr->iHotTracked = -1; 2799 } 2800 } 2801 else 2802 { 2803 infoPtr->items = Alloc(TAB_ITEM_SIZE(infoPtr) * infoPtr->uNumItem); 2804 2805 if (iItem > 0) 2806 memcpy(infoPtr->items, oldItems, iItem * TAB_ITEM_SIZE(infoPtr)); 2807 2808 if (iItem < infoPtr->uNumItem) 2809 memcpy(TAB_GetItem(infoPtr, iItem), 2810 oldItems + (iItem + 1) * TAB_ITEM_SIZE(infoPtr), 2811 (infoPtr->uNumItem - iItem) * TAB_ITEM_SIZE(infoPtr)); 2812 2813 if (iItem <= infoPtr->iHotTracked) 2814 { 2815 /* When tabs move left/up, the hot track item may change */ 2816 FIXME("Recalc hot track\n"); 2817 } 2818 } 2819 Free(oldItems); 2820 2821 /* Readjust the selected index */ 2822 if ((iItem == infoPtr->iSelected) && (iItem > 0)) 2823 infoPtr->iSelected--; 2824 2825 if (iItem < infoPtr->iSelected) 2826 infoPtr->iSelected--; 2827 2828 if (infoPtr->uNumItem == 0) 2829 infoPtr->iSelected = -1; 2830 2831 /* Reposition and repaint tabs */ 2832 TAB_SetItemBounds(infoPtr); 2833 2834 bResult = TRUE; 2835 } 2836 2837 return bResult; 2838 } 2839 2840 static inline LRESULT TAB_DeleteAllItems (TAB_INFO *infoPtr) 2841 { 2842 TRACE("(%p)\n", infoPtr); 2843 while (infoPtr->uNumItem) 2844 TAB_DeleteItem (infoPtr, 0); 2845 return TRUE; 2846 } 2847 2848 2849 static inline LRESULT TAB_GetFont (const TAB_INFO *infoPtr) 2850 { 2851 TRACE("(%p) returning %p\n", infoPtr, infoPtr->hFont); 2852 return (LRESULT)infoPtr->hFont; 2853 } 2854 2855 static inline LRESULT TAB_SetFont (TAB_INFO *infoPtr, HFONT hNewFont) 2856 { 2857 TRACE("(%p,%p)\n", infoPtr, hNewFont); 2858 2859 infoPtr->hFont = hNewFont; 2860 2861 TAB_SetItemBounds(infoPtr); 2862 2863 TAB_InvalidateTabArea(infoPtr); 2864 2865 return 0; 2866 } 2867 2868 2869 static inline LRESULT TAB_GetImageList (const TAB_INFO *infoPtr) 2870 { 2871 TRACE("\n"); 2872 return (LRESULT)infoPtr->himl; 2873 } 2874 2875 static inline LRESULT TAB_SetImageList (TAB_INFO *infoPtr, HIMAGELIST himlNew) 2876 { 2877 HIMAGELIST himlPrev = infoPtr->himl; 2878 TRACE("\n"); 2879 infoPtr->himl = himlNew; 2880 TAB_SetItemBounds(infoPtr); 2881 InvalidateRect(infoPtr->hwnd, NULL, TRUE); 2882 return (LRESULT)himlPrev; 2883 } 2884 2885 static inline LRESULT TAB_GetUnicodeFormat (const TAB_INFO *infoPtr) 2886 { 2887 return infoPtr->bUnicode; 2888 } 2889 2890 static inline LRESULT TAB_SetUnicodeFormat (TAB_INFO *infoPtr, BOOL bUnicode) 2891 { 2892 BOOL bTemp = infoPtr->bUnicode; 2893 2894 infoPtr->bUnicode = bUnicode; 2895 2896 return bTemp; 2897 } 2898 2899 static inline LRESULT TAB_Size (TAB_INFO *infoPtr) 2900 { 2901 /* I'm not really sure what the following code was meant to do. 2902 This is what it is doing: 2903 When WM_SIZE is sent with SIZE_RESTORED, the control 2904 gets positioned in the top left corner. 2905 2906 RECT parent_rect; 2907 HWND parent; 2908 UINT uPosFlags,cx,cy; 2909 2910 uPosFlags=0; 2911 if (!wParam) { 2912 parent = GetParent (hwnd); 2913 GetClientRect(parent, &parent_rect); 2914 cx=LOWORD (lParam); 2915 cy=HIWORD (lParam); 2916 if (GetWindowLongW(hwnd, GWL_STYLE) & CCS_NORESIZE) 2917 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE); 2918 2919 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top, 2920 cx, cy, uPosFlags | SWP_NOZORDER); 2921 } else { 2922 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam); 2923 } */ 2924 2925 /* Recompute the size/position of the tabs. */ 2926 TAB_SetItemBounds (infoPtr); 2927 2928 /* Force a repaint of the control. */ 2929 InvalidateRect(infoPtr->hwnd, NULL, TRUE); 2930 2931 return 0; 2932 } 2933 2934 2935 static LRESULT TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam) 2936 { 2937 TAB_INFO *infoPtr; 2938 TEXTMETRICW fontMetrics; 2939 HDC hdc; 2940 HFONT hOldFont; 2941 DWORD dwStyle; 2942 2943 infoPtr = (TAB_INFO *)Alloc (sizeof(TAB_INFO)); 2944 2945 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr); 2946 2947 infoPtr->hwnd = hwnd; 2948 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent; 2949 infoPtr->uNumItem = 0; 2950 infoPtr->uNumRows = 0; 2951 infoPtr->uHItemPadding = 6; 2952 infoPtr->uVItemPadding = 3; 2953 infoPtr->uHItemPadding_s = 6; 2954 infoPtr->uVItemPadding_s = 3; 2955 infoPtr->hFont = 0; 2956 infoPtr->items = 0; 2957 infoPtr->hcurArrow = LoadCursorW (0, (LPWSTR)IDC_ARROW); 2958 infoPtr->iSelected = -1; 2959 infoPtr->iHotTracked = -1; 2960 infoPtr->uFocus = -1; 2961 infoPtr->hwndToolTip = 0; 2962 infoPtr->DoRedraw = TRUE; 2963 infoPtr->needsScrolling = FALSE; 2964 infoPtr->hwndUpDown = 0; 2965 infoPtr->leftmostVisible = 0; 2966 infoPtr->fHeightSet = FALSE; 2967 infoPtr->bUnicode = IsWindowUnicode (hwnd); 2968 infoPtr->cbInfo = sizeof(LPARAM); 2969 2970 TRACE("Created tab control, hwnd [%p]\n", hwnd); 2971 2972 /* The tab control always has the WS_CLIPSIBLINGS style. Even 2973 if you don't specify it in CreateWindow. This is necessary in 2974 order for paint to work correctly. This follows windows behaviour. */ 2975 dwStyle = GetWindowLongW(hwnd, GWL_STYLE); 2976 SetWindowLongW(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS); 2977 2978 if (dwStyle & TCS_TOOLTIPS) { 2979 /* Create tooltip control */ 2980 infoPtr->hwndToolTip = 2981 CreateWindowExW (0, TOOLTIPS_CLASSW, NULL, WS_POPUP, 2982 CW_USEDEFAULT, CW_USEDEFAULT, 2983 CW_USEDEFAULT, CW_USEDEFAULT, 2984 hwnd, 0, 0, 0); 2985 2986 /* Send NM_TOOLTIPSCREATED notification */ 2987 if (infoPtr->hwndToolTip) { 2988 NMTOOLTIPSCREATED nmttc; 2989 2990 nmttc.hdr.hwndFrom = hwnd; 2991 nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID); 2992 nmttc.hdr.code = NM_TOOLTIPSCREATED; 2993 nmttc.hwndToolTips = infoPtr->hwndToolTip; 2994 2995 SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, 2996 (WPARAM)GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc); 2997 } 2998 } 2999 3000 OpenThemeData (infoPtr->hwnd, themeClass); 3001 3002 /* 3003 * We need to get text information so we need a DC and we need to select 3004 * a font. 3005 */ 3006 hdc = GetDC(hwnd); 3007 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT)); 3008 3009 /* Use the system font to determine the initial height of a tab. */ 3010 GetTextMetricsW(hdc, &fontMetrics); 3011 3012 /* 3013 * Make sure there is enough space for the letters + growing the 3014 * selected item + extra space for the selected item. 3015 */ 3016 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET + 3017 ((dwStyle & TCS_BUTTONS) ? 2 : 1) * 3018 infoPtr->uVItemPadding; 3019 3020 /* Initialize the width of a tab. */ 3021 if (dwStyle & TCS_FIXEDWIDTH) 3022 infoPtr->tabWidth = DEFAULT_TAB_WIDTH_FIXED; 3023 3024 infoPtr->tabMinWidth = -1; 3025 3026 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth); 3027 3028 SelectObject (hdc, hOldFont); 3029 ReleaseDC(hwnd, hdc); 3030 3031 return 0; 3032 } 3033 3034 static LRESULT 3035 TAB_Destroy (TAB_INFO *infoPtr) 3036 { 3037 UINT iItem; 3038 3039 if (!infoPtr) 3040 return 0; 3041 3042 SetWindowLongPtrW(infoPtr->hwnd, 0, 0); 3043 3044 if (infoPtr->items) { 3045 for (iItem = 0; iItem < infoPtr->uNumItem; iItem++) { 3046 Free (TAB_GetItem(infoPtr, iItem)->pszText); 3047 } 3048 Free (infoPtr->items); 3049 } 3050 3051 if (infoPtr->hwndToolTip) 3052 DestroyWindow (infoPtr->hwndToolTip); 3053 3054 if (infoPtr->hwndUpDown) 3055 DestroyWindow(infoPtr->hwndUpDown); 3056 3057 if (infoPtr->iHotTracked >= 0) 3058 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER); 3059 3060 CloseThemeData (GetWindowTheme (infoPtr->hwnd)); 3061 3062 Free (infoPtr); 3063 return 0; 3064 } 3065 3066 /* update theme after a WM_THEMECHANGED message */ 3067 static LRESULT theme_changed(const TAB_INFO *infoPtr) 3068 { 3069 HTHEME theme = GetWindowTheme (infoPtr->hwnd); 3070 CloseThemeData (theme); 3071 OpenThemeData (infoPtr->hwnd, themeClass); 3072 return 0; 3073 } 3074 3075 static LRESULT TAB_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam) 3076 { 3077 if (!wParam) 3078 return 0; 3079 return WVR_ALIGNTOP; 3080 } 3081 3082 static inline LRESULT 3083 TAB_SetItemExtra (TAB_INFO *infoPtr, INT cbInfo) 3084 { 3085 if (!infoPtr || cbInfo <= 0) 3086 return FALSE; 3087 3088 if (infoPtr->uNumItem) 3089 { 3090 /* FIXME: MSDN says this is not allowed, but this hasn't been verified */ 3091 return FALSE; 3092 } 3093 3094 infoPtr->cbInfo = cbInfo; 3095 return TRUE; 3096 } 3097 3098 static LRESULT WINAPI 3099 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 3100 { 3101 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd); 3102 3103 TRACE("hwnd=%p msg=%x wParam=%lx lParam=%lx\n", hwnd, uMsg, wParam, lParam); 3104 if (!infoPtr && (uMsg != WM_CREATE)) 3105 return DefWindowProcW (hwnd, uMsg, wParam, lParam); 3106 3107 switch (uMsg) 3108 { 3109 case TCM_GETIMAGELIST: 3110 return TAB_GetImageList (infoPtr); 3111 3112 case TCM_SETIMAGELIST: 3113 return TAB_SetImageList (infoPtr, (HIMAGELIST)lParam); 3114 3115 case TCM_GETITEMCOUNT: 3116 return TAB_GetItemCount (infoPtr); 3117 3118 case TCM_GETITEMA: 3119 case TCM_GETITEMW: 3120 return TAB_GetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_GETITEMW); 3121 3122 case TCM_SETITEMA: 3123 case TCM_SETITEMW: 3124 return TAB_SetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_SETITEMW); 3125 3126 case TCM_DELETEITEM: 3127 return TAB_DeleteItem (infoPtr, (INT)wParam); 3128 3129 case TCM_DELETEALLITEMS: 3130 return TAB_DeleteAllItems (infoPtr); 3131 3132 case TCM_GETITEMRECT: 3133 return TAB_GetItemRect (infoPtr, wParam, lParam); 3134 3135 case TCM_GETCURSEL: 3136 return TAB_GetCurSel (infoPtr); 3137 3138 case TCM_HITTEST: 3139 return TAB_HitTest (infoPtr, (LPTCHITTESTINFO)lParam); 3140 3141 case TCM_SETCURSEL: 3142 return TAB_SetCurSel (infoPtr, (INT)wParam); 3143 3144 case TCM_INSERTITEMA: 3145 case TCM_INSERTITEMW: 3146 return TAB_InsertItemT (infoPtr, wParam, lParam, uMsg == TCM_INSERTITEMW); 3147 3148 case TCM_SETITEMEXTRA: 3149 return TAB_SetItemExtra (infoPtr, (int)wParam); 3150 3151 case TCM_ADJUSTRECT: 3152 return TAB_AdjustRect (infoPtr, (BOOL)wParam, (LPRECT)lParam); 3153 3154 case TCM_SETITEMSIZE: 3155 return TAB_SetItemSize (infoPtr, lParam); 3156 3157 case TCM_REMOVEIMAGE: 3158 FIXME("Unimplemented msg TCM_REMOVEIMAGE\n"); 3159 return 0; 3160 3161 case TCM_SETPADDING: 3162 return TAB_SetPadding (infoPtr, lParam); 3163 3164 case TCM_GETROWCOUNT: 3165 return TAB_GetRowCount(infoPtr); 3166 3167 case TCM_GETUNICODEFORMAT: 3168 return TAB_GetUnicodeFormat (infoPtr); 3169 3170 case TCM_SETUNICODEFORMAT: 3171 return TAB_SetUnicodeFormat (infoPtr, (BOOL)wParam); 3172 3173 case TCM_HIGHLIGHTITEM: 3174 return TAB_HighlightItem (infoPtr, (INT)wParam, (BOOL)LOWORD(lParam)); 3175 3176 case TCM_GETTOOLTIPS: 3177 return TAB_GetToolTips (infoPtr); 3178 3179 case TCM_SETTOOLTIPS: 3180 return TAB_SetToolTips (infoPtr, (HWND)wParam); 3181 3182 case TCM_GETCURFOCUS: 3183 return TAB_GetCurFocus (infoPtr); 3184 3185 case TCM_SETCURFOCUS: 3186 return TAB_SetCurFocus (infoPtr, (INT)wParam); 3187 3188 case TCM_SETMINTABWIDTH: 3189 return TAB_SetMinTabWidth(infoPtr, (INT)lParam); 3190 3191 case TCM_DESELECTALL: 3192 FIXME("Unimplemented msg TCM_DESELECTALL\n"); 3193 return 0; 3194 3195 case TCM_GETEXTENDEDSTYLE: 3196 FIXME("Unimplemented msg TCM_GETEXTENDEDSTYLE\n"); 3197 return 0; 3198 3199 case TCM_SETEXTENDEDSTYLE: 3200 FIXME("Unimplemented msg TCM_SETEXTENDEDSTYLE\n"); 3201 return 0; 3202 3203 case WM_GETFONT: 3204 return TAB_GetFont (infoPtr); 3205 3206 case WM_SETFONT: 3207 return TAB_SetFont (infoPtr, (HFONT)wParam); 3208 3209 case WM_CREATE: 3210 return TAB_Create (hwnd, wParam, lParam); 3211 3212 case WM_NCDESTROY: 3213 return TAB_Destroy (infoPtr); 3214 3215 case WM_GETDLGCODE: 3216 return DLGC_WANTARROWS | DLGC_WANTCHARS; 3217 3218 case WM_LBUTTONDOWN: 3219 return TAB_LButtonDown (infoPtr, wParam, lParam); 3220 3221 case WM_LBUTTONUP: 3222 return TAB_LButtonUp (infoPtr); 3223 3224 case WM_NOTIFY: 3225 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam); 3226 3227 case WM_RBUTTONDOWN: 3228 return TAB_RButtonDown (infoPtr); 3229 3230 case WM_MOUSEMOVE: 3231 return TAB_MouseMove (infoPtr, wParam, lParam); 3232 3233 case WM_PRINTCLIENT: 3234 case WM_PAINT: 3235 return TAB_Paint (infoPtr, (HDC)wParam); 3236 3237 case WM_SIZE: 3238 return TAB_Size (infoPtr); 3239 3240 case WM_SETREDRAW: 3241 return TAB_SetRedraw (infoPtr, (BOOL)wParam); 3242 3243 case WM_HSCROLL: 3244 return TAB_OnHScroll(infoPtr, (int)LOWORD(wParam), (int)HIWORD(wParam), (HWND)lParam); 3245 3246 case WM_STYLECHANGED: 3247 TAB_SetItemBounds (infoPtr); 3248 InvalidateRect(hwnd, NULL, TRUE); 3249 return 0; 3250 3251 case WM_SYSCOLORCHANGE: 3252 COMCTL32_RefreshSysColors(); 3253 return 0; 3254 3255 case WM_THEMECHANGED: 3256 return theme_changed (infoPtr); 3257 3258 case WM_KILLFOCUS: 3259 case WM_SETFOCUS: 3260 TAB_FocusChanging(infoPtr); 3261 break; /* Don't disturb normal focus behavior */ 3262 3263 case WM_KEYUP: 3264 return TAB_KeyUp(infoPtr, wParam); 3265 case WM_NCHITTEST: 3266 return TAB_NCHitTest(infoPtr, lParam); 3267 3268 case WM_NCCALCSIZE: 3269 return TAB_NCCalcSize(hwnd, wParam, lParam); 3270 3271 default: 3272 if (uMsg >= WM_USER && uMsg < WM_APP) 3273 WARN("unknown msg %04x wp=%08lx lp=%08lx\n", 3274 uMsg, wParam, lParam); 3275 break; 3276 } 3277 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 3278 } 3279 3280 3281 void 3282 TAB_Register (void) 3283 { 3284 WNDCLASSW wndClass; 3285 3286 ZeroMemory (&wndClass, sizeof(WNDCLASSW)); 3287 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; 3288 wndClass.lpfnWndProc = TAB_WindowProc; 3289 wndClass.cbClsExtra = 0; 3290 wndClass.cbWndExtra = sizeof(TAB_INFO *); 3291 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW); 3292 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); 3293 wndClass.lpszClassName = WC_TABCONTROLW; 3294 3295 RegisterClassW (&wndClass); 3296 } 3297 3298 3299 void 3300 TAB_Unregister (void) 3301 { 3302 UnregisterClassW (WC_TABCONTROLW, NULL); 3303 } 3304