Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygentab.c
Go to the documentation of this file.
00001 /* 00002 * Tab control 00003 * 00004 * Copyright 1998 Anders Carlsson 00005 * Copyright 1999 Alex Priem <alexp@sci.kun.nl> 00006 * Copyright 1999 Francis Beaudet 00007 * Copyright 2003 Vitaliy Margolen 00008 * 00009 * This library is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU Lesser General Public 00011 * License as published by the Free Software Foundation; either 00012 * version 2.1 of the License, or (at your option) any later version. 00013 * 00014 * This library is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 * Lesser General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU Lesser General Public 00020 * License along with this library; if not, write to the Free Software 00021 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00022 * 00023 * NOTES 00024 * 00025 * This code was audited for completeness against the documented features 00026 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins. 00027 * 00028 * Unless otherwise noted, we believe this code to be complete, as per 00029 * the specification mentioned above. 00030 * If you discover missing features, or bugs, please note them below. 00031 * 00032 * TODO: 00033 * 00034 * Styles: 00035 * TCS_MULTISELECT - implement for VK_SPACE selection 00036 * TCS_RIGHT 00037 * TCS_RIGHTJUSTIFY 00038 * TCS_SCROLLOPPOSITE 00039 * TCS_SINGLELINE 00040 * TCIF_RTLREADING 00041 * 00042 * Extended Styles: 00043 * TCS_EX_REGISTERDROP 00044 * 00045 * Notifications: 00046 * NM_RELEASEDCAPTURE 00047 * TCN_FOCUSCHANGE 00048 * TCN_GETOBJECT 00049 * 00050 * Macros: 00051 * TabCtrl_AdjustRect 00052 * 00053 */ 00054 00055 #include <assert.h> 00056 #include <stdarg.h> 00057 #include <string.h> 00058 00059 #include "windef.h" 00060 #include "winbase.h" 00061 #include "wingdi.h" 00062 #include "winuser.h" 00063 #include "winnls.h" 00064 #include "commctrl.h" 00065 #include "comctl32.h" 00066 #include "uxtheme.h" 00067 #include "vssym32.h" 00068 #include "wine/debug.h" 00069 #include <math.h> 00070 00071 WINE_DEFAULT_DEBUG_CHANNEL(tab); 00072 00073 typedef struct 00074 { 00075 DWORD dwState; 00076 LPWSTR pszText; 00077 INT iImage; 00078 RECT rect; /* bounding rectangle of the item relative to the 00079 * leftmost item (the leftmost item, 0, would have a 00080 * "left" member of 0 in this rectangle) 00081 * 00082 * additionally the top member holds the row number 00083 * and bottom is unused and should be 0 */ 00084 BYTE extra[1]; /* Space for caller supplied info, variable size */ 00085 } TAB_ITEM; 00086 00087 /* The size of a tab item depends on how much extra data is requested. 00088 TCM_INSERTITEM always stores at least LPARAM sized data. */ 00089 #define EXTRA_ITEM_SIZE(infoPtr) (max((infoPtr)->cbInfo, sizeof(LPARAM))) 00090 #define TAB_ITEM_SIZE(infoPtr) FIELD_OFFSET(TAB_ITEM, extra[EXTRA_ITEM_SIZE(infoPtr)]) 00091 00092 typedef struct 00093 { 00094 HWND hwnd; /* Tab control window */ 00095 HWND hwndNotify; /* notification window (parent) */ 00096 UINT uNumItem; /* number of tab items */ 00097 UINT uNumRows; /* number of tab rows */ 00098 INT tabHeight; /* height of the tab row */ 00099 INT tabWidth; /* width of tabs */ 00100 INT tabMinWidth; /* minimum width of items */ 00101 USHORT uHItemPadding; /* amount of horizontal padding, in pixels */ 00102 USHORT uVItemPadding; /* amount of vertical padding, in pixels */ 00103 USHORT uHItemPadding_s; /* Set amount of horizontal padding, in pixels */ 00104 USHORT uVItemPadding_s; /* Set amount of vertical padding, in pixels */ 00105 HFONT hFont; /* handle to the current font */ 00106 HCURSOR hcurArrow; /* handle to the current cursor */ 00107 HIMAGELIST himl; /* handle to an image list (may be 0) */ 00108 HWND hwndToolTip; /* handle to tab's tooltip */ 00109 INT leftmostVisible; /* Used for scrolling, this member contains 00110 * the index of the first visible item */ 00111 INT iSelected; /* the currently selected item */ 00112 INT iHotTracked; /* the highlighted item under the mouse */ 00113 INT uFocus; /* item which has the focus */ 00114 BOOL DoRedraw; /* flag for redrawing when tab contents is changed*/ 00115 BOOL needsScrolling; /* TRUE if the size of the tabs is greater than 00116 * the size of the control */ 00117 BOOL fHeightSet; /* was the height of the tabs explicitly set? */ 00118 BOOL bUnicode; /* Unicode control? */ 00119 HWND hwndUpDown; /* Updown control used for scrolling */ 00120 INT cbInfo; /* Number of bytes of caller supplied info per tab */ 00121 00122 DWORD exStyle; /* Extended style used, currently: 00123 TCS_EX_FLATSEPARATORS, TCS_EX_REGISTERDROP */ 00124 DWORD dwStyle; /* the cached window GWL_STYLE */ 00125 00126 HDPA items; /* dynamic array of TAB_ITEM* pointers */ 00127 } TAB_INFO; 00128 00129 /****************************************************************************** 00130 * Positioning constants 00131 */ 00132 #define SELECTED_TAB_OFFSET 2 00133 #define ROUND_CORNER_SIZE 2 00134 #define DISPLAY_AREA_PADDINGX 2 00135 #define DISPLAY_AREA_PADDINGY 2 00136 #define CONTROL_BORDER_SIZEX 2 00137 #define CONTROL_BORDER_SIZEY 2 00138 #define BUTTON_SPACINGX 3 00139 #define BUTTON_SPACINGY 3 00140 #define FLAT_BTN_SPACINGX 8 00141 #define DEFAULT_MIN_TAB_WIDTH 54 00142 #define DEFAULT_PADDING_X 6 00143 #define EXTRA_ICON_PADDING 3 00144 00145 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongPtrW(hwnd,0)) 00146 00147 #define GET_DEFAULT_MIN_TAB_WIDTH(infoPtr) (DEFAULT_MIN_TAB_WIDTH - (DEFAULT_PADDING_X - (infoPtr)->uHItemPadding) * 2) 00148 00149 /****************************************************************************** 00150 * Hot-tracking timer constants 00151 */ 00152 #define TAB_HOTTRACK_TIMER 1 00153 #define TAB_HOTTRACK_TIMER_INTERVAL 100 /* milliseconds */ 00154 00155 static const WCHAR themeClass[] = { 'T','a','b',0 }; 00156 00157 static inline TAB_ITEM* TAB_GetItem(const TAB_INFO *infoPtr, INT i) 00158 { 00159 assert(i >= 0 && i < infoPtr->uNumItem); 00160 return DPA_GetPtr(infoPtr->items, i); 00161 } 00162 00163 /****************************************************************************** 00164 * Prototypes 00165 */ 00166 static void TAB_InvalidateTabArea(const TAB_INFO *); 00167 static void TAB_EnsureSelectionVisible(TAB_INFO *); 00168 static void TAB_DrawItemInterior(const TAB_INFO *, HDC, INT, RECT*); 00169 static LRESULT TAB_DeselectAll(TAB_INFO *, BOOL); 00170 static BOOL TAB_InternalGetItemRect(const TAB_INFO *, INT, RECT*, RECT*); 00171 00172 static BOOL 00173 TAB_SendSimpleNotify (const TAB_INFO *infoPtr, UINT code) 00174 { 00175 NMHDR nmhdr; 00176 00177 nmhdr.hwndFrom = infoPtr->hwnd; 00178 nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID); 00179 nmhdr.code = code; 00180 00181 return (BOOL) SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, 00182 nmhdr.idFrom, (LPARAM) &nmhdr); 00183 } 00184 00185 static void 00186 TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg, 00187 WPARAM wParam, LPARAM lParam) 00188 { 00189 MSG msg; 00190 00191 msg.hwnd = hwndMsg; 00192 msg.message = uMsg; 00193 msg.wParam = wParam; 00194 msg.lParam = lParam; 00195 msg.time = GetMessageTime (); 00196 msg.pt.x = (short)LOWORD(GetMessagePos ()); 00197 msg.pt.y = (short)HIWORD(GetMessagePos ()); 00198 00199 SendMessageW (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg); 00200 } 00201 00202 static void 00203 TAB_DumpItemExternalT(const TCITEMW *pti, UINT iItem, BOOL isW) 00204 { 00205 if (TRACE_ON(tab)) { 00206 TRACE("external tab %d, mask=0x%08x, dwState=0x%08x, dwStateMask=0x%08x, cchTextMax=0x%08x\n", 00207 iItem, pti->mask, pti->dwState, pti->dwStateMask, pti->cchTextMax); 00208 TRACE("external tab %d, iImage=%d, lParam=0x%08lx, pszTextW=%s\n", 00209 iItem, pti->iImage, pti->lParam, isW ? debugstr_w(pti->pszText) : debugstr_a((LPSTR)pti->pszText)); 00210 } 00211 } 00212 00213 static void 00214 TAB_DumpItemInternal(const TAB_INFO *infoPtr, UINT iItem) 00215 { 00216 if (TRACE_ON(tab)) { 00217 TAB_ITEM *ti = TAB_GetItem(infoPtr, iItem); 00218 00219 TRACE("tab %d, dwState=0x%08x, pszText=%s, iImage=%d\n", 00220 iItem, ti->dwState, debugstr_w(ti->pszText), ti->iImage); 00221 TRACE("tab %d, rect.left=%d, rect.top(row)=%d\n", 00222 iItem, ti->rect.left, ti->rect.top); 00223 } 00224 } 00225 00226 /* RETURNS 00227 * the index of the selected tab, or -1 if no tab is selected. */ 00228 static inline LRESULT TAB_GetCurSel (const TAB_INFO *infoPtr) 00229 { 00230 TRACE("(%p)\n", infoPtr); 00231 return infoPtr->iSelected; 00232 } 00233 00234 /* RETURNS 00235 * the index of the tab item that has the focus. */ 00236 static inline LRESULT 00237 TAB_GetCurFocus (const TAB_INFO *infoPtr) 00238 { 00239 TRACE("(%p)\n", infoPtr); 00240 return infoPtr->uFocus; 00241 } 00242 00243 static inline LRESULT TAB_GetToolTips (const TAB_INFO *infoPtr) 00244 { 00245 TRACE("(%p)\n", infoPtr); 00246 return (LRESULT)infoPtr->hwndToolTip; 00247 } 00248 00249 static inline LRESULT TAB_SetCurSel (TAB_INFO *infoPtr, INT iItem) 00250 { 00251 INT prevItem = infoPtr->iSelected; 00252 00253 TRACE("(%p %d)\n", infoPtr, iItem); 00254 00255 if (iItem < 0) 00256 infoPtr->iSelected = -1; 00257 else if (iItem >= infoPtr->uNumItem) 00258 return -1; 00259 else { 00260 if (prevItem != iItem) { 00261 if (prevItem != -1) 00262 TAB_GetItem(infoPtr, prevItem)->dwState &= ~TCIS_BUTTONPRESSED; 00263 TAB_GetItem(infoPtr, iItem)->dwState |= TCIS_BUTTONPRESSED; 00264 00265 infoPtr->iSelected = iItem; 00266 infoPtr->uFocus = iItem; 00267 TAB_EnsureSelectionVisible(infoPtr); 00268 TAB_InvalidateTabArea(infoPtr); 00269 } 00270 } 00271 return prevItem; 00272 } 00273 00274 static LRESULT TAB_SetCurFocus (TAB_INFO *infoPtr, INT iItem) 00275 { 00276 TRACE("(%p %d)\n", infoPtr, iItem); 00277 00278 if (iItem < 0) { 00279 infoPtr->uFocus = -1; 00280 if (infoPtr->iSelected != -1) { 00281 infoPtr->iSelected = -1; 00282 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE); 00283 TAB_InvalidateTabArea(infoPtr); 00284 } 00285 } 00286 else if (iItem < infoPtr->uNumItem) { 00287 if (infoPtr->dwStyle & TCS_BUTTONS) { 00288 /* set focus to new item, leave selection as is */ 00289 if (infoPtr->uFocus != iItem) { 00290 INT prev_focus = infoPtr->uFocus; 00291 RECT r; 00292 00293 infoPtr->uFocus = iItem; 00294 00295 if (prev_focus != infoPtr->iSelected) { 00296 if (TAB_InternalGetItemRect(infoPtr, prev_focus, &r, NULL)) 00297 InvalidateRect(infoPtr->hwnd, &r, FALSE); 00298 } 00299 00300 if (TAB_InternalGetItemRect(infoPtr, iItem, &r, NULL)) 00301 InvalidateRect(infoPtr->hwnd, &r, FALSE); 00302 00303 TAB_SendSimpleNotify(infoPtr, TCN_FOCUSCHANGE); 00304 } 00305 } else { 00306 INT oldFocus = infoPtr->uFocus; 00307 if (infoPtr->iSelected != iItem || oldFocus == -1 ) { 00308 infoPtr->uFocus = iItem; 00309 if (oldFocus != -1) { 00310 if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING)) { 00311 infoPtr->iSelected = iItem; 00312 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE); 00313 } 00314 else 00315 infoPtr->iSelected = iItem; 00316 TAB_EnsureSelectionVisible(infoPtr); 00317 TAB_InvalidateTabArea(infoPtr); 00318 } 00319 } 00320 } 00321 } 00322 return 0; 00323 } 00324 00325 static inline LRESULT 00326 TAB_SetToolTips (TAB_INFO *infoPtr, HWND hwndToolTip) 00327 { 00328 TRACE("%p %p\n", infoPtr, hwndToolTip); 00329 infoPtr->hwndToolTip = hwndToolTip; 00330 return 0; 00331 } 00332 00333 static inline LRESULT 00334 TAB_SetPadding (TAB_INFO *infoPtr, LPARAM lParam) 00335 { 00336 TRACE("(%p %d %d)\n", infoPtr, LOWORD(lParam), HIWORD(lParam)); 00337 infoPtr->uHItemPadding_s = LOWORD(lParam); 00338 infoPtr->uVItemPadding_s = HIWORD(lParam); 00339 00340 return 0; 00341 } 00342 00343 /****************************************************************************** 00344 * TAB_InternalGetItemRect 00345 * 00346 * This method will calculate the rectangle representing a given tab item in 00347 * client coordinates. This method takes scrolling into account. 00348 * 00349 * This method returns TRUE if the item is visible in the window and FALSE 00350 * if it is completely outside the client area. 00351 */ 00352 static BOOL TAB_InternalGetItemRect( 00353 const TAB_INFO* infoPtr, 00354 INT itemIndex, 00355 RECT* itemRect, 00356 RECT* selectedRect) 00357 { 00358 RECT tmpItemRect,clientRect; 00359 00360 /* Perform a sanity check and a trivial visibility check. */ 00361 if ( (infoPtr->uNumItem <= 0) || 00362 (itemIndex >= infoPtr->uNumItem) || 00363 (!(((infoPtr->dwStyle & TCS_MULTILINE) || (infoPtr->dwStyle & TCS_VERTICAL))) && 00364 (itemIndex < infoPtr->leftmostVisible))) 00365 { 00366 TRACE("Not Visible\n"); 00367 /* need to initialize these to empty rects */ 00368 if (itemRect) 00369 { 00370 memset(itemRect,0,sizeof(RECT)); 00371 itemRect->bottom = infoPtr->tabHeight; 00372 } 00373 if (selectedRect) 00374 memset(selectedRect,0,sizeof(RECT)); 00375 return FALSE; 00376 } 00377 00378 /* 00379 * Avoid special cases in this procedure by assigning the "out" 00380 * parameters if the caller didn't supply them 00381 */ 00382 if (itemRect == NULL) 00383 itemRect = &tmpItemRect; 00384 00385 /* Retrieve the unmodified item rect. */ 00386 *itemRect = TAB_GetItem(infoPtr,itemIndex)->rect; 00387 00388 /* calculate the times bottom and top based on the row */ 00389 GetClientRect(infoPtr->hwnd, &clientRect); 00390 00391 if ((infoPtr->dwStyle & TCS_BOTTOM) && (infoPtr->dwStyle & TCS_VERTICAL)) 00392 { 00393 itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * infoPtr->tabHeight - 00394 ((infoPtr->dwStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0); 00395 itemRect->left = itemRect->right - infoPtr->tabHeight; 00396 } 00397 else if (infoPtr->dwStyle & TCS_VERTICAL) 00398 { 00399 itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * infoPtr->tabHeight + 00400 ((infoPtr->dwStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGX : 0); 00401 itemRect->right = itemRect->left + infoPtr->tabHeight; 00402 } 00403 else if (infoPtr->dwStyle & TCS_BOTTOM) 00404 { 00405 itemRect->bottom = clientRect.bottom - itemRect->top * infoPtr->tabHeight - 00406 ((infoPtr->dwStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET); 00407 itemRect->top = itemRect->bottom - infoPtr->tabHeight; 00408 } 00409 else /* not TCS_BOTTOM and not TCS_VERTICAL */ 00410 { 00411 itemRect->top = clientRect.top + itemRect->top * infoPtr->tabHeight + 00412 ((infoPtr->dwStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : SELECTED_TAB_OFFSET); 00413 itemRect->bottom = itemRect->top + infoPtr->tabHeight; 00414 } 00415 00416 /* 00417 * "scroll" it to make sure the item at the very left of the 00418 * tab control is the leftmost visible tab. 00419 */ 00420 if(infoPtr->dwStyle & TCS_VERTICAL) 00421 { 00422 OffsetRect(itemRect, 00423 0, 00424 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.top); 00425 00426 /* 00427 * Move the rectangle so the first item is slightly offset from 00428 * the bottom of the tab control. 00429 */ 00430 OffsetRect(itemRect, 00431 0, 00432 SELECTED_TAB_OFFSET); 00433 00434 } else 00435 { 00436 OffsetRect(itemRect, 00437 -TAB_GetItem(infoPtr, infoPtr->leftmostVisible)->rect.left, 00438 0); 00439 00440 /* 00441 * Move the rectangle so the first item is slightly offset from 00442 * the left of the tab control. 00443 */ 00444 OffsetRect(itemRect, 00445 SELECTED_TAB_OFFSET, 00446 0); 00447 } 00448 TRACE("item %d tab h=%d, rect=(%s)\n", 00449 itemIndex, infoPtr->tabHeight, wine_dbgstr_rect(itemRect)); 00450 00451 /* Now, calculate the position of the item as if it were selected. */ 00452 if (selectedRect!=NULL) 00453 { 00454 CopyRect(selectedRect, itemRect); 00455 00456 /* The rectangle of a selected item is a bit wider. */ 00457 if(infoPtr->dwStyle & TCS_VERTICAL) 00458 InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET); 00459 else 00460 InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0); 00461 00462 /* If it also a bit higher. */ 00463 if ((infoPtr->dwStyle & TCS_BOTTOM) && (infoPtr->dwStyle & TCS_VERTICAL)) 00464 { 00465 selectedRect->left -= 2; /* the border is thicker on the right */ 00466 selectedRect->right += SELECTED_TAB_OFFSET; 00467 } 00468 else if (infoPtr->dwStyle & TCS_VERTICAL) 00469 { 00470 selectedRect->left -= SELECTED_TAB_OFFSET; 00471 selectedRect->right += 1; 00472 } 00473 else if (infoPtr->dwStyle & TCS_BOTTOM) 00474 { 00475 selectedRect->bottom += SELECTED_TAB_OFFSET; 00476 } 00477 else /* not TCS_BOTTOM and not TCS_VERTICAL */ 00478 { 00479 selectedRect->top -= SELECTED_TAB_OFFSET; 00480 selectedRect->bottom -= 1; 00481 } 00482 } 00483 00484 /* Check for visibility */ 00485 if (infoPtr->dwStyle & TCS_VERTICAL) 00486 return (itemRect->top < clientRect.bottom) && (itemRect->bottom > clientRect.top); 00487 else 00488 return (itemRect->left < clientRect.right) && (itemRect->right > clientRect.left); 00489 } 00490 00491 static inline BOOL 00492 TAB_GetItemRect(const TAB_INFO *infoPtr, INT item, RECT *rect) 00493 { 00494 TRACE("(%p, %d, %p)\n", infoPtr, item, rect); 00495 return TAB_InternalGetItemRect(infoPtr, item, rect, NULL); 00496 } 00497 00498 /****************************************************************************** 00499 * TAB_KeyDown 00500 * 00501 * This method is called to handle keyboard input 00502 */ 00503 static LRESULT TAB_KeyDown(TAB_INFO* infoPtr, WPARAM keyCode, LPARAM lParam) 00504 { 00505 INT newItem = -1; 00506 NMTCKEYDOWN nm; 00507 00508 /* TCN_KEYDOWN notification sent always */ 00509 nm.hdr.hwndFrom = infoPtr->hwnd; 00510 nm.hdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID); 00511 nm.hdr.code = TCN_KEYDOWN; 00512 nm.wVKey = keyCode; 00513 nm.flags = lParam; 00514 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nm.hdr.idFrom, (LPARAM)&nm); 00515 00516 switch (keyCode) 00517 { 00518 case VK_LEFT: 00519 newItem = infoPtr->uFocus - 1; 00520 break; 00521 case VK_RIGHT: 00522 newItem = infoPtr->uFocus + 1; 00523 break; 00524 } 00525 00526 /* If we changed to a valid item, change focused item */ 00527 if (newItem >= 0 && newItem < infoPtr->uNumItem && infoPtr->uFocus != newItem) 00528 TAB_SetCurFocus(infoPtr, newItem); 00529 00530 return 0; 00531 } 00532 00533 /* 00534 * WM_KILLFOCUS handler 00535 */ 00536 static void TAB_KillFocus(TAB_INFO *infoPtr) 00537 { 00538 /* clear current focused item back to selected for TCS_BUTTONS */ 00539 if ((infoPtr->dwStyle & TCS_BUTTONS) && (infoPtr->uFocus != infoPtr->iSelected)) 00540 { 00541 RECT r; 00542 00543 if (TAB_InternalGetItemRect(infoPtr, infoPtr->uFocus, &r, NULL)) 00544 InvalidateRect(infoPtr->hwnd, &r, FALSE); 00545 00546 infoPtr->uFocus = infoPtr->iSelected; 00547 } 00548 } 00549 00550 /****************************************************************************** 00551 * TAB_FocusChanging 00552 * 00553 * This method is called whenever the focus goes in or out of this control 00554 * it is used to update the visual state of the control. 00555 */ 00556 static void TAB_FocusChanging(const TAB_INFO *infoPtr) 00557 { 00558 RECT selectedRect; 00559 BOOL isVisible; 00560 00561 /* 00562 * Get the rectangle for the item. 00563 */ 00564 isVisible = TAB_InternalGetItemRect(infoPtr, 00565 infoPtr->uFocus, 00566 NULL, 00567 &selectedRect); 00568 00569 /* 00570 * If the rectangle is not completely invisible, invalidate that 00571 * portion of the window. 00572 */ 00573 if (isVisible) 00574 { 00575 TRACE("invalidate (%s)\n", wine_dbgstr_rect(&selectedRect)); 00576 InvalidateRect(infoPtr->hwnd, &selectedRect, TRUE); 00577 } 00578 } 00579 00580 static INT TAB_InternalHitTest (const TAB_INFO *infoPtr, POINT pt, UINT *flags) 00581 { 00582 RECT rect; 00583 INT iCount; 00584 00585 for (iCount = 0; iCount < infoPtr->uNumItem; iCount++) 00586 { 00587 TAB_InternalGetItemRect(infoPtr, iCount, &rect, NULL); 00588 00589 if (PtInRect(&rect, pt)) 00590 { 00591 *flags = TCHT_ONITEM; 00592 return iCount; 00593 } 00594 } 00595 00596 *flags = TCHT_NOWHERE; 00597 return -1; 00598 } 00599 00600 static inline LRESULT 00601 TAB_HitTest (const TAB_INFO *infoPtr, LPTCHITTESTINFO lptest) 00602 { 00603 TRACE("(%p, %p)\n", infoPtr, lptest); 00604 return TAB_InternalHitTest (infoPtr, lptest->pt, &lptest->flags); 00605 } 00606 00607 /****************************************************************************** 00608 * TAB_NCHitTest 00609 * 00610 * Napster v2b5 has a tab control for its main navigation which has a client 00611 * area that covers the whole area of the dialog pages. 00612 * That's why it receives all msgs for that area and the underlying dialog ctrls 00613 * are dead. 00614 * So I decided that we should handle WM_NCHITTEST here and return 00615 * HTTRANSPARENT if we don't hit the tab control buttons. 00616 * FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows 00617 * doesn't do it that way. Maybe depends on tab control styles ? 00618 */ 00619 static inline LRESULT 00620 TAB_NCHitTest (const TAB_INFO *infoPtr, LPARAM lParam) 00621 { 00622 POINT pt; 00623 UINT dummyflag; 00624 00625 pt.x = (short)LOWORD(lParam); 00626 pt.y = (short)HIWORD(lParam); 00627 ScreenToClient(infoPtr->hwnd, &pt); 00628 00629 if (TAB_InternalHitTest(infoPtr, pt, &dummyflag) == -1) 00630 return HTTRANSPARENT; 00631 else 00632 return HTCLIENT; 00633 } 00634 00635 static LRESULT 00636 TAB_LButtonDown (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 00637 { 00638 POINT pt; 00639 INT newItem; 00640 UINT dummy; 00641 00642 if (infoPtr->hwndToolTip) 00643 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd, 00644 WM_LBUTTONDOWN, wParam, lParam); 00645 00646 if (!(infoPtr->dwStyle & TCS_FOCUSNEVER)) { 00647 SetFocus (infoPtr->hwnd); 00648 } 00649 00650 if (infoPtr->hwndToolTip) 00651 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd, 00652 WM_LBUTTONDOWN, wParam, lParam); 00653 00654 pt.x = (short)LOWORD(lParam); 00655 pt.y = (short)HIWORD(lParam); 00656 00657 newItem = TAB_InternalHitTest (infoPtr, pt, &dummy); 00658 00659 TRACE("On Tab, item %d\n", newItem); 00660 00661 if ((newItem != -1) && (infoPtr->iSelected != newItem)) 00662 { 00663 if ((infoPtr->dwStyle & TCS_BUTTONS) && (infoPtr->dwStyle & TCS_MULTISELECT) && 00664 (wParam & MK_CONTROL)) 00665 { 00666 RECT r; 00667 00668 /* toggle multiselection */ 00669 TAB_GetItem(infoPtr, newItem)->dwState ^= TCIS_BUTTONPRESSED; 00670 if (TAB_InternalGetItemRect (infoPtr, newItem, &r, NULL)) 00671 InvalidateRect (infoPtr->hwnd, &r, TRUE); 00672 } 00673 else 00674 { 00675 INT i; 00676 BOOL pressed = FALSE; 00677 00678 /* any button pressed ? */ 00679 for (i = 0; i < infoPtr->uNumItem; i++) 00680 if ((TAB_GetItem (infoPtr, i)->dwState & TCIS_BUTTONPRESSED) && 00681 (infoPtr->iSelected != i)) 00682 { 00683 pressed = TRUE; 00684 break; 00685 } 00686 00687 if (TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING)) 00688 return 0; 00689 00690 if (pressed) 00691 TAB_DeselectAll (infoPtr, FALSE); 00692 else 00693 TAB_SetCurSel(infoPtr, newItem); 00694 00695 TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE); 00696 } 00697 } 00698 00699 return 0; 00700 } 00701 00702 static inline LRESULT 00703 TAB_LButtonUp (const TAB_INFO *infoPtr) 00704 { 00705 TAB_SendSimpleNotify(infoPtr, NM_CLICK); 00706 00707 return 0; 00708 } 00709 00710 static inline void 00711 TAB_RButtonUp (const TAB_INFO *infoPtr) 00712 { 00713 TAB_SendSimpleNotify(infoPtr, NM_RCLICK); 00714 } 00715 00716 /****************************************************************************** 00717 * TAB_DrawLoneItemInterior 00718 * 00719 * This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally 00720 * called by TAB_DrawItem which is normally called by TAB_Refresh which sets 00721 * up the device context and font. This routine does the same setup but 00722 * only calls TAB_DrawItemInterior for the single specified item. 00723 */ 00724 static void 00725 TAB_DrawLoneItemInterior(const TAB_INFO* infoPtr, int iItem) 00726 { 00727 HDC hdc = GetDC(infoPtr->hwnd); 00728 RECT r, rC; 00729 00730 /* Clip UpDown control to not draw over it */ 00731 if (infoPtr->needsScrolling) 00732 { 00733 GetWindowRect(infoPtr->hwnd, &rC); 00734 GetWindowRect(infoPtr->hwndUpDown, &r); 00735 ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top); 00736 } 00737 TAB_DrawItemInterior(infoPtr, hdc, iItem, NULL); 00738 ReleaseDC(infoPtr->hwnd, hdc); 00739 } 00740 00741 /* update a tab after hottracking - invalidate it or just redraw the interior, 00742 * based on whether theming is used or not */ 00743 static inline void hottrack_refresh(const TAB_INFO *infoPtr, int tabIndex) 00744 { 00745 if (tabIndex == -1) return; 00746 00747 if (GetWindowTheme (infoPtr->hwnd)) 00748 { 00749 RECT rect; 00750 TAB_InternalGetItemRect(infoPtr, tabIndex, &rect, NULL); 00751 InvalidateRect (infoPtr->hwnd, &rect, FALSE); 00752 } 00753 else 00754 TAB_DrawLoneItemInterior(infoPtr, tabIndex); 00755 } 00756 00757 /****************************************************************************** 00758 * TAB_HotTrackTimerProc 00759 * 00760 * When a mouse-move event causes a tab to be highlighted (hot-tracking), a 00761 * timer is setup so we can check if the mouse is moved out of our window. 00762 * (We don't get an event when the mouse leaves, the mouse-move events just 00763 * stop being delivered to our window and just start being delivered to 00764 * another window.) This function is called when the timer triggers so 00765 * we can check if the mouse has left our window. If so, we un-highlight 00766 * the hot-tracked tab. 00767 */ 00768 static void CALLBACK 00769 TAB_HotTrackTimerProc 00770 ( 00771 HWND hwnd, /* handle of window for timer messages */ 00772 UINT uMsg, /* WM_TIMER message */ 00773 UINT_PTR idEvent, /* timer identifier */ 00774 DWORD dwTime /* current system time */ 00775 ) 00776 { 00777 TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd); 00778 00779 if (infoPtr != NULL && infoPtr->iHotTracked >= 0) 00780 { 00781 POINT pt; 00782 00783 /* 00784 ** If we can't get the cursor position, or if the cursor is outside our 00785 ** window, we un-highlight the hot-tracked tab. Note that the cursor is 00786 ** "outside" even if it is within our bounding rect if another window 00787 ** overlaps. Note also that the case where the cursor stayed within our 00788 ** window but has moved off the hot-tracked tab will be handled by the 00789 ** WM_MOUSEMOVE event. 00790 */ 00791 if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd) 00792 { 00793 /* Redraw iHotTracked to look normal */ 00794 INT iRedraw = infoPtr->iHotTracked; 00795 infoPtr->iHotTracked = -1; 00796 hottrack_refresh (infoPtr, iRedraw); 00797 00798 /* Kill this timer */ 00799 KillTimer(hwnd, TAB_HOTTRACK_TIMER); 00800 } 00801 } 00802 } 00803 00804 /****************************************************************************** 00805 * TAB_RecalcHotTrack 00806 * 00807 * If a tab control has the TCS_HOTTRACK style, then the tab under the mouse 00808 * should be highlighted. This function determines which tab in a tab control, 00809 * if any, is under the mouse and records that information. The caller may 00810 * supply output parameters to receive the item number of the tab item which 00811 * was highlighted but isn't any longer and of the tab item which is now 00812 * highlighted but wasn't previously. The caller can use this information to 00813 * selectively redraw those tab items. 00814 * 00815 * If the caller has a mouse position, it can supply it through the pos 00816 * parameter. For example, TAB_MouseMove does this. Otherwise, the caller 00817 * supplies NULL and this function determines the current mouse position 00818 * itself. 00819 */ 00820 static void 00821 TAB_RecalcHotTrack 00822 ( 00823 TAB_INFO* infoPtr, 00824 const LPARAM* pos, 00825 int* out_redrawLeave, 00826 int* out_redrawEnter 00827 ) 00828 { 00829 int item = -1; 00830 00831 00832 if (out_redrawLeave != NULL) 00833 *out_redrawLeave = -1; 00834 if (out_redrawEnter != NULL) 00835 *out_redrawEnter = -1; 00836 00837 if ((infoPtr->dwStyle & TCS_HOTTRACK) || GetWindowTheme(infoPtr->hwnd)) 00838 { 00839 POINT pt; 00840 UINT flags; 00841 00842 if (pos == NULL) 00843 { 00844 GetCursorPos(&pt); 00845 ScreenToClient(infoPtr->hwnd, &pt); 00846 } 00847 else 00848 { 00849 pt.x = (short)LOWORD(*pos); 00850 pt.y = (short)HIWORD(*pos); 00851 } 00852 00853 item = TAB_InternalHitTest(infoPtr, pt, &flags); 00854 } 00855 00856 if (item != infoPtr->iHotTracked) 00857 { 00858 if (infoPtr->iHotTracked >= 0) 00859 { 00860 /* Mark currently hot-tracked to be redrawn to look normal */ 00861 if (out_redrawLeave != NULL) 00862 *out_redrawLeave = infoPtr->iHotTracked; 00863 00864 if (item < 0) 00865 { 00866 /* Kill timer which forces recheck of mouse pos */ 00867 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER); 00868 } 00869 } 00870 else 00871 { 00872 /* Start timer so we recheck mouse pos */ 00873 UINT timerID = SetTimer 00874 ( 00875 infoPtr->hwnd, 00876 TAB_HOTTRACK_TIMER, 00877 TAB_HOTTRACK_TIMER_INTERVAL, 00878 TAB_HotTrackTimerProc 00879 ); 00880 00881 if (timerID == 0) 00882 return; /* Hot tracking not available */ 00883 } 00884 00885 infoPtr->iHotTracked = item; 00886 00887 if (item >= 0) 00888 { 00889 /* Mark new hot-tracked to be redrawn to look highlighted */ 00890 if (out_redrawEnter != NULL) 00891 *out_redrawEnter = item; 00892 } 00893 } 00894 } 00895 00896 /****************************************************************************** 00897 * TAB_MouseMove 00898 * 00899 * Handles the mouse-move event. Updates tooltips. Updates hot-tracking. 00900 */ 00901 static LRESULT 00902 TAB_MouseMove (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 00903 { 00904 int redrawLeave; 00905 int redrawEnter; 00906 00907 if (infoPtr->hwndToolTip) 00908 TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd, 00909 WM_LBUTTONDOWN, wParam, lParam); 00910 00911 /* Determine which tab to highlight. Redraw tabs which change highlight 00912 ** status. */ 00913 TAB_RecalcHotTrack(infoPtr, &lParam, &redrawLeave, &redrawEnter); 00914 00915 hottrack_refresh (infoPtr, redrawLeave); 00916 hottrack_refresh (infoPtr, redrawEnter); 00917 00918 return 0; 00919 } 00920 00921 /****************************************************************************** 00922 * TAB_AdjustRect 00923 * 00924 * Calculates the tab control's display area given the window rectangle or 00925 * the window rectangle given the requested display rectangle. 00926 */ 00927 static LRESULT TAB_AdjustRect(const TAB_INFO *infoPtr, WPARAM fLarger, LPRECT prc) 00928 { 00929 LONG *iRightBottom, *iLeftTop; 00930 00931 TRACE ("hwnd=%p fLarger=%ld (%s)\n", infoPtr->hwnd, fLarger, 00932 wine_dbgstr_rect(prc)); 00933 00934 if (!prc) return -1; 00935 00936 if(infoPtr->dwStyle & TCS_VERTICAL) 00937 { 00938 iRightBottom = &(prc->right); 00939 iLeftTop = &(prc->left); 00940 } 00941 else 00942 { 00943 iRightBottom = &(prc->bottom); 00944 iLeftTop = &(prc->top); 00945 } 00946 00947 if (fLarger) /* Go from display rectangle */ 00948 { 00949 /* Add the height of the tabs. */ 00950 if (infoPtr->dwStyle & TCS_BOTTOM) 00951 *iRightBottom += infoPtr->tabHeight * infoPtr->uNumRows; 00952 else 00953 *iLeftTop -= infoPtr->tabHeight * infoPtr->uNumRows + 00954 ((infoPtr->dwStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0); 00955 00956 /* Inflate the rectangle for the padding */ 00957 InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY); 00958 00959 /* Inflate for the border */ 00960 InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEY); 00961 } 00962 else /* Go from window rectangle. */ 00963 { 00964 /* Deflate the rectangle for the border */ 00965 InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEY); 00966 00967 /* Deflate the rectangle for the padding */ 00968 InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY); 00969 00970 /* Remove the height of the tabs. */ 00971 if (infoPtr->dwStyle & TCS_BOTTOM) 00972 *iRightBottom -= infoPtr->tabHeight * infoPtr->uNumRows; 00973 else 00974 *iLeftTop += (infoPtr->tabHeight) * infoPtr->uNumRows + 00975 ((infoPtr->dwStyle & TCS_BUTTONS)? 3 * (infoPtr->uNumRows - 1) : 0); 00976 } 00977 00978 return 0; 00979 } 00980 00981 /****************************************************************************** 00982 * TAB_OnHScroll 00983 * 00984 * This method will handle the notification from the scroll control and 00985 * perform the scrolling operation on the tab control. 00986 */ 00987 static LRESULT TAB_OnHScroll(TAB_INFO *infoPtr, int nScrollCode, int nPos) 00988 { 00989 if(nScrollCode == SB_THUMBPOSITION && nPos != infoPtr->leftmostVisible) 00990 { 00991 if(nPos < infoPtr->leftmostVisible) 00992 infoPtr->leftmostVisible--; 00993 else 00994 infoPtr->leftmostVisible++; 00995 00996 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL); 00997 TAB_InvalidateTabArea(infoPtr); 00998 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0, 00999 MAKELONG(infoPtr->leftmostVisible, 0)); 01000 } 01001 01002 return 0; 01003 } 01004 01005 /****************************************************************************** 01006 * TAB_SetupScrolling 01007 * 01008 * This method will check the current scrolling state and make sure the 01009 * scrolling control is displayed (or not). 01010 */ 01011 static void TAB_SetupScrolling( 01012 TAB_INFO* infoPtr, 01013 const RECT* clientRect) 01014 { 01015 static const WCHAR emptyW[] = { 0 }; 01016 INT maxRange = 0; 01017 01018 if (infoPtr->needsScrolling) 01019 { 01020 RECT controlPos; 01021 INT vsize, tabwidth; 01022 01023 /* 01024 * Calculate the position of the scroll control. 01025 */ 01026 if(infoPtr->dwStyle & TCS_VERTICAL) 01027 { 01028 controlPos.right = clientRect->right; 01029 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL); 01030 01031 if (infoPtr->dwStyle & TCS_BOTTOM) 01032 { 01033 controlPos.top = clientRect->bottom - infoPtr->tabHeight; 01034 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL); 01035 } 01036 else 01037 { 01038 controlPos.bottom = clientRect->top + infoPtr->tabHeight; 01039 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL); 01040 } 01041 } 01042 else 01043 { 01044 controlPos.right = clientRect->right; 01045 controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL); 01046 01047 if (infoPtr->dwStyle & TCS_BOTTOM) 01048 { 01049 controlPos.top = clientRect->bottom - infoPtr->tabHeight; 01050 controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL); 01051 } 01052 else 01053 { 01054 controlPos.bottom = clientRect->top + infoPtr->tabHeight; 01055 controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL); 01056 } 01057 } 01058 01059 /* 01060 * If we don't have a scroll control yet, we want to create one. 01061 * If we have one, we want to make sure it's positioned properly. 01062 */ 01063 if (infoPtr->hwndUpDown==0) 01064 { 01065 infoPtr->hwndUpDown = CreateWindowW(UPDOWN_CLASSW, emptyW, 01066 WS_VISIBLE | WS_CHILD | UDS_HORZ, 01067 controlPos.left, controlPos.top, 01068 controlPos.right - controlPos.left, 01069 controlPos.bottom - controlPos.top, 01070 infoPtr->hwnd, NULL, NULL, NULL); 01071 } 01072 else 01073 { 01074 SetWindowPos(infoPtr->hwndUpDown, 01075 NULL, 01076 controlPos.left, controlPos.top, 01077 controlPos.right - controlPos.left, 01078 controlPos.bottom - controlPos.top, 01079 SWP_SHOWWINDOW | SWP_NOZORDER); 01080 } 01081 01082 /* Now calculate upper limit of the updown control range. 01083 * We do this by calculating how many tabs will be offscreen when the 01084 * last tab is visible. 01085 */ 01086 if(infoPtr->uNumItem) 01087 { 01088 vsize = clientRect->right - (controlPos.right - controlPos.left + 1); 01089 maxRange = infoPtr->uNumItem; 01090 tabwidth = TAB_GetItem(infoPtr, infoPtr->uNumItem - 1)->rect.right; 01091 01092 for(; maxRange > 0; maxRange--) 01093 { 01094 if(tabwidth - TAB_GetItem(infoPtr,maxRange - 1)->rect.left > vsize) 01095 break; 01096 } 01097 01098 if(maxRange == infoPtr->uNumItem) 01099 maxRange--; 01100 } 01101 } 01102 else 01103 { 01104 /* If we once had a scroll control... hide it */ 01105 if (infoPtr->hwndUpDown) 01106 ShowWindow(infoPtr->hwndUpDown, SW_HIDE); 01107 } 01108 if (infoPtr->hwndUpDown) 01109 SendMessageW(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange); 01110 } 01111 01112 /****************************************************************************** 01113 * TAB_SetItemBounds 01114 * 01115 * This method will calculate the position rectangles of all the items in the 01116 * control. The rectangle calculated starts at 0 for the first item in the 01117 * list and ignores scrolling and selection. 01118 * It also uses the current font to determine the height of the tab row and 01119 * it checks if all the tabs fit in the client area of the window. If they 01120 * don't, a scrolling control is added. 01121 */ 01122 static void TAB_SetItemBounds (TAB_INFO *infoPtr) 01123 { 01124 TEXTMETRICW fontMetrics; 01125 UINT curItem; 01126 INT curItemLeftPos; 01127 INT curItemRowCount; 01128 HFONT hFont, hOldFont; 01129 HDC hdc; 01130 RECT clientRect; 01131 INT iTemp; 01132 RECT* rcItem; 01133 INT iIndex; 01134 INT icon_width = 0; 01135 01136 /* 01137 * We need to get text information so we need a DC and we need to select 01138 * a font. 01139 */ 01140 hdc = GetDC(infoPtr->hwnd); 01141 01142 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT); 01143 hOldFont = SelectObject (hdc, hFont); 01144 01145 /* 01146 * We will base the rectangle calculations on the client rectangle 01147 * of the control. 01148 */ 01149 GetClientRect(infoPtr->hwnd, &clientRect); 01150 01151 /* if TCS_VERTICAL then swap the height and width so this code places the 01152 tabs along the top of the rectangle and we can just rotate them after 01153 rather than duplicate all of the below code */ 01154 if(infoPtr->dwStyle & TCS_VERTICAL) 01155 { 01156 iTemp = clientRect.bottom; 01157 clientRect.bottom = clientRect.right; 01158 clientRect.right = iTemp; 01159 } 01160 01161 /* Now use hPadding and vPadding */ 01162 infoPtr->uHItemPadding = infoPtr->uHItemPadding_s; 01163 infoPtr->uVItemPadding = infoPtr->uVItemPadding_s; 01164 01165 /* The leftmost item will be "0" aligned */ 01166 curItemLeftPos = 0; 01167 curItemRowCount = infoPtr->uNumItem ? 1 : 0; 01168 01169 if (!(infoPtr->fHeightSet)) 01170 { 01171 int item_height; 01172 INT icon_height = 0, cx; 01173 01174 /* Use the current font to determine the height of a tab. */ 01175 GetTextMetricsW(hdc, &fontMetrics); 01176 01177 /* Get the icon height */ 01178 if (infoPtr->himl) 01179 ImageList_GetIconSize(infoPtr->himl, &cx, &icon_height); 01180 01181 /* Take the highest between font or icon */ 01182 if (fontMetrics.tmHeight > icon_height) 01183 item_height = fontMetrics.tmHeight + 2; 01184 else 01185 item_height = icon_height; 01186 01187 /* 01188 * Make sure there is enough space for the letters + icon + growing the 01189 * selected item + extra space for the selected item. 01190 */ 01191 infoPtr->tabHeight = item_height + 01192 ((infoPtr->dwStyle & TCS_BUTTONS) ? 2 : 1) * 01193 infoPtr->uVItemPadding; 01194 01195 TRACE("tabH=%d, tmH=%d, iconh=%d\n", 01196 infoPtr->tabHeight, fontMetrics.tmHeight, icon_height); 01197 } 01198 01199 TRACE("client right=%d\n", clientRect.right); 01200 01201 /* Get the icon width */ 01202 if (infoPtr->himl) 01203 { 01204 INT cy; 01205 01206 ImageList_GetIconSize(infoPtr->himl, &icon_width, &cy); 01207 01208 if (infoPtr->dwStyle & TCS_FIXEDWIDTH) 01209 icon_width += 4; 01210 else 01211 /* Add padding if icon is present */ 01212 icon_width += infoPtr->uHItemPadding; 01213 } 01214 01215 for (curItem = 0; curItem < infoPtr->uNumItem; curItem++) 01216 { 01217 TAB_ITEM *curr = TAB_GetItem(infoPtr, curItem); 01218 01219 /* Set the leftmost position of the tab. */ 01220 curr->rect.left = curItemLeftPos; 01221 01222 if (infoPtr->dwStyle & TCS_FIXEDWIDTH) 01223 { 01224 curr->rect.right = curr->rect.left + 01225 max(infoPtr->tabWidth, icon_width); 01226 } 01227 else if (!curr->pszText) 01228 { 01229 /* If no text use minimum tab width including padding. */ 01230 if (infoPtr->tabMinWidth < 0) 01231 curr->rect.right = curr->rect.left + GET_DEFAULT_MIN_TAB_WIDTH(infoPtr); 01232 else 01233 { 01234 curr->rect.right = curr->rect.left + infoPtr->tabMinWidth; 01235 01236 /* Add extra padding if icon is present */ 01237 if (infoPtr->himl && infoPtr->tabMinWidth > 0 && infoPtr->tabMinWidth < DEFAULT_MIN_TAB_WIDTH 01238 && infoPtr->uHItemPadding > 1) 01239 curr->rect.right += EXTRA_ICON_PADDING * (infoPtr->uHItemPadding-1); 01240 } 01241 } 01242 else 01243 { 01244 int tabwidth; 01245 SIZE size; 01246 /* Calculate how wide the tab is depending on the text it contains */ 01247 GetTextExtentPoint32W(hdc, curr->pszText, 01248 lstrlenW(curr->pszText), &size); 01249 01250 tabwidth = size.cx + icon_width + 2 * infoPtr->uHItemPadding; 01251 01252 if (infoPtr->tabMinWidth < 0) 01253 tabwidth = max(tabwidth, GET_DEFAULT_MIN_TAB_WIDTH(infoPtr)); 01254 else 01255 tabwidth = max(tabwidth, infoPtr->tabMinWidth); 01256 01257 curr->rect.right = curr->rect.left + tabwidth; 01258 TRACE("for <%s>, l,r=%d,%d\n", 01259 debugstr_w(curr->pszText), curr->rect.left, curr->rect.right); 01260 } 01261 01262 /* 01263 * Check if this is a multiline tab control and if so 01264 * check to see if we should wrap the tabs 01265 * 01266 * Wrap all these tabs. We will arrange them evenly later. 01267 * 01268 */ 01269 01270 if (((infoPtr->dwStyle & TCS_MULTILINE) || (infoPtr->dwStyle & TCS_VERTICAL)) && 01271 (curr->rect.right > 01272 (clientRect.right - CONTROL_BORDER_SIZEX - DISPLAY_AREA_PADDINGX))) 01273 { 01274 curr->rect.right -= curr->rect.left; 01275 01276 curr->rect.left = 0; 01277 curItemRowCount++; 01278 TRACE("wrapping <%s>, l,r=%d,%d\n", debugstr_w(curr->pszText), 01279 curr->rect.left, curr->rect.right); 01280 } 01281 01282 curr->rect.bottom = 0; 01283 curr->rect.top = curItemRowCount - 1; 01284 01285 TRACE("Rect: %s\n", wine_dbgstr_rect(&curr->rect)); 01286 01287 /* 01288 * The leftmost position of the next item is the rightmost position 01289 * of this one. 01290 */ 01291 if (infoPtr->dwStyle & TCS_BUTTONS) 01292 { 01293 curItemLeftPos = curr->rect.right + BUTTON_SPACINGX; 01294 if (infoPtr->dwStyle & TCS_FLATBUTTONS) 01295 curItemLeftPos += FLAT_BTN_SPACINGX; 01296 } 01297 else 01298 curItemLeftPos = curr->rect.right; 01299 } 01300 01301 if (!((infoPtr->dwStyle & TCS_MULTILINE) || (infoPtr->dwStyle & TCS_VERTICAL))) 01302 { 01303 /* 01304 * Check if we need a scrolling control. 01305 */ 01306 infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) > 01307 clientRect.right); 01308 01309 /* Don't need scrolling, then update infoPtr->leftmostVisible */ 01310 if(!infoPtr->needsScrolling) 01311 infoPtr->leftmostVisible = 0; 01312 } 01313 else 01314 { 01315 /* 01316 * No scrolling in Multiline or Vertical styles. 01317 */ 01318 infoPtr->needsScrolling = FALSE; 01319 infoPtr->leftmostVisible = 0; 01320 } 01321 TAB_SetupScrolling(infoPtr, &clientRect); 01322 01323 /* Set the number of rows */ 01324 infoPtr->uNumRows = curItemRowCount; 01325 01326 /* Arrange all tabs evenly if style says so */ 01327 if (!(infoPtr->dwStyle & TCS_RAGGEDRIGHT) && 01328 ((infoPtr->dwStyle & TCS_MULTILINE) || (infoPtr->dwStyle & TCS_VERTICAL)) && 01329 (infoPtr->uNumItem > 0) && 01330 (infoPtr->uNumRows > 1)) 01331 { 01332 INT tabPerRow,remTab,iRow; 01333 UINT iItm; 01334 INT iCount=0; 01335 01336 /* 01337 * Ok windows tries to even out the rows. place the same 01338 * number of tabs in each row. So lets give that a shot 01339 */ 01340 01341 tabPerRow = infoPtr->uNumItem / (infoPtr->uNumRows); 01342 remTab = infoPtr->uNumItem % (infoPtr->uNumRows); 01343 01344 for (iItm=0,iRow=0,iCount=0,curItemLeftPos=0; 01345 iItm<infoPtr->uNumItem; 01346 iItm++,iCount++) 01347 { 01348 /* normalize the current rect */ 01349 TAB_ITEM *curr = TAB_GetItem(infoPtr, iItm); 01350 01351 /* shift the item to the left side of the clientRect */ 01352 curr->rect.right -= curr->rect.left; 01353 curr->rect.left = 0; 01354 01355 TRACE("r=%d, cl=%d, cl.r=%d, iCount=%d, iRow=%d, uNumRows=%d, remTab=%d, tabPerRow=%d\n", 01356 curr->rect.right, curItemLeftPos, clientRect.right, 01357 iCount, iRow, infoPtr->uNumRows, remTab, tabPerRow); 01358 01359 /* if we have reached the maximum number of tabs on this row */ 01360 /* move to the next row, reset our current item left position and */ 01361 /* the count of items on this row */ 01362 01363 if (infoPtr->dwStyle & TCS_VERTICAL) { 01364 /* Vert: Add the remaining tabs in the *last* remainder rows */ 01365 if (iCount >= ((iRow>=(INT)infoPtr->uNumRows - remTab)?tabPerRow + 1:tabPerRow)) { 01366 iRow++; 01367 curItemLeftPos = 0; 01368 iCount = 0; 01369 } 01370 } else { 01371 /* Horz: Add the remaining tabs in the *first* remainder rows */ 01372 if (iCount >= ((iRow<remTab)?tabPerRow + 1:tabPerRow)) { 01373 iRow++; 01374 curItemLeftPos = 0; 01375 iCount = 0; 01376 } 01377 } 01378 01379 /* shift the item to the right to place it as the next item in this row */ 01380 curr->rect.left += curItemLeftPos; 01381 curr->rect.right += curItemLeftPos; 01382 curr->rect.top = iRow; 01383 if (infoPtr->dwStyle & TCS_BUTTONS) 01384 { 01385 curItemLeftPos = curr->rect.right + 1; 01386 if (infoPtr->dwStyle & TCS_FLATBUTTONS) 01387 curItemLeftPos += FLAT_BTN_SPACINGX; 01388 } 01389 else 01390 curItemLeftPos = curr->rect.right; 01391 01392 TRACE("arranging <%s>, l,r=%d,%d, row=%d\n", 01393 debugstr_w(curr->pszText), curr->rect.left, 01394 curr->rect.right, curr->rect.top); 01395 } 01396 01397 /* 01398 * Justify the rows 01399 */ 01400 { 01401 INT widthDiff, iIndexStart=0, iIndexEnd=0; 01402 INT remainder; 01403 INT iCount=0; 01404 01405 while(iIndexStart < infoPtr->uNumItem) 01406 { 01407 TAB_ITEM *start = TAB_GetItem(infoPtr, iIndexStart); 01408 01409 /* 01410 * find the index of the row 01411 */ 01412 /* find the first item on the next row */ 01413 for (iIndexEnd=iIndexStart; 01414 (iIndexEnd < infoPtr->uNumItem) && 01415 (TAB_GetItem(infoPtr, iIndexEnd)->rect.top == 01416 start->rect.top) ; 01417 iIndexEnd++) 01418 /* intentionally blank */; 01419 01420 /* 01421 * we need to justify these tabs so they fill the whole given 01422 * client area 01423 * 01424 */ 01425 /* find the amount of space remaining on this row */ 01426 widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) - 01427 TAB_GetItem(infoPtr, iIndexEnd - 1)->rect.right; 01428 01429 /* iCount is the number of tab items on this row */ 01430 iCount = iIndexEnd - iIndexStart; 01431 01432 if (iCount > 1) 01433 { 01434 remainder = widthDiff % iCount; 01435 widthDiff = widthDiff / iCount; 01436 /* add widthDiff/iCount, or extra space/items on row, to each item on this row */ 01437 for (iIndex=iIndexStart, iCount=0; iIndex < iIndexEnd; iIndex++, iCount++) 01438 { 01439 TAB_ITEM *item = TAB_GetItem(infoPtr, iIndex); 01440 01441 item->rect.left += iCount * widthDiff; 01442 item->rect.right += (iCount + 1) * widthDiff; 01443 01444 TRACE("adjusting 1 <%s>, l,r=%d,%d\n", 01445 debugstr_w(item->pszText), 01446 item->rect.left, item->rect.right); 01447 01448 } 01449 TAB_GetItem(infoPtr, iIndex - 1)->rect.right += remainder; 01450 } 01451 else /* we have only one item on this row, make it take up the entire row */ 01452 { 01453 start->rect.left = clientRect.left; 01454 start->rect.right = clientRect.right - 4; 01455 01456 TRACE("adjusting 2 <%s>, l,r=%d,%d\n", 01457 debugstr_w(start->pszText), 01458 start->rect.left, start->rect.right); 01459 01460 } 01461 01462 01463 iIndexStart = iIndexEnd; 01464 } 01465 } 01466 } 01467 01468 /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */ 01469 if(infoPtr->dwStyle & TCS_VERTICAL) 01470 { 01471 RECT rcOriginal; 01472 for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++) 01473 { 01474 rcItem = &TAB_GetItem(infoPtr, iIndex)->rect; 01475 01476 rcOriginal = *rcItem; 01477 01478 /* this is rotating the items by 90 degrees clockwise around the center of the control */ 01479 rcItem->top = (rcOriginal.left - clientRect.left); 01480 rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left); 01481 rcItem->left = rcOriginal.top; 01482 rcItem->right = rcOriginal.bottom; 01483 } 01484 } 01485 01486 TAB_EnsureSelectionVisible(infoPtr); 01487 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL); 01488 01489 /* Cleanup */ 01490 SelectObject (hdc, hOldFont); 01491 ReleaseDC (infoPtr->hwnd, hdc); 01492 } 01493 01494 01495 static void 01496 TAB_EraseTabInterior(const TAB_INFO *infoPtr, HDC hdc, INT iItem, const RECT *drawRect) 01497 { 01498 HBRUSH hbr = CreateSolidBrush (comctl32_color.clrBtnFace); 01499 BOOL deleteBrush = TRUE; 01500 RECT rTemp = *drawRect; 01501 01502 if (infoPtr->dwStyle & TCS_BUTTONS) 01503 { 01504 if (iItem == infoPtr->iSelected) 01505 { 01506 /* Background color */ 01507 if (!(infoPtr->dwStyle & TCS_OWNERDRAWFIXED)) 01508 { 01509 DeleteObject(hbr); 01510 hbr = GetSysColorBrush(COLOR_SCROLLBAR); 01511 01512 SetTextColor(hdc, comctl32_color.clr3dFace); 01513 SetBkColor(hdc, comctl32_color.clr3dHilight); 01514 01515 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT 01516 * we better use 0x55aa bitmap brush to make scrollbar's background 01517 * look different from the window background. 01518 */ 01519 if (comctl32_color.clr3dHilight == comctl32_color.clrWindow) 01520 hbr = COMCTL32_hPattern55AABrush; 01521 01522 deleteBrush = FALSE; 01523 } 01524 FillRect(hdc, &rTemp, hbr); 01525 } 01526 else /* ! selected */ 01527 { 01528 if (infoPtr->dwStyle & TCS_FLATBUTTONS) 01529 { 01530 InflateRect(&rTemp, 2, 2); 01531 FillRect(hdc, &rTemp, hbr); 01532 if (iItem == infoPtr->iHotTracked || 01533 (iItem != infoPtr->iSelected && iItem == infoPtr->uFocus)) 01534 DrawEdge(hdc, &rTemp, BDR_RAISEDINNER, BF_RECT); 01535 } 01536 else 01537 FillRect(hdc, &rTemp, hbr); 01538 } 01539 01540 } 01541 else /* !TCS_BUTTONS */ 01542 { 01543 InflateRect(&rTemp, -2, -2); 01544 if (!GetWindowTheme (infoPtr->hwnd)) 01545 FillRect(hdc, &rTemp, hbr); 01546 } 01547 01548 /* highlighting is drawn on top of previous fills */ 01549 if (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED) 01550 { 01551 if (deleteBrush) 01552 { 01553 DeleteObject(hbr); 01554 deleteBrush = FALSE; 01555 } 01556 hbr = GetSysColorBrush(COLOR_HIGHLIGHT); 01557 FillRect(hdc, &rTemp, hbr); 01558 } 01559 01560 /* Cleanup */ 01561 if (deleteBrush) DeleteObject(hbr); 01562 } 01563 01564 /****************************************************************************** 01565 * TAB_DrawItemInterior 01566 * 01567 * This method is used to draw the interior (text and icon) of a single tab 01568 * into the tab control. 01569 */ 01570 static void 01571 TAB_DrawItemInterior(const TAB_INFO *infoPtr, HDC hdc, INT iItem, RECT *drawRect) 01572 { 01573 RECT localRect; 01574 01575 HPEN htextPen; 01576 HPEN holdPen; 01577 INT oldBkMode; 01578 HFONT hOldFont; 01579 01580 /* if (drawRect == NULL) */ 01581 { 01582 BOOL isVisible; 01583 RECT itemRect; 01584 RECT selectedRect; 01585 01586 /* 01587 * Get the rectangle for the item. 01588 */ 01589 isVisible = TAB_InternalGetItemRect(infoPtr, iItem, &itemRect, &selectedRect); 01590 if (!isVisible) 01591 return; 01592 01593 /* 01594 * Make sure drawRect points to something valid; simplifies code. 01595 */ 01596 drawRect = &localRect; 01597 01598 /* 01599 * This logic copied from the part of TAB_DrawItem which draws 01600 * the tab background. It's important to keep it in sync. I 01601 * would have liked to avoid code duplication, but couldn't figure 01602 * out how without making spaghetti of TAB_DrawItem. 01603 */ 01604 if (iItem == infoPtr->iSelected) 01605 *drawRect = selectedRect; 01606 else 01607 *drawRect = itemRect; 01608 01609 if (infoPtr->dwStyle & TCS_BUTTONS) 01610 { 01611 if (iItem == infoPtr->iSelected) 01612 { 01613 drawRect->left += 4; 01614 drawRect->top += 4; 01615 drawRect->right -= 4; 01616 01617 if (infoPtr->dwStyle & TCS_VERTICAL) 01618 { 01619 if (!(infoPtr->dwStyle & TCS_BOTTOM)) drawRect->right += 1; 01620 drawRect->bottom -= 4; 01621 } 01622 else 01623 { 01624 if (infoPtr->dwStyle & TCS_BOTTOM) 01625 { 01626 drawRect->top -= 2; 01627 drawRect->bottom -= 4; 01628 } 01629 else 01630 drawRect->bottom -= 1; 01631 } 01632 } 01633 else 01634 { 01635 drawRect->left += 2; 01636 drawRect->top += 2; 01637 drawRect->right -= 2; 01638 drawRect->bottom -= 2; 01639 } 01640 } 01641 else 01642 { 01643 if ((infoPtr->dwStyle & TCS_VERTICAL) && (infoPtr->dwStyle & TCS_BOTTOM)) 01644 { 01645 if (iItem != infoPtr->iSelected) 01646 { 01647 drawRect->left += 2; 01648 drawRect->top += 2; 01649 drawRect->bottom -= 2; 01650 } 01651 } 01652 else if (infoPtr->dwStyle & TCS_VERTICAL) 01653 { 01654 if (iItem == infoPtr->iSelected) 01655 { 01656 drawRect->right += 1; 01657 } 01658 else 01659 { 01660 drawRect->top += 2; 01661 drawRect->right -= 2; 01662 drawRect->bottom -= 2; 01663 } 01664 } 01665 else if (infoPtr->dwStyle & TCS_BOTTOM) 01666 { 01667 if (iItem == infoPtr->iSelected) 01668 { 01669 drawRect->top -= 2; 01670 } 01671 else 01672 { 01673 InflateRect(drawRect, -2, -2); 01674 drawRect->bottom += 2; 01675 } 01676 } 01677 else 01678 { 01679 if (iItem == infoPtr->iSelected) 01680 { 01681 drawRect->bottom += 3; 01682 } 01683 else 01684 { 01685 drawRect->bottom -= 2; 01686 InflateRect(drawRect, -2, 0); 01687 } 01688 } 01689 } 01690 } 01691 TRACE("drawRect=(%s)\n", wine_dbgstr_rect(drawRect)); 01692 01693 /* Clear interior */ 01694 TAB_EraseTabInterior (infoPtr, hdc, iItem, drawRect); 01695 01696 /* Draw the focus rectangle */ 01697 if (!(infoPtr->dwStyle & TCS_FOCUSNEVER) && 01698 (GetFocus() == infoPtr->hwnd) && 01699 (iItem == infoPtr->uFocus) ) 01700 { 01701 RECT rFocus = *drawRect; 01702 01703 if (!(infoPtr->dwStyle & TCS_BUTTONS)) InflateRect(&rFocus, -3, -3); 01704 if (infoPtr->dwStyle & TCS_BOTTOM && !(infoPtr->dwStyle & TCS_VERTICAL)) 01705 rFocus.top -= 3; 01706 01707 /* focus should stay on selected item for TCS_BUTTONS style */ 01708 if (!((infoPtr->dwStyle & TCS_BUTTONS) && (infoPtr->iSelected != iItem))) 01709 DrawFocusRect(hdc, &rFocus); 01710 } 01711 01712 /* 01713 * Text pen 01714 */ 01715 htextPen = CreatePen( PS_SOLID, 1, comctl32_color.clrBtnText ); 01716 holdPen = SelectObject(hdc, htextPen); 01717 hOldFont = SelectObject(hdc, infoPtr->hFont); 01718 01719 /* 01720 * Setup for text output 01721 */ 01722 oldBkMode = SetBkMode(hdc, TRANSPARENT); 01723 if (!GetWindowTheme (infoPtr->hwnd) || (infoPtr->dwStyle & TCS_BUTTONS)) 01724 { 01725 if ((infoPtr->dwStyle & TCS_HOTTRACK) && (iItem == infoPtr->iHotTracked) && 01726 !(infoPtr->dwStyle & TCS_FLATBUTTONS)) 01727 SetTextColor(hdc, comctl32_color.clrHighlight); 01728 else if (TAB_GetItem(infoPtr, iItem)->dwState & TCIS_HIGHLIGHTED) 01729 SetTextColor(hdc, comctl32_color.clrHighlightText); 01730 else 01731 SetTextColor(hdc, comctl32_color.clrBtnText); 01732 } 01733 01734 /* 01735 * if owner draw, tell the owner to draw 01736 */ 01737 if ((infoPtr->dwStyle & TCS_OWNERDRAWFIXED) && IsWindow(infoPtr->hwndNotify)) 01738 { 01739 DRAWITEMSTRUCT dis; 01740 UINT id; 01741 01742 drawRect->top += 2; 01743 drawRect->right -= 1; 01744 if ( iItem == infoPtr->iSelected ) 01745 { 01746 drawRect->right -= 1; 01747 drawRect->left += 1; 01748 } 01749 01750 id = (UINT)GetWindowLongPtrW( infoPtr->hwnd, GWLP_ID ); 01751 01752 /* fill DRAWITEMSTRUCT */ 01753 dis.CtlType = ODT_TAB; 01754 dis.CtlID = id; 01755 dis.itemID = iItem; 01756 dis.itemAction = ODA_DRAWENTIRE; 01757 dis.itemState = 0; 01758 if ( iItem == infoPtr->iSelected ) 01759 dis.itemState |= ODS_SELECTED; 01760 if (infoPtr->uFocus == iItem) 01761 dis.itemState |= ODS_FOCUS; 01762 dis.hwndItem = infoPtr->hwnd; 01763 dis.hDC = hdc; 01764 CopyRect(&dis.rcItem,drawRect); 01765 01766 /* when extra data fits ULONG_PTR, store it directly */ 01767 if (infoPtr->cbInfo > sizeof(LPARAM)) 01768 dis.itemData = (ULONG_PTR) TAB_GetItem(infoPtr, iItem)->extra; 01769 else 01770 { 01771 /* this could be considered broken on 64 bit, but that's how it works - 01772 only first 4 bytes are copied */ 01773 dis.itemData = 0; 01774 memcpy(&dis.itemData, (ULONG_PTR*)TAB_GetItem(infoPtr, iItem)->extra, 4); 01775 } 01776 01777 /* draw notification */ 01778 SendMessageW( infoPtr->hwndNotify, WM_DRAWITEM, id, (LPARAM)&dis ); 01779 } 01780 else 01781 { 01782 TAB_ITEM *item = TAB_GetItem(infoPtr, iItem); 01783 RECT rcTemp; 01784 RECT rcImage; 01785 01786 /* used to center the icon and text in the tab */ 01787 RECT rcText; 01788 INT center_offset_h, center_offset_v; 01789 01790 /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */ 01791 rcImage = *drawRect; 01792 01793 rcTemp = *drawRect; 01794 01795 rcText.left = rcText.top = rcText.right = rcText.bottom = 0; 01796 01797 /* get the rectangle that the text fits in */ 01798 if (item->pszText) 01799 { 01800 DrawTextW(hdc, item->pszText, -1, &rcText, DT_CALCRECT); 01801 } 01802 /* 01803 * If not owner draw, then do the drawing ourselves. 01804 * 01805 * Draw the icon. 01806 */ 01807 if (infoPtr->himl && item->iImage != -1) 01808 { 01809 INT cx; 01810 INT cy; 01811 01812 ImageList_GetIconSize(infoPtr->himl, &cx, &cy); 01813 01814 if(infoPtr->dwStyle & TCS_VERTICAL) 01815 { 01816 center_offset_h = ((drawRect->bottom - drawRect->top) - (cy + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2; 01817 center_offset_v = ((drawRect->right - drawRect->left) - cx) / 2; 01818 } 01819 else 01820 { 01821 center_offset_h = ((drawRect->right - drawRect->left) - (cx + infoPtr->uHItemPadding + (rcText.right - rcText.left))) / 2; 01822 center_offset_v = ((drawRect->bottom - drawRect->top) - cy) / 2; 01823 } 01824 01825 /* if an item is selected, the icon is shifted up instead of down */ 01826 if (iItem == infoPtr->iSelected) 01827 center_offset_v -= infoPtr->uVItemPadding / 2; 01828 else 01829 center_offset_v += infoPtr->uVItemPadding / 2; 01830 01831 if (infoPtr->dwStyle & TCS_FIXEDWIDTH && infoPtr->dwStyle & (TCS_FORCELABELLEFT | TCS_FORCEICONLEFT)) 01832 center_offset_h = infoPtr->uHItemPadding; 01833 01834 if (center_offset_h < 2) 01835 center_offset_h = 2; 01836 01837 if (center_offset_v < 0) 01838 center_offset_v = 0; 01839 01840 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%s), textlen=%d\n", 01841 debugstr_w(item->pszText), center_offset_h, center_offset_v, 01842 wine_dbgstr_rect(drawRect), (rcText.right-rcText.left)); 01843 01844 if((infoPtr->dwStyle & TCS_VERTICAL) && (infoPtr->dwStyle & TCS_BOTTOM)) 01845 { 01846 rcImage.top = drawRect->top + center_offset_h; 01847 /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */ 01848 /* right side of the tab, but the image still uses the left as its x position */ 01849 /* this keeps the image always drawn off of the same side of the tab */ 01850 rcImage.left = drawRect->right - cx - center_offset_v; 01851 drawRect->top += cy + infoPtr->uHItemPadding; 01852 } 01853 else if(infoPtr->dwStyle & TCS_VERTICAL) 01854 { 01855 rcImage.top = drawRect->bottom - cy - center_offset_h; 01856 rcImage.left = drawRect->left + center_offset_v; 01857 drawRect->bottom -= cy + infoPtr->uHItemPadding; 01858 } 01859 else /* normal style, whether TCS_BOTTOM or not */ 01860 { 01861 rcImage.left = drawRect->left + center_offset_h; 01862 rcImage.top = drawRect->top + center_offset_v; 01863 drawRect->left += cx + infoPtr->uHItemPadding; 01864 } 01865 01866 TRACE("drawing image=%d, left=%d, top=%d\n", 01867 item->iImage, rcImage.left, rcImage.top-1); 01868 ImageList_Draw 01869 ( 01870 infoPtr->himl, 01871 item->iImage, 01872 hdc, 01873 rcImage.left, 01874 rcImage.top, 01875 ILD_NORMAL 01876 ); 01877 } 01878 01879 /* Now position text */ 01880 if (infoPtr->dwStyle & TCS_FIXEDWIDTH && infoPtr->dwStyle & TCS_FORCELABELLEFT) 01881 center_offset_h = infoPtr->uHItemPadding; 01882 else 01883 if(infoPtr->dwStyle & TCS_VERTICAL) 01884 center_offset_h = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2; 01885 else 01886 center_offset_h = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2; 01887 01888 if(infoPtr->dwStyle & TCS_VERTICAL) 01889 { 01890 if(infoPtr->dwStyle & TCS_BOTTOM) 01891 drawRect->top+=center_offset_h; 01892 else 01893 drawRect->bottom-=center_offset_h; 01894 01895 center_offset_v = ((drawRect->right - drawRect->left) - (rcText.bottom - rcText.top)) / 2; 01896 } 01897 else 01898 { 01899 drawRect->left += center_offset_h; 01900 center_offset_v = ((drawRect->bottom - drawRect->top) - (rcText.bottom - rcText.top)) / 2; 01901 } 01902 01903 /* if an item is selected, the text is shifted up instead of down */ 01904 if (iItem == infoPtr->iSelected) 01905 center_offset_v -= infoPtr->uVItemPadding / 2; 01906 else 01907 center_offset_v += infoPtr->uVItemPadding / 2; 01908 01909 if (center_offset_v < 0) 01910 center_offset_v = 0; 01911 01912 if(infoPtr->dwStyle & TCS_VERTICAL) 01913 drawRect->left += center_offset_v; 01914 else 01915 drawRect->top += center_offset_v; 01916 01917 /* Draw the text */ 01918 if(infoPtr->dwStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */ 01919 { 01920 static const WCHAR ArialW[] = { 'A','r','i','a','l',0 }; 01921 LOGFONTW logfont; 01922 HFONT hFont = 0; 01923 INT nEscapement = 900; 01924 INT nOrientation = 900; 01925 01926 if(infoPtr->dwStyle & TCS_BOTTOM) 01927 { 01928 nEscapement = -900; 01929 nOrientation = -900; 01930 } 01931 01932 /* to get a font with the escapement and orientation we are looking for, we need to */ 01933 /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */ 01934 if (!GetObjectW((infoPtr->hFont) ? 01935 infoPtr->hFont : GetStockObject(SYSTEM_FONT), 01936 sizeof(LOGFONTW),&logfont)) 01937 { 01938 INT iPointSize = 9; 01939 01940 lstrcpyW(logfont.lfFaceName, ArialW); 01941 logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY), 01942 72); 01943 logfont.lfWeight = FW_NORMAL; 01944 logfont.lfItalic = 0; 01945 logfont.lfUnderline = 0; 01946 logfont.lfStrikeOut = 0; 01947 } 01948 01949 logfont.lfEscapement = nEscapement; 01950 logfont.lfOrientation = nOrientation; 01951 hFont = CreateFontIndirectW(&logfont); 01952 SelectObject(hdc, hFont); 01953 01954 if (item->pszText) 01955 { 01956 ExtTextOutW(hdc, 01957 (infoPtr->dwStyle & TCS_BOTTOM) ? drawRect->right : drawRect->left, 01958 (!(infoPtr->dwStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top, 01959 ETO_CLIPPED, 01960 drawRect, 01961 item->pszText, 01962 lstrlenW(item->pszText), 01963 0); 01964 } 01965 01966 DeleteObject(hFont); 01967 } 01968 else 01969 { 01970 TRACE("for <%s>, c_o_h=%d, c_o_v=%d, draw=(%s), textlen=%d\n", 01971 debugstr_w(item->pszText), center_offset_h, center_offset_v, 01972 wine_dbgstr_rect(drawRect), (rcText.right-rcText.left)); 01973 if (item->pszText) 01974 { 01975 DrawTextW 01976 ( 01977 hdc, 01978 item->pszText, 01979 lstrlenW(item->pszText), 01980 drawRect, 01981 DT_LEFT | DT_SINGLELINE 01982 ); 01983 } 01984 } 01985 01986 *drawRect = rcTemp; /* restore drawRect */ 01987 } 01988 01989 /* 01990 * Cleanup 01991 */ 01992 SelectObject(hdc, hOldFont); 01993 SetBkMode(hdc, oldBkMode); 01994 SelectObject(hdc, holdPen); 01995 DeleteObject( htextPen ); 01996 } 01997 01998 /****************************************************************************** 01999 * TAB_DrawItem 02000 * 02001 * This method is used to draw a single tab into the tab control. 02002 */ 02003 static void TAB_DrawItem(const TAB_INFO *infoPtr, HDC hdc, INT iItem) 02004 { 02005 RECT itemRect; 02006 RECT selectedRect; 02007 BOOL isVisible; 02008 RECT r, fillRect, r1; 02009 INT clRight = 0; 02010 INT clBottom = 0; 02011 COLORREF bkgnd, corner; 02012 HTHEME theme; 02013 02014 /* 02015 * Get the rectangle for the item. 02016 */ 02017 isVisible = TAB_InternalGetItemRect(infoPtr, 02018 iItem, 02019 &itemRect, 02020 &selectedRect); 02021 02022 if (isVisible) 02023 { 02024 RECT rUD, rC; 02025 02026 /* Clip UpDown control to not draw over it */ 02027 if (infoPtr->needsScrolling) 02028 { 02029 GetWindowRect(infoPtr->hwnd, &rC); 02030 GetWindowRect(infoPtr->hwndUpDown, &rUD); 02031 ExcludeClipRect(hdc, rUD.left - rC.left, rUD.top - rC.top, rUD.right - rC.left, rUD.bottom - rC.top); 02032 } 02033 02034 /* If you need to see what the control is doing, 02035 * then override these variables. They will change what 02036 * fill colors are used for filling the tabs, and the 02037 * corners when drawing the edge. 02038 */ 02039 bkgnd = comctl32_color.clrBtnFace; 02040 corner = comctl32_color.clrBtnFace; 02041 02042 if (infoPtr->dwStyle & TCS_BUTTONS) 02043 { 02044 /* Get item rectangle */ 02045 r = itemRect; 02046 02047 /* Separators between flat buttons */ 02048 if ((infoPtr->dwStyle & TCS_FLATBUTTONS) && (infoPtr->exStyle & TCS_EX_FLATSEPARATORS)) 02049 { 02050 r1 = r; 02051 r1.right += (FLAT_BTN_SPACINGX -2); 02052 DrawEdge(hdc, &r1, EDGE_ETCHED, BF_RIGHT); 02053 } 02054 02055 if (iItem == infoPtr->iSelected) 02056 { 02057 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT); 02058 02059 OffsetRect(&r, 1, 1); 02060 } 02061 else /* ! selected */ 02062 { 02063 DWORD state = TAB_GetItem(infoPtr, iItem)->dwState; 02064 02065 if ((state & TCIS_BUTTONPRESSED) || (iItem == infoPtr->uFocus)) 02066 DrawEdge(hdc, &r, EDGE_SUNKEN, BF_SOFT|BF_RECT); 02067 else 02068 if (!(infoPtr->dwStyle & TCS_FLATBUTTONS)) 02069 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RECT); 02070 } 02071 } 02072 else /* !TCS_BUTTONS */ 02073 { 02074 /* We draw a rectangle of different sizes depending on the selection 02075 * state. */ 02076 if (iItem == infoPtr->iSelected) { 02077 RECT rect; 02078 GetClientRect (infoPtr->hwnd, &rect); 02079 clRight = rect.right; 02080 clBottom = rect.bottom; 02081 r = selectedRect; 02082 } 02083 else 02084 r = itemRect; 02085 02086 /* 02087 * Erase the background. (Delay it but setup rectangle.) 02088 * This is necessary when drawing the selected item since it is larger 02089 * than the others, it might overlap with stuff already drawn by the 02090 * other tabs 02091 */ 02092 fillRect = r; 02093 02094 /* Draw themed tabs - but only if they are at the top. 02095 * Windows draws even side or bottom tabs themed, with wacky results. 02096 * However, since in Wine apps may get themed that did not opt in via 02097 * a manifest avoid theming when we know the result will be wrong */ 02098 if ((theme = GetWindowTheme (infoPtr->hwnd)) 02099 && ((infoPtr->dwStyle & (TCS_VERTICAL | TCS_BOTTOM)) == 0)) 02100 { 02101 static const int partIds[8] = { 02102 /* Normal item */ 02103 TABP_TABITEM, 02104 TABP_TABITEMLEFTEDGE, 02105 TABP_TABITEMRIGHTEDGE, 02106 TABP_TABITEMBOTHEDGE, 02107 /* Selected tab */ 02108 TABP_TOPTABITEM, 02109 TABP_TOPTABITEMLEFTEDGE, 02110 TABP_TOPTABITEMRIGHTEDGE, 02111 TABP_TOPTABITEMBOTHEDGE, 02112 }; 02113 int partIndex = 0; 02114 int stateId = TIS_NORMAL; 02115 02116 /* selected and unselected tabs have different parts */ 02117 if (iItem == infoPtr->iSelected) 02118 partIndex += 4; 02119 /* The part also differs on the position of a tab on a line. 02120 * "Visually" determining the position works well enough. */ 02121 GetClientRect(infoPtr->hwnd, &r1); 02122 if(selectedRect.left == 0) 02123 partIndex += 1; 02124 if(selectedRect.right == r1.right) 02125 partIndex += 2; 02126 02127 if (iItem == infoPtr->iSelected) 02128 stateId = TIS_SELECTED; 02129 else if (iItem == infoPtr->iHotTracked) 02130 stateId = TIS_HOT; 02131 else if (iItem == infoPtr->uFocus) 02132 stateId = TIS_FOCUSED; 02133 02134 /* Adjust rectangle for bottommost row */ 02135 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1) 02136 r.bottom += 3; 02137 02138 DrawThemeBackground (theme, hdc, partIds[partIndex], stateId, &r, NULL); 02139 GetThemeBackgroundContentRect (theme, hdc, partIds[partIndex], stateId, &r, &r); 02140 } 02141 else if(infoPtr->dwStyle & TCS_VERTICAL) 02142 { 02143 /* These are for adjusting the drawing of a Selected tab */ 02144 /* The initial values are for the normal case of non-Selected */ 02145 int ZZ = 1; /* Do not stretch if selected */ 02146 if (iItem == infoPtr->iSelected) { 02147 ZZ = 0; 02148 02149 /* if leftmost draw the line longer */ 02150 if(selectedRect.top == 0) 02151 fillRect.top += CONTROL_BORDER_SIZEY; 02152 /* if rightmost draw the line longer */ 02153 if(selectedRect.bottom == clBottom) 02154 fillRect.bottom -= CONTROL_BORDER_SIZEY; 02155 } 02156 02157 if (infoPtr->dwStyle & TCS_BOTTOM) 02158 { 02159 /* Adjust both rectangles to match native */ 02160 r.left += (1-ZZ); 02161 02162 TRACE("<right> item=%d, fill=(%s), edge=(%s)\n", 02163 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r)); 02164 02165 /* Clear interior */ 02166 SetBkColor(hdc, bkgnd); 02167 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0); 02168 02169 /* Draw rectangular edge around tab */ 02170 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_RIGHT|BF_TOP|BF_BOTTOM); 02171 02172 /* Now erase the top corner and draw diagonal edge */ 02173 SetBkColor(hdc, corner); 02174 r1.left = r.right - ROUND_CORNER_SIZE - 1; 02175 r1.top = r.top; 02176 r1.right = r.right; 02177 r1.bottom = r1.top + ROUND_CORNER_SIZE; 02178 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0); 02179 r1.right--; 02180 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT); 02181 02182 /* Now erase the bottom corner and draw diagonal edge */ 02183 r1.left = r.right - ROUND_CORNER_SIZE - 1; 02184 r1.bottom = r.bottom; 02185 r1.right = r.right; 02186 r1.top = r1.bottom - ROUND_CORNER_SIZE; 02187 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0); 02188 r1.right--; 02189 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT); 02190 02191 if ((iItem == infoPtr->iSelected) && (selectedRect.top == 0)) { 02192 r1 = r; 02193 r1.right = r1.left; 02194 r1.left--; 02195 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_TOP); 02196 } 02197 02198 } 02199 else 02200 { 02201 TRACE("<left> item=%d, fill=(%s), edge=(%s)\n", 02202 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r)); 02203 02204 /* Clear interior */ 02205 SetBkColor(hdc, bkgnd); 02206 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0); 02207 02208 /* Draw rectangular edge around tab */ 02209 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_BOTTOM); 02210 02211 /* Now erase the top corner and draw diagonal edge */ 02212 SetBkColor(hdc, corner); 02213 r1.left = r.left; 02214 r1.top = r.top; 02215 r1.right = r1.left + ROUND_CORNER_SIZE + 1; 02216 r1.bottom = r1.top + ROUND_CORNER_SIZE; 02217 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0); 02218 r1.left++; 02219 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT); 02220 02221 /* Now erase the bottom corner and draw diagonal edge */ 02222 r1.left = r.left; 02223 r1.bottom = r.bottom; 02224 r1.right = r1.left + ROUND_CORNER_SIZE + 1; 02225 r1.top = r1.bottom - ROUND_CORNER_SIZE; 02226 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0); 02227 r1.left++; 02228 DrawEdge(hdc, &r1, EDGE_SUNKEN, BF_DIAGONAL_ENDTOPLEFT); 02229 } 02230 } 02231 else /* ! TCS_VERTICAL */ 02232 { 02233 /* These are for adjusting the drawing of a Selected tab */ 02234 /* The initial values are for the normal case of non-Selected */ 02235 if (iItem == infoPtr->iSelected) { 02236 /* if leftmost draw the line longer */ 02237 if(selectedRect.left == 0) 02238 fillRect.left += CONTROL_BORDER_SIZEX; 02239 /* if rightmost draw the line longer */ 02240 if(selectedRect.right == clRight) 02241 fillRect.right -= CONTROL_BORDER_SIZEX; 02242 } 02243 02244 if (infoPtr->dwStyle & TCS_BOTTOM) 02245 { 02246 /* Adjust both rectangles for topmost row */ 02247 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1) 02248 { 02249 fillRect.top -= 2; 02250 r.top -= 1; 02251 } 02252 02253 TRACE("<bottom> item=%d, fill=(%s), edge=(%s)\n", 02254 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r)); 02255 02256 /* Clear interior */ 02257 SetBkColor(hdc, bkgnd); 02258 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0); 02259 02260 /* Draw rectangular edge around tab */ 02261 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_BOTTOM|BF_RIGHT); 02262 02263 /* Now erase the righthand corner and draw diagonal edge */ 02264 SetBkColor(hdc, corner); 02265 r1.left = r.right - ROUND_CORNER_SIZE; 02266 r1.bottom = r.bottom; 02267 r1.right = r.right; 02268 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1; 02269 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0); 02270 r1.bottom--; 02271 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMLEFT); 02272 02273 /* Now erase the lefthand corner and draw diagonal edge */ 02274 r1.left = r.left; 02275 r1.bottom = r.bottom; 02276 r1.right = r1.left + ROUND_CORNER_SIZE; 02277 r1.top = r1.bottom - ROUND_CORNER_SIZE - 1; 02278 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0); 02279 r1.bottom--; 02280 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPLEFT); 02281 02282 if (iItem == infoPtr->iSelected) 02283 { 02284 r.top += 2; 02285 r.left += 1; 02286 if (selectedRect.left == 0) 02287 { 02288 r1 = r; 02289 r1.bottom = r1.top; 02290 r1.top--; 02291 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_LEFT); 02292 } 02293 } 02294 02295 } 02296 else 02297 { 02298 /* Adjust both rectangles for bottommost row */ 02299 if (TAB_GetItem(infoPtr, iItem)->rect.top == infoPtr->uNumRows-1) 02300 { 02301 fillRect.bottom += 3; 02302 r.bottom += 2; 02303 } 02304 02305 TRACE("<top> item=%d, fill=(%s), edge=(%s)\n", 02306 iItem, wine_dbgstr_rect(&fillRect), wine_dbgstr_rect(&r)); 02307 02308 /* Clear interior */ 02309 SetBkColor(hdc, bkgnd); 02310 ExtTextOutW(hdc, 0, 0, 2, &fillRect, NULL, 0, 0); 02311 02312 /* Draw rectangular edge around tab */ 02313 DrawEdge(hdc, &r, EDGE_RAISED, BF_SOFT|BF_LEFT|BF_TOP|BF_RIGHT); 02314 02315 /* Now erase the righthand corner and draw diagonal edge */ 02316 SetBkColor(hdc, corner); 02317 r1.left = r.right - ROUND_CORNER_SIZE; 02318 r1.top = r.top; 02319 r1.right = r.right; 02320 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1; 02321 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0); 02322 r1.top++; 02323 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDBOTTOMRIGHT); 02324 02325 /* Now erase the lefthand corner and draw diagonal edge */ 02326 r1.left = r.left; 02327 r1.top = r.top; 02328 r1.right = r1.left + ROUND_CORNER_SIZE; 02329 r1.bottom = r1.top + ROUND_CORNER_SIZE + 1; 02330 ExtTextOutW(hdc, 0, 0, 2, &r1, NULL, 0, 0); 02331 r1.top++; 02332 DrawEdge(hdc, &r1, EDGE_RAISED, BF_SOFT|BF_DIAGONAL_ENDTOPRIGHT); 02333 } 02334 } 02335 } 02336 02337 TAB_DumpItemInternal(infoPtr, iItem); 02338 02339 /* This modifies r to be the text rectangle. */ 02340 TAB_DrawItemInterior(infoPtr, hdc, iItem, &r); 02341 } 02342 } 02343 02344 /****************************************************************************** 02345 * TAB_DrawBorder 02346 * 02347 * This method is used to draw the raised border around the tab control 02348 * "content" area. 02349 */ 02350 static void TAB_DrawBorder(const TAB_INFO *infoPtr, HDC hdc) 02351 { 02352 RECT rect; 02353 HTHEME theme = GetWindowTheme (infoPtr->hwnd); 02354 02355 GetClientRect (infoPtr->hwnd, &rect); 02356 02357 /* 02358 * Adjust for the style 02359 */ 02360 02361 if (infoPtr->uNumItem) 02362 { 02363 if ((infoPtr->dwStyle & TCS_BOTTOM) && !(infoPtr->dwStyle & TCS_VERTICAL)) 02364 rect.bottom -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX; 02365 else if((infoPtr->dwStyle & TCS_BOTTOM) && (infoPtr->dwStyle & TCS_VERTICAL)) 02366 rect.right -= infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX; 02367 else if(infoPtr->dwStyle & TCS_VERTICAL) 02368 rect.left += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX; 02369 else /* not TCS_VERTICAL and not TCS_BOTTOM */ 02370 rect.top += infoPtr->tabHeight * infoPtr->uNumRows + CONTROL_BORDER_SIZEX; 02371 } 02372 02373 TRACE("border=(%s)\n", wine_dbgstr_rect(&rect)); 02374 02375 if (theme) 02376 DrawThemeBackground (theme, hdc, TABP_PANE, 0, &rect, NULL); 02377 else 02378 DrawEdge(hdc, &rect, EDGE_RAISED, BF_SOFT|BF_RECT); 02379 } 02380 02381 /****************************************************************************** 02382 * TAB_Refresh 02383 * 02384 * This method repaints the tab control.. 02385 */ 02386 static void TAB_Refresh (const TAB_INFO *infoPtr, HDC hdc) 02387 { 02388 HFONT hOldFont; 02389 INT i; 02390 02391 if (!infoPtr->DoRedraw) 02392 return; 02393 02394 hOldFont = SelectObject (hdc, infoPtr->hFont); 02395 02396 if (infoPtr->dwStyle & TCS_BUTTONS) 02397 { 02398 for (i = 0; i < infoPtr->uNumItem; i++) 02399 TAB_DrawItem (infoPtr, hdc, i); 02400 } 02401 else 02402 { 02403 /* Draw all the non selected item first */ 02404 for (i = 0; i < infoPtr->uNumItem; i++) 02405 { 02406 if (i != infoPtr->iSelected) 02407 TAB_DrawItem (infoPtr, hdc, i); 02408 } 02409 02410 /* Now, draw the border, draw it before the selected item 02411 * since the selected item overwrites part of the border. */ 02412 TAB_DrawBorder (infoPtr, hdc); 02413 02414 /* Then, draw the selected item */ 02415 TAB_DrawItem (infoPtr, hdc, infoPtr->iSelected); 02416 } 02417 02418 SelectObject (hdc, hOldFont); 02419 } 02420 02421 static inline DWORD TAB_GetRowCount (const TAB_INFO *infoPtr) 02422 { 02423 TRACE("(%p)\n", infoPtr); 02424 return infoPtr->uNumRows; 02425 } 02426 02427 static inline LRESULT TAB_SetRedraw (TAB_INFO *infoPtr, BOOL doRedraw) 02428 { 02429 infoPtr->DoRedraw = doRedraw; 02430 return 0; 02431 } 02432 02433 /****************************************************************************** 02434 * TAB_EnsureSelectionVisible 02435 * 02436 * This method will make sure that the current selection is completely 02437 * visible by scrolling until it is. 02438 */ 02439 static void TAB_EnsureSelectionVisible( 02440 TAB_INFO* infoPtr) 02441 { 02442 INT iSelected = infoPtr->iSelected; 02443 INT iOrigLeftmostVisible = infoPtr->leftmostVisible; 02444 02445 if (iSelected < 0) 02446 return; 02447 02448 /* set the items row to the bottommost row or topmost row depending on 02449 * style */ 02450 if ((infoPtr->uNumRows > 1) && !(infoPtr->dwStyle & TCS_BUTTONS)) 02451 { 02452 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected); 02453 INT newselected; 02454 INT iTargetRow; 02455 02456 if(infoPtr->dwStyle & TCS_VERTICAL) 02457 newselected = selected->rect.left; 02458 else 02459 newselected = selected->rect.top; 02460 02461 /* the target row is always (number of rows - 1) 02462 as row 0 is furthest from the clientRect */ 02463 iTargetRow = infoPtr->uNumRows - 1; 02464 02465 if (newselected != iTargetRow) 02466 { 02467 UINT i; 02468 if(infoPtr->dwStyle & TCS_VERTICAL) 02469 { 02470 for (i=0; i < infoPtr->uNumItem; i++) 02471 { 02472 /* move everything in the row of the selected item to the iTargetRow */ 02473 TAB_ITEM *item = TAB_GetItem(infoPtr, i); 02474 02475 if (item->rect.left == newselected ) 02476 item->rect.left = iTargetRow; 02477 else 02478 { 02479 if (item->rect.left > newselected) 02480 item->rect.left-=1; 02481 } 02482 } 02483 } 02484 else 02485 { 02486 for (i=0; i < infoPtr->uNumItem; i++) 02487 { 02488 TAB_ITEM *item = TAB_GetItem(infoPtr, i); 02489 02490 if (item->rect.top == newselected ) 02491 item->rect.top = iTargetRow; 02492 else 02493 { 02494 if (item->rect.top > newselected) 02495 item->rect.top-=1; 02496 } 02497 } 02498 } 02499 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL); 02500 } 02501 } 02502 02503 /* 02504 * Do the trivial cases first. 02505 */ 02506 if ( (!infoPtr->needsScrolling) || 02507 (infoPtr->hwndUpDown==0) || (infoPtr->dwStyle & TCS_VERTICAL)) 02508 return; 02509 02510 if (infoPtr->leftmostVisible >= iSelected) 02511 { 02512 infoPtr->leftmostVisible = iSelected; 02513 } 02514 else 02515 { 02516 TAB_ITEM *selected = TAB_GetItem(infoPtr, iSelected); 02517 RECT r; 02518 INT width; 02519 UINT i; 02520 02521 /* Calculate the part of the client area that is visible */ 02522 GetClientRect(infoPtr->hwnd, &r); 02523 width = r.right; 02524 02525 GetClientRect(infoPtr->hwndUpDown, &r); 02526 width -= r.right; 02527 02528 if ((selected->rect.right - 02529 selected->rect.left) >= width ) 02530 { 02531 /* Special case: width of selected item is greater than visible 02532 * part of control. 02533 */ 02534 infoPtr->leftmostVisible = iSelected; 02535 } 02536 else 02537 { 02538 for (i = infoPtr->leftmostVisible; i < infoPtr->uNumItem; i++) 02539 { 02540 if ((selected->rect.right - TAB_GetItem(infoPtr, i)->rect.left) < width) 02541 break; 02542 } 02543 infoPtr->leftmostVisible = i; 02544 } 02545 } 02546 02547 if (infoPtr->leftmostVisible != iOrigLeftmostVisible) 02548 TAB_RecalcHotTrack(infoPtr, NULL, NULL, NULL); 02549 02550 SendMessageW(infoPtr->hwndUpDown, UDM_SETPOS, 0, 02551 MAKELONG(infoPtr->leftmostVisible, 0)); 02552 } 02553 02554 /****************************************************************************** 02555 * TAB_InvalidateTabArea 02556 * 02557 * This method will invalidate the portion of the control that contains the 02558 * tabs. It is called when the state of the control changes and needs 02559 * to be redisplayed 02560 */ 02561 static void TAB_InvalidateTabArea(const TAB_INFO *infoPtr) 02562 { 02563 RECT clientRect, rInvalidate, rAdjClient; 02564 INT lastRow = infoPtr->uNumRows - 1; 02565 RECT rect; 02566 02567 if (lastRow < 0) return; 02568 02569 GetClientRect(infoPtr->hwnd, &clientRect); 02570 rInvalidate = clientRect; 02571 rAdjClient = clientRect; 02572 02573 TAB_AdjustRect(infoPtr, 0, &rAdjClient); 02574 02575 TAB_InternalGetItemRect(infoPtr, infoPtr->uNumItem-1 , &rect, NULL); 02576 if ((infoPtr->dwStyle & TCS_BOTTOM) && (infoPtr->dwStyle & TCS_VERTICAL)) 02577 { 02578 rInvalidate.left = rAdjClient.right; 02579 if (infoPtr->uNumRows == 1) 02580 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET; 02581 } 02582 else if(infoPtr->dwStyle & TCS_VERTICAL) 02583 { 02584 rInvalidate.right = rAdjClient.left; 02585 if (infoPtr->uNumRows == 1) 02586 rInvalidate.bottom = clientRect.top + rect.bottom + 2 * SELECTED_TAB_OFFSET; 02587 } 02588 else if (infoPtr->dwStyle & TCS_BOTTOM) 02589 { 02590 rInvalidate.top = rAdjClient.bottom; 02591 if (infoPtr->uNumRows == 1) 02592 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET; 02593 } 02594 else 02595 { 02596 rInvalidate.bottom = rAdjClient.top; 02597 if (infoPtr->uNumRows == 1) 02598 rInvalidate.right = clientRect.left + rect.right + 2 * SELECTED_TAB_OFFSET; 02599 } 02600 02601 /* Punch out the updown control */ 02602 if (infoPtr->needsScrolling && (rInvalidate.right > 0)) { 02603 RECT r; 02604 GetClientRect(infoPtr->hwndUpDown, &r); 02605 if (rInvalidate.right > clientRect.right - r.left) 02606 rInvalidate.right = rInvalidate.right - (r.right - r.left); 02607 else 02608 rInvalidate.right = clientRect.right - r.left; 02609 } 02610 02611 TRACE("invalidate (%s)\n", wine_dbgstr_rect(&rInvalidate)); 02612 02613 InvalidateRect(infoPtr->hwnd, &rInvalidate, TRUE); 02614 } 02615 02616 static inline LRESULT TAB_Paint (TAB_INFO *infoPtr, HDC hdcPaint) 02617 { 02618 HDC hdc; 02619 PAINTSTRUCT ps; 02620 02621 if (hdcPaint) 02622 hdc = hdcPaint; 02623 else 02624 { 02625 hdc = BeginPaint (infoPtr->hwnd, &ps); 02626 TRACE("erase %d, rect=(%s)\n", ps.fErase, wine_dbgstr_rect(&ps.rcPaint)); 02627 } 02628 02629 TAB_Refresh (infoPtr, hdc); 02630 02631 if (!hdcPaint) 02632 EndPaint (infoPtr->hwnd, &ps); 02633 02634 return 0; 02635 } 02636 02637 static LRESULT 02638 TAB_InsertItemT (TAB_INFO *infoPtr, INT iItem, const TCITEMW *pti, BOOL bUnicode) 02639 { 02640 TAB_ITEM *item; 02641 RECT rect; 02642 02643 GetClientRect (infoPtr->hwnd, &rect); 02644 TRACE("Rect: %p %s\n", infoPtr->hwnd, wine_dbgstr_rect(&rect)); 02645 02646 if (iItem < 0) return -1; 02647 if (iItem > infoPtr->uNumItem) 02648 iItem = infoPtr->uNumItem; 02649 02650 TAB_DumpItemExternalT(pti, iItem, bUnicode); 02651 02652 if (!(item = Alloc(TAB_ITEM_SIZE(infoPtr)))) return FALSE; 02653 if (DPA_InsertPtr(infoPtr->items, iItem, item) == -1) 02654 { 02655 Free(item); 02656 return FALSE; 02657 } 02658 02659 if (infoPtr->uNumItem == 0) 02660 infoPtr->iSelected = 0; 02661 else if (iItem <= infoPtr->iSelected) 02662 infoPtr->iSelected++; 02663 02664 infoPtr->uNumItem++; 02665 02666 item->pszText = NULL; 02667 if (pti->mask & TCIF_TEXT) 02668 { 02669 if (bUnicode) 02670 Str_SetPtrW (&item->pszText, pti->pszText); 02671 else 02672 Str_SetPtrAtoW (&item->pszText, (LPSTR)pti->pszText); 02673 } 02674 02675 if (pti->mask & TCIF_IMAGE) 02676 item->iImage = pti->iImage; 02677 else 02678 item->iImage = -1; 02679 02680 if (pti->mask & TCIF_PARAM) 02681 memcpy(item->extra, &pti->lParam, EXTRA_ITEM_SIZE(infoPtr)); 02682 else 02683 memset(item->extra, 0, EXTRA_ITEM_SIZE(infoPtr)); 02684 02685 TAB_SetItemBounds(infoPtr); 02686 if (infoPtr->uNumItem > 1) 02687 TAB_InvalidateTabArea(infoPtr); 02688 else 02689 InvalidateRect(infoPtr->hwnd, NULL, TRUE); 02690 02691 TRACE("[%p]: added item %d %s\n", 02692 infoPtr->hwnd, iItem, debugstr_w(item->pszText)); 02693 02694 /* If we haven't set the current focus yet, set it now. */ 02695 if (infoPtr->uFocus == -1) 02696 TAB_SetCurFocus(infoPtr, iItem); 02697 02698 return iItem; 02699 } 02700 02701 static LRESULT 02702 TAB_SetItemSize (TAB_INFO *infoPtr, INT cx, INT cy) 02703 { 02704 LONG lResult = 0; 02705 BOOL bNeedPaint = FALSE; 02706 02707 lResult = MAKELONG(infoPtr->tabWidth, infoPtr->tabHeight); 02708 02709 /* UNDOCUMENTED: If requested Width or Height is 0 this means that program wants to use auto size. */ 02710 if (infoPtr->dwStyle & TCS_FIXEDWIDTH && (infoPtr->tabWidth != cx)) 02711 { 02712 infoPtr->tabWidth = cx; 02713 bNeedPaint = TRUE; 02714 } 02715 02716 if (infoPtr->tabHeight != cy) 02717 { 02718 if ((infoPtr->fHeightSet = (cy != 0))) 02719 infoPtr->tabHeight = cy; 02720 02721 bNeedPaint = TRUE; 02722 } 02723 TRACE("was h=%d,w=%d, now h=%d,w=%d\n", 02724 HIWORD(lResult), LOWORD(lResult), 02725 infoPtr->tabHeight, infoPtr->tabWidth); 02726 02727 if (bNeedPaint) 02728 { 02729 TAB_SetItemBounds(infoPtr); 02730 RedrawWindow(infoPtr->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW); 02731 } 02732 02733 return lResult; 02734 } 02735 02736 static inline LRESULT TAB_SetMinTabWidth (TAB_INFO *infoPtr, INT cx) 02737 { 02738 INT oldcx = 0; 02739 02740 TRACE("(%p,%d)\n", infoPtr, cx); 02741 02742 if (infoPtr->tabMinWidth < 0) 02743 oldcx = DEFAULT_MIN_TAB_WIDTH; 02744 else 02745 oldcx = infoPtr->tabMinWidth; 02746 infoPtr->tabMinWidth = cx; 02747 TAB_SetItemBounds(infoPtr); 02748 return oldcx; 02749 } 02750 02751 static inline LRESULT 02752 TAB_HighlightItem (TAB_INFO *infoPtr, INT iItem, BOOL fHighlight) 02753 { 02754 LPDWORD lpState; 02755 DWORD oldState; 02756 RECT r; 02757 02758 TRACE("(%p,%d,%s)\n", infoPtr, iItem, fHighlight ? "true" : "false"); 02759 02760 if (iItem < 0 || iItem >= infoPtr->uNumItem) 02761 return FALSE; 02762 02763 lpState = &TAB_GetItem(infoPtr, iItem)->dwState; 02764 oldState = *lpState; 02765 02766 if (fHighlight) 02767 *lpState |= TCIS_HIGHLIGHTED; 02768 else 02769 *lpState &= ~TCIS_HIGHLIGHTED; 02770 02771 if ((oldState != *lpState) && TAB_InternalGetItemRect (infoPtr, iItem, &r, NULL)) 02772 InvalidateRect (infoPtr->hwnd, &r, TRUE); 02773 02774 return TRUE; 02775 } 02776 02777 static LRESULT 02778 TAB_SetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode) 02779 { 02780 TAB_ITEM *wineItem; 02781 02782 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false"); 02783 02784 if (iItem < 0 || iItem >= infoPtr->uNumItem) 02785 return FALSE; 02786 02787 TAB_DumpItemExternalT(tabItem, iItem, bUnicode); 02788 02789 wineItem = TAB_GetItem(infoPtr, iItem); 02790 02791 if (tabItem->mask & TCIF_IMAGE) 02792 wineItem->iImage = tabItem->iImage; 02793 02794 if (tabItem->mask & TCIF_PARAM) 02795 memcpy(wineItem->extra, &tabItem->lParam, infoPtr->cbInfo); 02796 02797 if (tabItem->mask & TCIF_RTLREADING) 02798 FIXME("TCIF_RTLREADING\n"); 02799 02800 if (tabItem->mask & TCIF_STATE) 02801 wineItem->dwState = (wineItem->dwState & ~tabItem->dwStateMask) | 02802 ( tabItem->dwState & tabItem->dwStateMask); 02803 02804 if (tabItem->mask & TCIF_TEXT) 02805 { 02806 Free(wineItem->pszText); 02807 wineItem->pszText = NULL; 02808 if (bUnicode) 02809 Str_SetPtrW(&wineItem->pszText, tabItem->pszText); 02810 else 02811 Str_SetPtrAtoW(&wineItem->pszText, (LPSTR)tabItem->pszText); 02812 } 02813 02814 /* Update and repaint tabs */ 02815 TAB_SetItemBounds(infoPtr); 02816 TAB_InvalidateTabArea(infoPtr); 02817 02818 return TRUE; 02819 } 02820 02821 static inline LRESULT TAB_GetItemCount (const TAB_INFO *infoPtr) 02822 { 02823 TRACE("\n"); 02824 return infoPtr->uNumItem; 02825 } 02826 02827 02828 static LRESULT 02829 TAB_GetItemT (TAB_INFO *infoPtr, INT iItem, LPTCITEMW tabItem, BOOL bUnicode) 02830 { 02831 TAB_ITEM *wineItem; 02832 02833 TRACE("(%p,%d,%p,%s)\n", infoPtr, iItem, tabItem, bUnicode ? "true" : "false"); 02834 02835 if (!tabItem) return FALSE; 02836 02837 if (iItem < 0 || iItem >= infoPtr->uNumItem) 02838 { 02839 /* init requested fields */ 02840 if (tabItem->mask & TCIF_IMAGE) tabItem->iImage = 0; 02841 if (tabItem->mask & TCIF_PARAM) tabItem->lParam = 0; 02842 if (tabItem->mask & TCIF_STATE) tabItem->dwState = 0; 02843 return FALSE; 02844 } 02845 02846 wineItem = TAB_GetItem(infoPtr, iItem); 02847 02848 if (tabItem->mask & TCIF_IMAGE) 02849 tabItem->iImage = wineItem->iImage; 02850 02851 if (tabItem->mask & TCIF_PARAM) 02852 memcpy(&tabItem->lParam, wineItem->extra, infoPtr->cbInfo); 02853 02854 if (tabItem->mask & TCIF_RTLREADING) 02855 FIXME("TCIF_RTLREADING\n"); 02856 02857 if (tabItem->mask & TCIF_STATE) 02858 tabItem->dwState = wineItem->dwState & tabItem->dwStateMask; 02859 02860 if (tabItem->mask & TCIF_TEXT) 02861 { 02862 if (bUnicode) 02863 Str_GetPtrW (wineItem->pszText, tabItem->pszText, tabItem->cchTextMax); 02864 else 02865 Str_GetPtrWtoA (wineItem->pszText, (LPSTR)tabItem->pszText, tabItem->cchTextMax); 02866 } 02867 02868 TAB_DumpItemExternalT(tabItem, iItem, bUnicode); 02869 02870 return TRUE; 02871 } 02872 02873 02874 static LRESULT TAB_DeleteItem (TAB_INFO *infoPtr, INT iItem) 02875 { 02876 TAB_ITEM *item; 02877 02878 TRACE("(%p, %d)\n", infoPtr, iItem); 02879 02880 if (iItem < 0 || iItem >= infoPtr->uNumItem) return FALSE; 02881 02882 item = TAB_GetItem(infoPtr, iItem); 02883 Free(item->pszText); 02884 Free(item); 02885 infoPtr->uNumItem--; 02886 DPA_DeletePtr(infoPtr->items, iItem); 02887 02888 TAB_InvalidateTabArea(infoPtr); 02889 02890 if (infoPtr->uNumItem == 0) 02891 { 02892 if (infoPtr->iHotTracked >= 0) 02893 { 02894 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER); 02895 infoPtr->iHotTracked = -1; 02896 } 02897 02898 infoPtr->iSelected = -1; 02899 } 02900 else 02901 { 02902 if (iItem <= infoPtr->iHotTracked) 02903 { 02904 /* When tabs move left/up, the hot track item may change */ 02905 FIXME("Recalc hot track\n"); 02906 } 02907 } 02908 02909 /* adjust the selected index */ 02910 if (iItem == infoPtr->iSelected) 02911 infoPtr->iSelected = -1; 02912 else if (iItem < infoPtr->iSelected) 02913 infoPtr->iSelected--; 02914 02915 /* reposition and repaint tabs */ 02916 TAB_SetItemBounds(infoPtr); 02917 02918 return TRUE; 02919 } 02920 02921 static inline LRESULT TAB_DeleteAllItems (TAB_INFO *infoPtr) 02922 { 02923 TRACE("(%p)\n", infoPtr); 02924 while (infoPtr->uNumItem) 02925 TAB_DeleteItem (infoPtr, 0); 02926 return TRUE; 02927 } 02928 02929 02930 static inline LRESULT TAB_GetFont (const TAB_INFO *infoPtr) 02931 { 02932 TRACE("(%p) returning %p\n", infoPtr, infoPtr->hFont); 02933 return (LRESULT)infoPtr->hFont; 02934 } 02935 02936 static inline LRESULT TAB_SetFont (TAB_INFO *infoPtr, HFONT hNewFont) 02937 { 02938 TRACE("(%p,%p)\n", infoPtr, hNewFont); 02939 02940 infoPtr->hFont = hNewFont; 02941 02942 TAB_SetItemBounds(infoPtr); 02943 02944 TAB_InvalidateTabArea(infoPtr); 02945 02946 return 0; 02947 } 02948 02949 02950 static inline LRESULT TAB_GetImageList (const TAB_INFO *infoPtr) 02951 { 02952 TRACE("\n"); 02953 return (LRESULT)infoPtr->himl; 02954 } 02955 02956 static inline LRESULT TAB_SetImageList (TAB_INFO *infoPtr, HIMAGELIST himlNew) 02957 { 02958 HIMAGELIST himlPrev = infoPtr->himl; 02959 TRACE("himl=%p\n", himlNew); 02960 infoPtr->himl = himlNew; 02961 TAB_SetItemBounds(infoPtr); 02962 InvalidateRect(infoPtr->hwnd, NULL, TRUE); 02963 return (LRESULT)himlPrev; 02964 } 02965 02966 static inline LRESULT TAB_GetUnicodeFormat (const TAB_INFO *infoPtr) 02967 { 02968 TRACE("(%p)\n", infoPtr); 02969 return infoPtr->bUnicode; 02970 } 02971 02972 static inline LRESULT TAB_SetUnicodeFormat (TAB_INFO *infoPtr, BOOL bUnicode) 02973 { 02974 BOOL bTemp = infoPtr->bUnicode; 02975 02976 TRACE("(%p %d)\n", infoPtr, bUnicode); 02977 infoPtr->bUnicode = bUnicode; 02978 02979 return bTemp; 02980 } 02981 02982 static inline LRESULT TAB_Size (TAB_INFO *infoPtr) 02983 { 02984 /* I'm not really sure what the following code was meant to do. 02985 This is what it is doing: 02986 When WM_SIZE is sent with SIZE_RESTORED, the control 02987 gets positioned in the top left corner. 02988 02989 RECT parent_rect; 02990 HWND parent; 02991 UINT uPosFlags,cx,cy; 02992 02993 uPosFlags=0; 02994 if (!wParam) { 02995 parent = GetParent (hwnd); 02996 GetClientRect(parent, &parent_rect); 02997 cx=LOWORD (lParam); 02998 cy=HIWORD (lParam); 02999 if (GetWindowLongW(hwnd, GWL_STYLE) & CCS_NORESIZE) 03000 uPosFlags |= (SWP_NOSIZE | SWP_NOMOVE); 03001 03002 SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top, 03003 cx, cy, uPosFlags | SWP_NOZORDER); 03004 } else { 03005 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam); 03006 } */ 03007 03008 /* Recompute the size/position of the tabs. */ 03009 TAB_SetItemBounds (infoPtr); 03010 03011 /* Force a repaint of the control. */ 03012 InvalidateRect(infoPtr->hwnd, NULL, TRUE); 03013 03014 return 0; 03015 } 03016 03017 03018 static LRESULT TAB_Create (HWND hwnd, LPARAM lParam) 03019 { 03020 TAB_INFO *infoPtr; 03021 TEXTMETRICW fontMetrics; 03022 HDC hdc; 03023 HFONT hOldFont; 03024 DWORD dwStyle; 03025 03026 infoPtr = Alloc (sizeof(TAB_INFO)); 03027 03028 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr); 03029 03030 infoPtr->hwnd = hwnd; 03031 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent; 03032 infoPtr->uNumItem = 0; 03033 infoPtr->uNumRows = 0; 03034 infoPtr->uHItemPadding = 6; 03035 infoPtr->uVItemPadding = 3; 03036 infoPtr->uHItemPadding_s = 6; 03037 infoPtr->uVItemPadding_s = 3; 03038 infoPtr->hFont = 0; 03039 infoPtr->items = DPA_Create(8); 03040 infoPtr->hcurArrow = LoadCursorW (0, (LPWSTR)IDC_ARROW); 03041 infoPtr->iSelected = -1; 03042 infoPtr->iHotTracked = -1; 03043 infoPtr->uFocus = -1; 03044 infoPtr->hwndToolTip = 0; 03045 infoPtr->DoRedraw = TRUE; 03046 infoPtr->needsScrolling = FALSE; 03047 infoPtr->hwndUpDown = 0; 03048 infoPtr->leftmostVisible = 0; 03049 infoPtr->fHeightSet = FALSE; 03050 infoPtr->bUnicode = IsWindowUnicode (hwnd); 03051 infoPtr->cbInfo = sizeof(LPARAM); 03052 03053 TRACE("Created tab control, hwnd [%p]\n", hwnd); 03054 03055 /* The tab control always has the WS_CLIPSIBLINGS style. Even 03056 if you don't specify it in CreateWindow. This is necessary in 03057 order for paint to work correctly. This follows windows behaviour. */ 03058 dwStyle = GetWindowLongW(hwnd, GWL_STYLE); 03059 SetWindowLongW(hwnd, GWL_STYLE, dwStyle|WS_CLIPSIBLINGS); 03060 03061 infoPtr->dwStyle = dwStyle | WS_CLIPSIBLINGS; 03062 infoPtr->exStyle = (dwStyle & TCS_FLATBUTTONS) ? TCS_EX_FLATSEPARATORS : 0; 03063 03064 if (infoPtr->dwStyle & TCS_TOOLTIPS) { 03065 /* Create tooltip control */ 03066 infoPtr->hwndToolTip = 03067 CreateWindowExW (0, TOOLTIPS_CLASSW, NULL, WS_POPUP, 03068 CW_USEDEFAULT, CW_USEDEFAULT, 03069 CW_USEDEFAULT, CW_USEDEFAULT, 03070 hwnd, 0, 0, 0); 03071 03072 /* Send NM_TOOLTIPSCREATED notification */ 03073 if (infoPtr->hwndToolTip) { 03074 NMTOOLTIPSCREATED nmttc; 03075 03076 nmttc.hdr.hwndFrom = hwnd; 03077 nmttc.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID); 03078 nmttc.hdr.code = NM_TOOLTIPSCREATED; 03079 nmttc.hwndToolTips = infoPtr->hwndToolTip; 03080 03081 SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, 03082 GetWindowLongPtrW(hwnd, GWLP_ID), (LPARAM)&nmttc); 03083 } 03084 } 03085 03086 OpenThemeData (infoPtr->hwnd, themeClass); 03087 03088 /* 03089 * We need to get text information so we need a DC and we need to select 03090 * a font. 03091 */ 03092 hdc = GetDC(hwnd); 03093 hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT)); 03094 03095 /* Use the system font to determine the initial height of a tab. */ 03096 GetTextMetricsW(hdc, &fontMetrics); 03097 03098 /* 03099 * Make sure there is enough space for the letters + growing the 03100 * selected item + extra space for the selected item. 03101 */ 03102 infoPtr->tabHeight = fontMetrics.tmHeight + SELECTED_TAB_OFFSET + 03103 ((infoPtr->dwStyle & TCS_BUTTONS) ? 2 : 1) * 03104 infoPtr->uVItemPadding; 03105 03106 /* Initialize the width of a tab. */ 03107 if (infoPtr->dwStyle & TCS_FIXEDWIDTH) 03108 infoPtr->tabWidth = GetDeviceCaps(hdc, LOGPIXELSX); 03109 03110 infoPtr->tabMinWidth = -1; 03111 03112 TRACE("tabH=%d, tabW=%d\n", infoPtr->tabHeight, infoPtr->tabWidth); 03113 03114 SelectObject (hdc, hOldFont); 03115 ReleaseDC(hwnd, hdc); 03116 03117 return 0; 03118 } 03119 03120 static LRESULT 03121 TAB_Destroy (TAB_INFO *infoPtr) 03122 { 03123 INT iItem; 03124 03125 SetWindowLongPtrW(infoPtr->hwnd, 0, 0); 03126 03127 for (iItem = infoPtr->uNumItem - 1; iItem >= 0; iItem--) 03128 { 03129 TAB_ITEM *tab = TAB_GetItem(infoPtr, iItem); 03130 03131 DPA_DeletePtr(infoPtr->items, iItem); 03132 infoPtr->uNumItem--; 03133 03134 Free(tab->pszText); 03135 Free(tab); 03136 } 03137 DPA_Destroy(infoPtr->items); 03138 infoPtr->items = NULL; 03139 03140 if (infoPtr->hwndToolTip) 03141 DestroyWindow (infoPtr->hwndToolTip); 03142 03143 if (infoPtr->hwndUpDown) 03144 DestroyWindow(infoPtr->hwndUpDown); 03145 03146 if (infoPtr->iHotTracked >= 0) 03147 KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER); 03148 03149 CloseThemeData (GetWindowTheme (infoPtr->hwnd)); 03150 03151 Free (infoPtr); 03152 return 0; 03153 } 03154 03155 /* update theme after a WM_THEMECHANGED message */ 03156 static LRESULT theme_changed(const TAB_INFO *infoPtr) 03157 { 03158 HTHEME theme = GetWindowTheme (infoPtr->hwnd); 03159 CloseThemeData (theme); 03160 OpenThemeData (infoPtr->hwnd, themeClass); 03161 return 0; 03162 } 03163 03164 static LRESULT TAB_NCCalcSize(WPARAM wParam) 03165 { 03166 if (!wParam) 03167 return 0; 03168 return WVR_ALIGNTOP; 03169 } 03170 03171 static inline LRESULT 03172 TAB_SetItemExtra (TAB_INFO *infoPtr, INT cbInfo) 03173 { 03174 TRACE("(%p %d)\n", infoPtr, cbInfo); 03175 03176 if (cbInfo < 0 || infoPtr->uNumItem) return FALSE; 03177 03178 infoPtr->cbInfo = cbInfo; 03179 return TRUE; 03180 } 03181 03182 static LRESULT TAB_RemoveImage (TAB_INFO *infoPtr, INT image) 03183 { 03184 TRACE("%p %d\n", infoPtr, image); 03185 03186 if (ImageList_Remove (infoPtr->himl, image)) 03187 { 03188 INT i, *idx; 03189 RECT r; 03190 03191 /* shift indices, repaint items if needed */ 03192 for (i = 0; i < infoPtr->uNumItem; i++) 03193 { 03194 idx = &TAB_GetItem(infoPtr, i)->iImage; 03195 if (*idx >= image) 03196 { 03197 if (*idx == image) 03198 *idx = -1; 03199 else 03200 (*idx)--; 03201 03202 /* repaint item */ 03203 if (TAB_InternalGetItemRect (infoPtr, i, &r, NULL)) 03204 InvalidateRect (infoPtr->hwnd, &r, TRUE); 03205 } 03206 } 03207 } 03208 03209 return 0; 03210 } 03211 03212 static LRESULT 03213 TAB_SetExtendedStyle (TAB_INFO *infoPtr, DWORD exMask, DWORD exStyle) 03214 { 03215 DWORD prevstyle = infoPtr->exStyle; 03216 03217 /* zero mask means all styles */ 03218 if (exMask == 0) exMask = ~0; 03219 03220 if (exMask & TCS_EX_REGISTERDROP) 03221 { 03222 FIXME("TCS_EX_REGISTERDROP style unimplemented\n"); 03223 exMask &= ~TCS_EX_REGISTERDROP; 03224 exStyle &= ~TCS_EX_REGISTERDROP; 03225 } 03226 03227 if (exMask & TCS_EX_FLATSEPARATORS) 03228 { 03229 if ((prevstyle ^ exStyle) & TCS_EX_FLATSEPARATORS) 03230 { 03231 infoPtr->exStyle ^= TCS_EX_FLATSEPARATORS; 03232 TAB_InvalidateTabArea(infoPtr); 03233 } 03234 } 03235 03236 return prevstyle; 03237 } 03238 03239 static inline LRESULT 03240 TAB_GetExtendedStyle (const TAB_INFO *infoPtr) 03241 { 03242 return infoPtr->exStyle; 03243 } 03244 03245 static LRESULT 03246 TAB_DeselectAll (TAB_INFO *infoPtr, BOOL excludesel) 03247 { 03248 BOOL paint = FALSE; 03249 INT i, selected = infoPtr->iSelected; 03250 03251 TRACE("(%p, %d)\n", infoPtr, excludesel); 03252 03253 if (!(infoPtr->dwStyle & TCS_BUTTONS)) 03254 return 0; 03255 03256 for (i = 0; i < infoPtr->uNumItem; i++) 03257 { 03258 if ((TAB_GetItem(infoPtr, i)->dwState & TCIS_BUTTONPRESSED) && 03259 (selected != i)) 03260 { 03261 TAB_GetItem(infoPtr, i)->dwState &= ~TCIS_BUTTONPRESSED; 03262 paint = TRUE; 03263 } 03264 } 03265 03266 if (!excludesel && (selected != -1)) 03267 { 03268 TAB_GetItem(infoPtr, selected)->dwState &= ~TCIS_BUTTONPRESSED; 03269 infoPtr->iSelected = -1; 03270 paint = TRUE; 03271 } 03272 03273 if (paint) 03274 TAB_InvalidateTabArea (infoPtr); 03275 03276 return 0; 03277 } 03278 03279 /*** 03280 * DESCRIPTION: 03281 * Processes WM_STYLECHANGED messages. 03282 * 03283 * PARAMETER(S): 03284 * [I] infoPtr : valid pointer to the tab data structure 03285 * [I] wStyleType : window style type (normal or extended) 03286 * [I] lpss : window style information 03287 * 03288 * RETURN: 03289 * Zero 03290 */ 03291 static INT TAB_StyleChanged(TAB_INFO *infoPtr, WPARAM wStyleType, 03292 const STYLESTRUCT *lpss) 03293 { 03294 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n", 03295 wStyleType, lpss->styleOld, lpss->styleNew); 03296 03297 if (wStyleType != GWL_STYLE) return 0; 03298 03299 infoPtr->dwStyle = lpss->styleNew; 03300 03301 TAB_SetItemBounds (infoPtr); 03302 InvalidateRect(infoPtr->hwnd, NULL, TRUE); 03303 03304 return 0; 03305 } 03306 03307 static LRESULT WINAPI 03308 TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 03309 { 03310 TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd); 03311 03312 TRACE("hwnd=%p msg=%x wParam=%lx lParam=%lx\n", hwnd, uMsg, wParam, lParam); 03313 if (!infoPtr && (uMsg != WM_CREATE)) 03314 return DefWindowProcW (hwnd, uMsg, wParam, lParam); 03315 03316 switch (uMsg) 03317 { 03318 case TCM_GETIMAGELIST: 03319 return TAB_GetImageList (infoPtr); 03320 03321 case TCM_SETIMAGELIST: 03322 return TAB_SetImageList (infoPtr, (HIMAGELIST)lParam); 03323 03324 case TCM_GETITEMCOUNT: 03325 return TAB_GetItemCount (infoPtr); 03326 03327 case TCM_GETITEMA: 03328 case TCM_GETITEMW: 03329 return TAB_GetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_GETITEMW); 03330 03331 case TCM_SETITEMA: 03332 case TCM_SETITEMW: 03333 return TAB_SetItemT (infoPtr, (INT)wParam, (LPTCITEMW)lParam, uMsg == TCM_SETITEMW); 03334 03335 case TCM_DELETEITEM: 03336 return TAB_DeleteItem (infoPtr, (INT)wParam); 03337 03338 case TCM_DELETEALLITEMS: 03339 return TAB_DeleteAllItems (infoPtr); 03340 03341 case TCM_GETITEMRECT: 03342 return TAB_GetItemRect (infoPtr, (INT)wParam, (LPRECT)lParam); 03343 03344 case TCM_GETCURSEL: 03345 return TAB_GetCurSel (infoPtr); 03346 03347 case TCM_HITTEST: 03348 return TAB_HitTest (infoPtr, (LPTCHITTESTINFO)lParam); 03349 03350 case TCM_SETCURSEL: 03351 return TAB_SetCurSel (infoPtr, (INT)wParam); 03352 03353 case TCM_INSERTITEMA: 03354 case TCM_INSERTITEMW: 03355 return TAB_InsertItemT (infoPtr, (INT)wParam, (TCITEMW*)lParam, uMsg == TCM_INSERTITEMW); 03356 03357 case TCM_SETITEMEXTRA: 03358 return TAB_SetItemExtra (infoPtr, (INT)wParam); 03359 03360 case TCM_ADJUSTRECT: 03361 return TAB_AdjustRect (infoPtr, (BOOL)wParam, (LPRECT)lParam); 03362 03363 case TCM_SETITEMSIZE: 03364 return TAB_SetItemSize (infoPtr, (INT)LOWORD(lParam), (INT)HIWORD(lParam)); 03365 03366 case TCM_REMOVEIMAGE: 03367 return TAB_RemoveImage (infoPtr, (INT)wParam); 03368 03369 case TCM_SETPADDING: 03370 return TAB_SetPadding (infoPtr, lParam); 03371 03372 case TCM_GETROWCOUNT: 03373 return TAB_GetRowCount(infoPtr); 03374 03375 case TCM_GETUNICODEFORMAT: 03376 return TAB_GetUnicodeFormat (infoPtr); 03377 03378 case TCM_SETUNICODEFORMAT: 03379 return TAB_SetUnicodeFormat (infoPtr, (BOOL)wParam); 03380 03381 case TCM_HIGHLIGHTITEM: 03382 return TAB_HighlightItem (infoPtr, (INT)wParam, (BOOL)LOWORD(lParam)); 03383 03384 case TCM_GETTOOLTIPS: 03385 return TAB_GetToolTips (infoPtr); 03386 03387 case TCM_SETTOOLTIPS: 03388 return TAB_SetToolTips (infoPtr, (HWND)wParam); 03389 03390 case TCM_GETCURFOCUS: 03391 return TAB_GetCurFocus (infoPtr); 03392 03393 case TCM_SETCURFOCUS: 03394 return TAB_SetCurFocus (infoPtr, (INT)wParam); 03395 03396 case TCM_SETMINTABWIDTH: 03397 return TAB_SetMinTabWidth(infoPtr, (INT)lParam); 03398 03399 case TCM_DESELECTALL: 03400 return TAB_DeselectAll (infoPtr, (BOOL)wParam); 03401 03402 case TCM_GETEXTENDEDSTYLE: 03403 return TAB_GetExtendedStyle (infoPtr); 03404 03405 case TCM_SETEXTENDEDSTYLE: 03406 return TAB_SetExtendedStyle (infoPtr, wParam, lParam); 03407 03408 case WM_GETFONT: 03409 return TAB_GetFont (infoPtr); 03410 03411 case WM_SETFONT: 03412 return TAB_SetFont (infoPtr, (HFONT)wParam); 03413 03414 case WM_CREATE: 03415 return TAB_Create (hwnd, lParam); 03416 03417 case WM_NCDESTROY: 03418 return TAB_Destroy (infoPtr); 03419 03420 case WM_GETDLGCODE: 03421 return DLGC_WANTARROWS | DLGC_WANTCHARS; 03422 03423 case WM_LBUTTONDOWN: 03424 return TAB_LButtonDown (infoPtr, wParam, lParam); 03425 03426 case WM_LBUTTONUP: 03427 return TAB_LButtonUp (infoPtr); 03428 03429 case WM_NOTIFY: 03430 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam); 03431 03432 case WM_RBUTTONUP: 03433 TAB_RButtonUp (infoPtr); 03434 return DefWindowProcW (hwnd, uMsg, wParam, lParam); 03435 03436 case WM_MOUSEMOVE: 03437 return TAB_MouseMove (infoPtr, wParam, lParam); 03438 03439 case WM_PRINTCLIENT: 03440 case WM_PAINT: 03441 return TAB_Paint (infoPtr, (HDC)wParam); 03442 03443 case WM_SIZE: 03444 return TAB_Size (infoPtr); 03445 03446 case WM_SETREDRAW: 03447 return TAB_SetRedraw (infoPtr, (BOOL)wParam); 03448 03449 case WM_HSCROLL: 03450 return TAB_OnHScroll(infoPtr, (int)LOWORD(wParam), (int)HIWORD(wParam)); 03451 03452 case WM_STYLECHANGED: 03453 return TAB_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam); 03454 03455 case WM_SYSCOLORCHANGE: 03456 COMCTL32_RefreshSysColors(); 03457 return 0; 03458 03459 case WM_THEMECHANGED: 03460 return theme_changed (infoPtr); 03461 03462 case WM_KILLFOCUS: 03463 TAB_KillFocus(infoPtr); 03464 case WM_SETFOCUS: 03465 TAB_FocusChanging(infoPtr); 03466 break; /* Don't disturb normal focus behavior */ 03467 03468 case WM_KEYDOWN: 03469 return TAB_KeyDown(infoPtr, wParam, lParam); 03470 03471 case WM_NCHITTEST: 03472 return TAB_NCHitTest(infoPtr, lParam); 03473 03474 case WM_NCCALCSIZE: 03475 return TAB_NCCalcSize(wParam); 03476 03477 default: 03478 if (uMsg >= WM_USER && uMsg < WM_APP && !COMCTL32_IsReflectedMessage(uMsg)) 03479 WARN("unknown msg %04x wp=%08lx lp=%08lx\n", 03480 uMsg, wParam, lParam); 03481 break; 03482 } 03483 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 03484 } 03485 03486 03487 void 03488 TAB_Register (void) 03489 { 03490 WNDCLASSW wndClass; 03491 03492 ZeroMemory (&wndClass, sizeof(WNDCLASSW)); 03493 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; 03494 wndClass.lpfnWndProc = TAB_WindowProc; 03495 wndClass.cbClsExtra = 0; 03496 wndClass.cbWndExtra = sizeof(TAB_INFO *); 03497 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW); 03498 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); 03499 wndClass.lpszClassName = WC_TABCONTROLW; 03500 03501 RegisterClassW (&wndClass); 03502 } 03503 03504 03505 void 03506 TAB_Unregister (void) 03507 { 03508 UnregisterClassW (WC_TABCONTROLW, NULL); 03509 } Generated on Sun May 27 2012 04:23:04 for ReactOS by
1.7.6.1
|