From 7a6cf75f7685654a7b9525a51d6565bb9bc5eda8 Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Wed, 25 Oct 2000 20:31:18 +0000 Subject: [PATCH] Implement TCS_VERTICAL style. Fix tab borders to have shading that matches Windows. Center image and text in tabs. Size tabs to completely fill each row with multiline style. Fix positioning of tabs for TCS_BOTTOM style. Fix various drawing issues. --- dlls/comctl32/tab.c | 1069 +++++++++++++++++++++++++++---------------- 1 file changed, 671 insertions(+), 398 deletions(-) diff --git a/dlls/comctl32/tab.c b/dlls/comctl32/tab.c index c9d14ad0bf0..26fbd050c69 100644 --- a/dlls/comctl32/tab.c +++ b/dlls/comctl32/tab.c @@ -7,8 +7,10 @@ * * TODO: * Image list support - * Multiline support * Unicode support + * + * FIXME: + * UpDown control not displayed until after a tab is clicked on */ #include @@ -17,6 +19,7 @@ #include "commctrl.h" #include "debugtools.h" #include "cache.h" +#include DEFAULT_DEBUG_CHANNEL(tab); @@ -72,7 +75,8 @@ typedef struct #define CONTROL_BORDER_SIZEX 2 #define CONTROL_BORDER_SIZEY 2 #define BUTTON_SPACINGX 4 -#define FLAT_BTN_SPACINGX 8 +#define BUTTON_SPACINGY 4 +#define FLAT_BTN_SPACINGX 8 #define DEFAULT_TAB_WIDTH 96 #define TAB_GetInfoPtr(hwnd) ((TAB_INFO *)GetWindowLongA(hwnd,0)) @@ -87,7 +91,7 @@ typedef struct * Prototypes */ static void TAB_Refresh (HWND hwnd, HDC hdc); -static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr); +static void TAB_InvalidateTabArea(HWND hwnd, TAB_INFO* infoPtr); static void TAB_EnsureSelectionVisible(HWND hwnd, TAB_INFO* infoPtr); static void TAB_DrawItem(HWND hwnd, HDC hdc, INT iItem); static void TAB_DrawItemInterior(HWND hwnd, HDC hdc, INT iItem, RECT* drawRect); @@ -105,7 +109,6 @@ TAB_SendSimpleNotify (HWND hwnd, UINT code) (WPARAM) nmhdr.idFrom, (LPARAM) &nmhdr); } - static VOID TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg, WPARAM wParam, LPARAM lParam) @@ -123,8 +126,6 @@ TAB_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg, SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg); } - - static LRESULT TAB_GetCurSel (HWND hwnd) { @@ -150,15 +151,14 @@ TAB_GetToolTips (HWND hwnd, WPARAM wParam, LPARAM lParam) return infoPtr->hwndToolTip; } - static LRESULT TAB_SetCurSel (HWND hwnd,WPARAM wParam) { TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd); - INT iItem=(INT) wParam; + INT iItem = (INT)wParam; INT prevItem; - prevItem=-1; + prevItem = -1; if ((iItem >= 0) && (iItem < infoPtr->uNumItem)) { prevItem=infoPtr->iSelected; infoPtr->iSelected=iItem; @@ -180,7 +180,7 @@ TAB_SetCurFocus (HWND hwnd,WPARAM wParam) FIXME("Should set input focus\n"); } else { if (infoPtr->iSelected != iItem || infoPtr->uFocus == -1 ) { - infoPtr->uFocus=iItem; + infoPtr->uFocus = iItem; if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) { infoPtr->iSelected = iItem; TAB_SendSimpleNotify(hwnd, TCN_SELCHANGE); @@ -222,92 +222,127 @@ static BOOL TAB_InternalGetItemRect( RECT tmpItemRect,clientRect; LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE); - /* - * Perform a sanity check and a trivial visibility check. - */ - if ( (infoPtr->uNumItem <=0) || + /* Perform a sanity check and a trivial visibility check. */ + if ( (infoPtr->uNumItem <= 0) || (itemIndex >= infoPtr->uNumItem) || - (!(lStyle &TCS_MULTILINE) && (itemIndex < infoPtr->leftmostVisible)) ) + (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (itemIndex < infoPtr->leftmostVisible)) ) return FALSE; /* * Avoid special cases in this procedure by assigning the "out" * parameters if the caller didn't supply them */ - if (itemRect==NULL) + if (itemRect == NULL) itemRect = &tmpItemRect; - /* - * Retrieve the unmodified item rect. - */ + /* Retrieve the unmodified item rect. */ *itemRect = infoPtr->items[itemIndex].rect; - /* - * calculate the times bottom and top based on the row - */ + /* calculate the times bottom and top based on the row */ GetClientRect(hwnd, &clientRect); - if (lStyle & TCS_BOTTOM) + if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL)) { - itemRect->bottom = clientRect.bottom - - SELECTED_TAB_OFFSET - - itemRect->top * (infoPtr->tabHeight - 2); + itemRect->bottom = clientRect.bottom - + SELECTED_TAB_OFFSET - + itemRect->top * (infoPtr->tabHeight - 2) - + ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0); - itemRect->top = clientRect.bottom - + itemRect->top = clientRect.bottom - infoPtr->tabHeight - - itemRect->top * ( infoPtr->tabHeight - 2); + itemRect->top * (infoPtr->tabHeight - 2) - + ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0); } - else + else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL)) + { + itemRect->right = clientRect.right - SELECTED_TAB_OFFSET - itemRect->left * (infoPtr->tabHeight - 2) - + ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0); + itemRect->left = clientRect.right - infoPtr->tabHeight - itemRect->left * (infoPtr->tabHeight - 2) - + ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0); + } + else if((lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM)) + { + itemRect->right = clientRect.left + infoPtr->tabHeight + itemRect->left * (infoPtr->tabHeight - 2) + + ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0); + itemRect->left = clientRect.left + SELECTED_TAB_OFFSET + itemRect->left * (infoPtr->tabHeight - 2) + + ((lStyle & TCS_BUTTONS) ? itemRect->left * BUTTON_SPACINGY : 0); + } + else if(!(lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM)) /* not TCS_BOTTOM and not TCS_VERTICAL */ { itemRect->bottom = clientRect.top + infoPtr->tabHeight + - itemRect->top * (infoPtr->tabHeight - 2); + itemRect->top * (infoPtr->tabHeight - 2) + + ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0); itemRect->top = clientRect.top + - SELECTED_TAB_OFFSET+ - itemRect->top * (infoPtr->tabHeight - 2); + SELECTED_TAB_OFFSET + + itemRect->top * (infoPtr->tabHeight - 2) + + ((lStyle & TCS_BUTTONS) ? itemRect->top * BUTTON_SPACINGY : 0); } /* * "scroll" it to make sure the item at the very left of the * tab control is the leftmost visible tab. */ - OffsetRect(itemRect, + if(lStyle & TCS_VERTICAL) + { + OffsetRect(itemRect, + 0, + -(clientRect.bottom - infoPtr->items[infoPtr->leftmostVisible].rect.bottom)); + + /* + * Move the rectangle so the first item is slightly offset from + * the bottom of the tab control. + */ + OffsetRect(itemRect, + 0, + -SELECTED_TAB_OFFSET); + + } else + { + OffsetRect(itemRect, -infoPtr->items[infoPtr->leftmostVisible].rect.left, 0); - /* - * Move the rectangle so the first item is slightly offset from - * the left of the tab control. - */ - OffsetRect(itemRect, + /* + * Move the rectangle so the first item is slightly offset from + * the left of the tab control. + */ + OffsetRect(itemRect, SELECTED_TAB_OFFSET, 0); + } - - /* - * Now, calculate the position of the item as if it were selected. - */ + /* Now, calculate the position of the item as if it were selected. */ if (selectedRect!=NULL) { CopyRect(selectedRect, itemRect); - /* - * The rectangle of a selected item is a bit wider. - */ - InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0); + /* The rectangle of a selected item is a bit wider. */ + if(lStyle & TCS_VERTICAL) + InflateRect(selectedRect, 0, SELECTED_TAB_OFFSET); + else + InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0); - /* - * If it also a bit higher. - */ - if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM) + /* If it also a bit higher. */ + if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL)) { - selectedRect->top -=2; /* the border is thicker on the bottom */ - selectedRect->bottom +=SELECTED_TAB_OFFSET; + selectedRect->top -= 2; /* the border is thicker on the bottom */ + selectedRect->bottom += SELECTED_TAB_OFFSET; + } + else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL)) + { + selectedRect->left -= 2; /* the border is thicker on the right */ + selectedRect->right += SELECTED_TAB_OFFSET; + } + else if(lStyle & TCS_VERTICAL) + { + selectedRect->left -= SELECTED_TAB_OFFSET; + selectedRect->right += 1; } else { - selectedRect->top -=SELECTED_TAB_OFFSET; - selectedRect->bottom+=1; + selectedRect->top -= SELECTED_TAB_OFFSET; + selectedRect->bottom += 1; } } @@ -335,19 +370,19 @@ static LRESULT TAB_KeyUp( switch (keyCode) { case VK_LEFT: - newItem = infoPtr->uFocus-1; + newItem = infoPtr->uFocus - 1; break; case VK_RIGHT: - newItem = infoPtr->uFocus+1; + newItem = infoPtr->uFocus + 1; break; } /* * If we changed to a valid item, change the selection */ - if ( (newItem >= 0) && + if ((newItem >= 0) && (newItem < infoPtr->uNumItem) && - (infoPtr->uFocus != newItem) ) + (infoPtr->uFocus != newItem)) { if (!TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)) { @@ -417,14 +452,14 @@ static HWND TAB_InternalHitTest ( { TAB_InternalGetItemRect(hwnd, infoPtr, iCount, &rect, NULL); - if (PtInRect (&rect, pt)) + if (PtInRect(&rect, pt)) { *flags = TCHT_ONITEM; return iCount; } } - *flags=TCHT_NOWHERE; + *flags = TCHT_NOWHERE; return -1; } @@ -432,9 +467,9 @@ static LRESULT TAB_HitTest (HWND hwnd, WPARAM wParam, LPARAM lParam) { TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd); - LPTCHITTESTINFO lptest=(LPTCHITTESTINFO) lParam; + LPTCHITTESTINFO lptest = (LPTCHITTESTINFO) lParam; - return TAB_InternalHitTest (hwnd, infoPtr,lptest->pt,&lptest->flags); + return TAB_InternalHitTest (hwnd, infoPtr, lptest->pt, &lptest->flags); } /****************************************************************************** @@ -471,7 +506,7 @@ TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam) { TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd); POINT pt; - INT newItem,dummy; + INT newItem, dummy; if (infoPtr->hwndToolTip) TAB_RelayEvent (infoPtr->hwndToolTip, hwnd, @@ -488,14 +523,13 @@ TAB_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam) pt.x = (INT)LOWORD(lParam); pt.y = (INT)HIWORD(lParam); - newItem=TAB_InternalHitTest (hwnd, infoPtr,pt,&dummy); + newItem = TAB_InternalHitTest (hwnd, infoPtr, pt, &dummy); TRACE("On Tab, item %d\n", newItem); - - if ( (newItem!=-1) && - (infoPtr->iSelected != newItem) ) + + if ((newItem != -1) && (infoPtr->iSelected != newItem)) { - if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING)!=TRUE) + if (TAB_SendSimpleNotify(hwnd, TCN_SELCHANGING) != TRUE) { infoPtr->iSelected = newItem; infoPtr->uFocus = newItem; @@ -724,57 +758,72 @@ static LRESULT TAB_AdjustRect( LPRECT prc) { TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd); + DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE); - if (fLarger) + if(lStyle & TCS_VERTICAL) { - /* - * Go from display rectangle - */ + if (fLarger) /* Go from display rectangle */ + { + /* Add the height of the tabs. */ + if (lStyle & TCS_BOTTOM) + prc->right += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2; + else + prc->left -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2; - /* - * Add the height of the tabs. - */ - if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM) - prc->bottom += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2; - else - prc->top -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2; + /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */ + /* Inflate the rectangle for the padding */ + InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY); - /* - * Inflate the rectangle for the padding - */ - InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY); + /* Inflate for the border */ + InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX); + } + else /* Go from window rectangle. */ + { + /* FIXME: not sure if these InflateRect's need to have different values for TCS_VERTICAL */ + /* Deflate the rectangle for the border */ + InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX); - /* - * Inflate for the border - */ - InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX); + /* Deflate the rectangle for the padding */ + InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY); + + /* Remove the height of the tabs. */ + if (lStyle & TCS_BOTTOM) + prc->right -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2; + else + prc->left += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2; + } } - else - { - /* - * Go from window rectangle. - */ - - /* - * Deflate the rectangle for the border - */ - InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX); + else { + if (fLarger) /* Go from display rectangle */ + { + /* Add the height of the tabs. */ + if (lStyle & TCS_BOTTOM) + prc->bottom += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2; + else + prc->top -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2; - /* - * Deflate the rectangle for the padding - */ - InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY); + /* Inflate the rectangle for the padding */ + InflateRect(prc, DISPLAY_AREA_PADDINGX, DISPLAY_AREA_PADDINGY); - /* - * Remove the height of the tabs. - */ - if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM) - prc->bottom -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2; - else - prc->top += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2; + /* Inflate for the border */ + InflateRect(prc, CONTROL_BORDER_SIZEX, CONTROL_BORDER_SIZEX); + } + else /* Go from window rectangle. */ + { + /* Deflate the rectangle for the border */ + InflateRect(prc, -CONTROL_BORDER_SIZEX, -CONTROL_BORDER_SIZEX); + /* Deflate the rectangle for the padding */ + InflateRect(prc, -DISPLAY_AREA_PADDINGX, -DISPLAY_AREA_PADDINGY); + + /* Remove the height of the tabs. */ + if (lStyle & TCS_BOTTOM) + prc->bottom -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2; + else + prc->top += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2; + } } - + return 0; } @@ -820,6 +869,8 @@ static void TAB_SetupScrolling( const RECT* clientRect) { INT maxRange = 0; + DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE); + if (infoPtr->needsScrolling) { RECT controlPos; @@ -828,23 +879,42 @@ static void TAB_SetupScrolling( /* * Calculate the position of the scroll control. */ - controlPos.right = clientRect->right; - controlPos.left = controlPos.right - 2*GetSystemMetrics(SM_CXHSCROLL); - - if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM) + if(lStyle & TCS_VERTICAL) { - controlPos.top = clientRect->bottom - infoPtr->tabHeight; - controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL); + controlPos.right = clientRect->right; + controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL); + + if (lStyle & TCS_BOTTOM) + { + controlPos.top = clientRect->bottom - infoPtr->tabHeight; + controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL); + } + else + { + controlPos.bottom = clientRect->top + infoPtr->tabHeight; + controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL); + } } else { - controlPos.bottom = clientRect->top + infoPtr->tabHeight; - controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL); + controlPos.right = clientRect->right; + controlPos.left = controlPos.right - 2 * GetSystemMetrics(SM_CXHSCROLL); + + if (lStyle & TCS_BOTTOM) + { + controlPos.top = clientRect->bottom - infoPtr->tabHeight; + controlPos.bottom = controlPos.top + GetSystemMetrics(SM_CYHSCROLL); + } + else + { + controlPos.bottom = clientRect->top + infoPtr->tabHeight; + controlPos.top = controlPos.bottom - GetSystemMetrics(SM_CYHSCROLL); + } } /* * If we don't have a scroll control yet, we want to create one. - * If we have one, we want to make sure it's positioned right. + * If we have one, we want to make sure it's positioned properly. */ if (infoPtr->hwndUpDown==0) { @@ -877,7 +947,7 @@ static void TAB_SetupScrolling( { vsize = clientRect->right - (controlPos.right - controlPos.left + 1); maxRange = infoPtr->uNumItem; - tabwidth = infoPtr->items[maxRange-1].rect.right; + tabwidth = infoPtr->items[maxRange - 1].rect.right; for(; maxRange > 0; maxRange--) { @@ -891,13 +961,9 @@ static void TAB_SetupScrolling( } else { - /* - * If we once had a scroll control... hide it. - */ + /* If we once had a scroll control... hide it */ if (infoPtr->hwndUpDown!=0) - { ShowWindow(infoPtr->hwndUpDown, SW_HIDE); - } } if (infoPtr->hwndUpDown) SendMessageA(infoPtr->hwndUpDown, UDM_SETRANGE32, 0, maxRange); @@ -925,6 +991,9 @@ static void TAB_SetItemBounds (HWND hwnd) HDC hdc; RECT clientRect; SIZE size; + INT iTemp; + RECT* rcItem; + INT iIndex; /* * We need to get text information so we need a DC and we need to select @@ -940,32 +1009,33 @@ static void TAB_SetItemBounds (HWND hwnd) * of the control. */ GetClientRect(hwnd, &clientRect); - - /* - * The leftmost item will be "0" aligned - */ + + /* if TCS_VERTICAL then swap the height and width so this code places the tabs along the top of the rectangle */ + /* and we can just rotate them after rather than duplicate all of the below code */ + if(lStyle & TCS_VERTICAL) + { + iTemp = clientRect.bottom; + clientRect.bottom = clientRect.right; + clientRect.right = iTemp; + } + + /* The leftmost item will be "0" aligned */ curItemLeftPos = 0; curItemRowCount = 0; - if ( !(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet) ) + if (!(lStyle & TCS_FIXEDWIDTH) && !((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet) ) { int item_height; int icon_height = 0; - /* - * Use the current font to determine the height of a tab. - */ + /* Use the current font to determine the height of a tab. */ GetTextMetricsA(hdc, &fontMetrics); - /* - * Get the icon height - */ + /* Get the icon height */ if (infoPtr->himl) ImageList_GetIconSize(infoPtr->himl, 0, &icon_height); - /* - * Take the highest between font or icon - */ + /* Take the highest between font or icon */ if (fontMetrics.tmHeight > icon_height) item_height = fontMetrics.tmHeight; else @@ -975,43 +1045,37 @@ static void TAB_SetItemBounds (HWND hwnd) * Make sure there is enough space for the letters + icon + growing the * selected item + extra space for the selected item. */ - infoPtr->tabHeight = item_height + 2*VERTICAL_ITEM_PADDING + - SELECTED_TAB_OFFSET; + infoPtr->tabHeight = item_height + 2 * VERTICAL_ITEM_PADDING + + SELECTED_TAB_OFFSET; + } for (curItem = 0; curItem < infoPtr->uNumItem; curItem++) { - /* - * Set the leftmost position of the tab. - */ + /* Set the leftmost position of the tab. */ infoPtr->items[curItem].rect.left = curItemLeftPos; if ( (lStyle & TCS_FIXEDWIDTH) || ((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet)) { infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left + infoPtr->tabWidth + - 2*HORIZONTAL_ITEM_PADDING; + 2 * HORIZONTAL_ITEM_PADDING; } else { int icon_width = 0; int num = 2; - /* - * Calculate how wide the tab is depending on the text it contains - */ + /* Calculate how wide the tab is depending on the text it contains */ GetTextExtentPoint32A(hdc, infoPtr->items[curItem].pszText, - lstrlenA(infoPtr->items[curItem].pszText), &size); - /* - * under Windows, there seems to be a minimum width of 2x the height - * for button style tabs - */ - if (lStyle & TCS_BUTTONS) - size.cx = max(size.cx, 2*infoPtr->tabHeight); + strlen(infoPtr->items[curItem].pszText), &size); - /* - * Add the icon width - */ + /* under Windows, there seems to be a minimum width of 2x the height + * for button style tabs */ + if (lStyle & TCS_BUTTONS) + size.cx = max(size.cx, 2 * (infoPtr->tabHeight - 2)); + + /* Add the icon width */ if (infoPtr->himl) { ImageList_GetIconSize(infoPtr->himl, &icon_width, 0); @@ -1020,7 +1084,7 @@ static void TAB_SetItemBounds (HWND hwnd) infoPtr->items[curItem].rect.right = infoPtr->items[curItem].rect.left + size.cx + icon_width + - num*HORIZONTAL_ITEM_PADDING; + num * HORIZONTAL_ITEM_PADDING; } /* @@ -1032,13 +1096,14 @@ static void TAB_SetItemBounds (HWND hwnd) * */ - if ((lStyle & TCS_MULTILINE)&& + if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->items[curItem].rect.right > clientRect.right)) { - infoPtr->items[curItem].rect.right -= + infoPtr->items[curItem].rect.right -= infoPtr->items[curItem].rect.left; + infoPtr->items[curItem].rect.left = 0; - curItemRowCount ++; + curItemRowCount++; } infoPtr->items[curItem].rect.bottom = 0; @@ -1057,7 +1122,7 @@ static void TAB_SetItemBounds (HWND hwnd) */ if (lStyle & TCS_BUTTONS) { - curItemLeftPos = infoPtr->items[curItem].rect.right + BUTTON_SPACINGX; + curItemLeftPos = infoPtr->items[curItem].rect.right + 1; if (lStyle & TCS_FLATBUTTONS) curItemLeftPos += FLAT_BTN_SPACINGX; } @@ -1065,30 +1130,27 @@ static void TAB_SetItemBounds (HWND hwnd) curItemLeftPos = infoPtr->items[curItem].rect.right; } - if (!(lStyle & TCS_MULTILINE)) + if (!((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL))) { /* * Check if we need a scrolling control. */ - infoPtr->needsScrolling = (curItemLeftPos + (2*SELECTED_TAB_OFFSET) > + infoPtr->needsScrolling = (curItemLeftPos + (2 * SELECTED_TAB_OFFSET) > clientRect.right); /* Don't need scrolling, then update infoPtr->leftmostVisible */ if(!infoPtr->needsScrolling) infoPtr->leftmostVisible = 0; - TAB_SetupScrolling(hwnd, infoPtr, &clientRect); + TAB_SetupScrolling(hwnd, infoPtr, &clientRect); } - /* - * Set the number of rows - */ - + /* Set the number of rows */ infoPtr->uNumRows = curItemRowCount; - if ((lStyle & TCS_MULTILINE)&&(infoPtr->uNumItem > 0)) + if (((lStyle & TCS_MULTILINE) || (lStyle & TCS_VERTICAL)) && (infoPtr->uNumItem > 0)) { - INT widthDiff,remainder; + INT widthDiff, remainder; INT tabPerRow,remTab; INT iRow,iItm; INT iIndexStart=0,iIndexEnd=0, iCount=0; @@ -1106,25 +1168,30 @@ static void TAB_SetItemBounds (HWND hwnd) iItmuNumItem; iItm++,iCount++) { - if (iCount >= ((iRow= ((iRowitems[iItm].rect.right -= infoPtr->items[iItm].rect.left; infoPtr->items[iItm].rect.left = 0; + /* shift the item to the right to place it as the next item in this row */ + infoPtr->items[iItm].rect.left += curItemLeftPos; + infoPtr->items[iItm].rect.right += curItemLeftPos; infoPtr->items[iItm].rect.top = iRow; - infoPtr->items[iItm].rect.left +=curItemLeftPos; - infoPtr->items[iItm].rect.right +=curItemLeftPos; if (lStyle & TCS_BUTTONS) { - curItemLeftPos = infoPtr->items[iItm].rect.right + BUTTON_SPACINGX; + curItemLeftPos = infoPtr->items[iItm].rect.right + 1; if (lStyle & TCS_FLATBUTTONS) curItemLeftPos += FLAT_BTN_SPACINGX; } @@ -1134,7 +1201,6 @@ static void TAB_SetItemBounds (HWND hwnd) /* * Justify the rows - * */ { while(iIndexStart < infoPtr->uNumItem) @@ -1142,6 +1208,7 @@ static void TAB_SetItemBounds (HWND hwnd) /* * find the indexs of the row */ + /* find the first item on the next row */ for (iIndexEnd=iIndexStart; (iIndexEnd < infoPtr->uNumItem) && (infoPtr->items[iIndexEnd].rect.top == @@ -1154,35 +1221,61 @@ static void TAB_SetItemBounds (HWND hwnd) * client area * */ - widthDiff = clientRect.right - (2*SELECTED_TAB_OFFSET) - - infoPtr->items[iIndexEnd-1].rect.right; - - iCount = iIndexEnd-iIndexStart; + /* find the amount of space remaining on this row */ + widthDiff = clientRect.right - (2 * SELECTED_TAB_OFFSET) - + infoPtr->items[iIndexEnd - 1].rect.right; - if (iCount>1) + /* iCount is the number of tab items on this row */ + iCount = iIndexEnd - iIndexStart; + + + if (iCount > 1) { - INT iIndex; remainder = widthDiff % iCount; widthDiff = widthDiff / iCount; + /* add widthDiff/iCount, or extra space/items on row, to each item on this row */ for (iIndex=iIndexStart,iCount=0; iIndex < iIndexEnd; iIndex++,iCount++) { - infoPtr->items[iIndex].rect.left +=iCount*widthDiff; - infoPtr->items[iIndex].rect.right +=(iCount+1)*widthDiff; + infoPtr->items[iIndex].rect.left += iCount * widthDiff; + infoPtr->items[iIndex].rect.right += (iCount + 1) * widthDiff; } - infoPtr->items[iIndex-1].rect.right += remainder; + infoPtr->items[iIndex - 1].rect.right += remainder; + } + else /* we have only one item on this row, make it take up the entire row */ + { + infoPtr->items[iIndexStart].rect.left = clientRect.left; + infoPtr->items[iIndexStart].rect.right = clientRect.right - 4; } - iIndexStart=iIndexEnd; + + iIndexStart = iIndexEnd; } } } + /* if TCS_VERTICAL rotate the tabs so they are along the side of the clientRect */ + if(lStyle & TCS_VERTICAL) + { + RECT rcOriginal; + for(iIndex = 0; iIndex < infoPtr->uNumItem; iIndex++) + { + rcItem = &(infoPtr->items[iIndex].rect); + + rcOriginal = *rcItem; + + /* this is rotating the items by 90 degrees around the center of the control */ + rcItem->top = (clientRect.right - (rcOriginal.left - clientRect.left)) - (rcOriginal.right - rcOriginal.left); + rcItem->bottom = rcItem->top + (rcOriginal.right - rcOriginal.left); + rcItem->left = rcOriginal.top; + rcItem->right = rcOriginal.bottom; + } + } + TAB_EnsureSelectionVisible(hwnd,infoPtr); TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL); - /* - * Cleanup - */ + + /* Cleanup */ SelectObject (hdc, hOldFont); ReleaseDC (hwnd, hdc); } @@ -1220,14 +1313,7 @@ TAB_DrawItemInterior /* * Get the rectangle for the item. */ - isVisible = TAB_InternalGetItemRect - ( - hwnd, - infoPtr, - iItem, - &itemRect, - &selectedRect - ); + isVisible = TAB_InternalGetItemRect(hwnd, infoPtr, iItem, &itemRect, &selectedRect); if (!isVisible) return; @@ -1265,27 +1351,24 @@ TAB_DrawItemInterior /* * Text pen */ - holdPen = SelectObject(hdc, htextPen); + holdPen = SelectObject(hdc, htextPen); - oldBkMode = SetBkMode(hdc, TRANSPARENT); - SetTextColor - ( - hdc, - GetSysColor - ( - (iItem == infoPtr->iHotTracked) ? COLOR_HIGHLIGHT : COLOR_BTNTEXT - ) - ); + oldBkMode = SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, GetSysColor((iItem == infoPtr->iHotTracked) ? COLOR_HIGHLIGHT : COLOR_BTNTEXT)); /* * Deflate the rectangle to acount for the padding */ - InflateRect(drawRect, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING); + if(lStyle & TCS_VERTICAL) + InflateRect(drawRect, -VERTICAL_ITEM_PADDING, -HORIZONTAL_ITEM_PADDING); + else + InflateRect(drawRect, -HORIZONTAL_ITEM_PADDING, -VERTICAL_ITEM_PADDING); + /* * if owner draw, tell the owner to draw */ - if ( (lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd) ) + if ((lStyle & TCS_OWNERDRAWFIXED) && GetParent(hwnd)) { DRAWITEMSTRUCT dis; UINT id; @@ -1318,8 +1401,38 @@ TAB_DrawItemInterior } else { + INT cx; + INT cy; UINT uHorizAlign; - + RECT rcTemp; + RECT rcImage; + LOGFONTA logfont; + HFONT hFont; + HFONT hOldFont = 0; /* stop uninitialized warning */ + + INT nEscapement; + INT nOrientation; + INT iPointSize; + + /* used to center the icon and text in the tab */ + RECT rcText; + INT center_offset; + + /* set rcImage to drawRect, we will use top & left in our ImageList_Draw call */ + rcImage = *drawRect; + + rcTemp = *drawRect; + + /* + * Setup for text output + */ + oldBkMode = SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, GetSysColor((iItem == infoPtr->iHotTracked) ? COLOR_HIGHLIGHT : COLOR_BTNTEXT)); + + /* get the rectangle that the text fits in */ + DrawTextA(hdc, infoPtr->items[iItem].pszText, -1, + &rcText, DT_CALCRECT); + /* * If not owner draw, then do the drawing ourselves. * @@ -1327,38 +1440,118 @@ TAB_DrawItemInterior */ if (infoPtr->himl && (infoPtr->items[iItem].mask & TCIF_IMAGE)) { - INT cx; - INT cy; + ImageList_GetIconSize(infoPtr->himl, &cx, &cy); + + if(lStyle & TCS_VERTICAL) + center_offset = ((drawRect->bottom - drawRect->top) - (cy + VERTICAL_ITEM_PADDING + (rcText.right - rcText.left))) / 2; + else + center_offset = ((drawRect->right - drawRect->left) - (cx + HORIZONTAL_ITEM_PADDING + (rcText.right - rcText.left))) / 2; + + if((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM)) + { + /* rcImage.left = drawRect->left; */ /* explicit from above rcImage = *drawRect */ + rcImage.top = drawRect->top + center_offset; + rcImage.left = drawRect->right - cx; /* if tab is TCS_VERTICAL and TCS_BOTTOM, the text is drawn from the */ + /* right side of the tab, but the image still uses the left as its x position */ + /* this keeps the image always drawn off of the same side of the tab */ + drawRect->top = rcImage.top + (cx + VERTICAL_ITEM_PADDING); + } + else if(lStyle & TCS_VERTICAL) + { + /* rcImage.left = drawRect->left; */ /* explicit from above rcImage = *drawRect */ + rcImage.top = drawRect->bottom - cy - center_offset; + + drawRect->bottom = rcImage.top - VERTICAL_ITEM_PADDING; + } + else /* normal style, whether TCS_BOTTOM or not */ + { + rcImage.left = drawRect->left + center_offset; + /* rcImage.top = drawRect->top; */ /* explicit from above rcImage = *drawRect */ + + drawRect->left = rcImage.left + cx + HORIZONTAL_ITEM_PADDING; + } ImageList_Draw ( infoPtr->himl, infoPtr->items[iItem].iImage, hdc, - drawRect->left, - drawRect->top + 1, + rcImage.left, + rcImage.top + 1, ILD_NORMAL ); - ImageList_GetIconSize(infoPtr->himl, &cx, &cy); - drawRect->left += (cx + HORIZONTAL_ITEM_PADDING); + } else /* no image, so just shift the drawRect borders around */ + { + if(lStyle & TCS_VERTICAL) + { + center_offset = ((drawRect->bottom - drawRect->top) - (rcText.right - rcText.left)) / 2; + + if(lStyle & TCS_BOTTOM) + drawRect->top+=center_offset; + else + drawRect->bottom-=center_offset; + } + else + { + center_offset = ((drawRect->right - drawRect->left) - (rcText.right - rcText.left)) / 2; + drawRect->left+=center_offset; + } } - /* - * Draw the text; - */ + /* Draw the text */ if (lStyle & TCS_RIGHTJUSTIFY) uHorizAlign = DT_CENTER; else uHorizAlign = DT_LEFT; - DrawTextA - ( - hdc, - infoPtr->items[iItem].pszText, - lstrlenA(infoPtr->items[iItem].pszText), - drawRect, - uHorizAlign | DT_SINGLELINE | DT_VCENTER - ); + if(lStyle & TCS_VERTICAL) /* if we are vertical rotate the text and each character */ + { + if(lStyle & TCS_BOTTOM) + { + nEscapement = -900; + nOrientation = -900; + } + else + { + nEscapement = 900; + nOrientation = 900; + } + } + + /* to get a font with the escapement and orientation we are looking for, we need to */ + /* call CreateFontIndirectA, which requires us to set the values of the logfont we pass in */ + if(lStyle & TCS_VERTICAL) + { + iPointSize = 9; + + lstrcpyA(logfont.lfFaceName, "Arial"); + logfont.lfHeight = -MulDiv(iPointSize, GetDeviceCaps(hdc, LOGPIXELSY), 72); + logfont.lfWeight = FW_NORMAL; + logfont.lfItalic = 0; + logfont.lfUnderline = 0; + logfont.lfStrikeOut = 0; + + logfont.lfEscapement = nEscapement; + logfont.lfOrientation = nOrientation; + hFont = CreateFontIndirectA(&logfont); + hOldFont = SelectObject(hdc, hFont); + } + + ExtTextOutA(hdc, + ((lStyle & TCS_VERTICAL) && (lStyle & TCS_BOTTOM)) ? drawRect->right : drawRect->left, + ((lStyle & TCS_VERTICAL) && !(lStyle & TCS_BOTTOM)) ? drawRect->bottom : drawRect->top, + 0, + 0, + infoPtr->items[iItem].pszText, + strlen(infoPtr->items[iItem].pszText), + 0); + + + /* clean things up */ + *drawRect = rcTemp; /* restore drawRect */ + + if(lStyle & TCS_VERTICAL) + SelectObject(hdc, hOldFont); /* restore the original font */ } /* @@ -1398,47 +1591,53 @@ static void TAB_DrawItem( { HBRUSH hbr = CreateSolidBrush (GetSysColor(COLOR_BTNFACE)); HPEN hwPen = GetSysColorPen (COLOR_3DHILIGHT); - HPEN hbPen = GetSysColorPen (COLOR_BTNSHADOW); + HPEN hbPen = GetSysColorPen (COLOR_3DDKSHADOW); + HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW); + HPEN hfocusPen = CreatePen(PS_DOT, 1, GetSysColor(COLOR_BTNTEXT)); + HPEN holdPen; INT oldBkMode; BOOL deleteBrush = TRUE; if (lStyle & TCS_BUTTONS) { - /* - * Get item rectangle. - */ + /* Get item rectangle */ r = itemRect; holdPen = SelectObject (hdc, hwPen); - /* - * Separators between flat buttons - */ + /* Separators between flat buttons */ + /* FIXME: test and correct this if necessary for TCS_FLATBUTTONS style */ if (lStyle & TCS_FLATBUTTONS) { + int x = r.right + FLAT_BTN_SPACINGX - 2; + /* highlight */ - int x = r.right+FLAT_BTN_SPACINGX-2; - MoveToEx (hdc, x, r.bottom-1, NULL); - LineTo (hdc, x, r.top-1); + MoveToEx (hdc, x, r.bottom - 1, NULL); + LineTo (hdc, x, r.top - 1); x--; + /* shadow */ SelectObject(hdc, hbPen); - MoveToEx (hdc, x, r.bottom-1, NULL); - LineTo (hdc, x, r.top-1); + MoveToEx (hdc, x, r.bottom - 1, NULL); + LineTo (hdc, x, r.top - 1); + + /* shade */ + SelectObject (hdc, hShade ); + MoveToEx (hdc, x - 1, r.bottom - 1, NULL); + LineTo (hdc, x - 1, r.top - 1); } if (iItem == infoPtr->iSelected) { - /* - * Background color. - */ + /* Background color */ if (!((lStyle & TCS_OWNERDRAWFIXED) && infoPtr->fSizeSet)) { COLORREF bk = GetSysColor(COLOR_3DHILIGHT); - DeleteObject(hbr); + DeleteObject(hbr); hbr = GetSysColorBrush(COLOR_SCROLLBAR); + SetTextColor(hdc, GetSysColor(COLOR_3DFACE)); SetBkColor(hdc, bk); @@ -1446,15 +1645,13 @@ static void TAB_DrawItem( * we better use 0x55aa bitmap brush to make scrollbar's background * look different from the window background. */ - if (bk == GetSysColor(COLOR_WINDOW)) + if (bk == GetSysColor(COLOR_WINDOW)) hbr = CACHE_GetPattern55AABrush(); deleteBrush = FALSE; } - /* - * Erase the background. - */ + /* Erase the background */ FillRect(hdc, &r, hbr); /* @@ -1470,21 +1667,25 @@ static void TAB_DrawItem( SelectObject(hdc, hwPen); MoveToEx (hdc, r.left, r.bottom, NULL); LineTo (hdc, r.right, r.bottom); - LineTo (hdc, r.right, r.top); + LineTo (hdc, r.right, r.top + 1); /* shadow */ SelectObject(hdc, hbPen); - LineTo (hdc, r.left, r.top); - LineTo (hdc, r.left, r.bottom); + LineTo (hdc, r.left + 1, r.top + 1); + LineTo (hdc, r.left + 1, r.bottom); + + /* shade */ + SelectObject (hdc, hShade ); + MoveToEx (hdc, r.right, r.top, NULL); + LineTo (hdc, r.left, r.top); + LineTo (hdc, r.left, r.bottom); } else { - /* - * Erase the background. - */ + /* Erase the background */ FillRect(hdc, &r, hbr); - if (!(lStyle & TCS_FLATBUTTONS)) + if (!(lStyle & TCS_FLATBUTTONS)) { /* highlight */ MoveToEx (hdc, r.left, r.bottom, NULL); @@ -1495,21 +1696,23 @@ static void TAB_DrawItem( SelectObject(hdc, hbPen); LineTo (hdc, r.right, r.bottom); LineTo (hdc, r.left, r.bottom); + + /* shade */ + SelectObject (hdc, hShade ); + MoveToEx (hdc, r.right - 1, r.top, NULL); + LineTo (hdc, r.right - 1, r.bottom - 1); + LineTo (hdc, r.left + 1, r.bottom - 1); } } } - else + else /* !TCS_BUTTONS */ { - /* - * Background color. - */ + /* Background color */ DeleteObject(hbr); hbr = CreateSolidBrush(GetSysColor(COLOR_BTNFACE)); - /* - * We draw a rectangle of different sizes depending on the selection - * state. - */ + /* We draw a rectangle of different sizes depending on the selection + * state. */ if (iItem == infoPtr->iSelected) r = selectedRect; else @@ -1533,43 +1736,100 @@ static void TAB_DrawItem( r.bottom--; holdPen = SelectObject (hdc, hwPen); + if(lStyle & TCS_VERTICAL) + { + if (lStyle & TCS_BOTTOM) + { + /* highlight */ + MoveToEx (hdc, r.left, r.top, NULL); + LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top); + LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE); - if (lStyle & TCS_BOTTOM) - { - /* highlight */ - MoveToEx (hdc, r.left, r.top, NULL); - LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE); - LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom); - - /* shadow */ - SelectObject(hdc, hbPen); - LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom); - LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE); - LineTo (hdc, r.right, r.top); + /* shadow */ + SelectObject(hdc, hbPen); + LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE); + LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom); + LineTo (hdc, r.left - 1, r.bottom); + + /* shade */ + SelectObject (hdc, hShade ); + MoveToEx (hdc, r.right - 1, r.top, NULL); + LineTo (hdc, r.right - 1, r.bottom - 1); + LineTo (hdc, r.left - 1, r.bottom - 1); + } + else + { + /* highlight */ + MoveToEx (hdc, r.right, r.top, NULL); + LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top); + LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE); + LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE); + + /* shadow */ + SelectObject(hdc, hbPen); + LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom); + LineTo (hdc, r.right + 1, r.bottom); + + /* shade */ + SelectObject (hdc, hShade ); + MoveToEx (hdc, r.left + ROUND_CORNER_SIZE - 1, r.bottom - 1, NULL); + LineTo (hdc, r.right + 1, r.bottom - 1); + } } - else + else { - /* highlight */ - MoveToEx (hdc, r.left, r.bottom, NULL); - LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE); - LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top); - LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top); - - /* shadow */ - SelectObject(hdc, hbPen); - LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE); - LineTo (hdc, r.right, r.bottom); + if (lStyle & TCS_BOTTOM) + { + /* highlight */ + MoveToEx (hdc, r.left, r.top, NULL); + LineTo (hdc, r.left, r.bottom - ROUND_CORNER_SIZE); + LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.bottom); + + /* shadow */ + SelectObject(hdc, hbPen); + LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.bottom); + LineTo (hdc, r.right, r.bottom - ROUND_CORNER_SIZE); + LineTo (hdc, r.right, r.top - 1); + + /* shade */ + SelectObject (hdc, hShade ); + MoveToEx (hdc, r.left, r.bottom - 1, NULL); + LineTo (hdc, r.right - ROUND_CORNER_SIZE - 1, r.bottom - 1); + LineTo (hdc, r.right - 1, r.bottom - ROUND_CORNER_SIZE - 1); + LineTo (hdc, r.right - 1, r.top - 1); + } + else + { + /* highlight */ + if(infoPtr->items[iItem].rect.left == 0) /* if leftmost draw the line longer */ + MoveToEx (hdc, r.left, r.bottom, NULL); + else + MoveToEx (hdc, r.left, r.bottom - 1, NULL); + + LineTo (hdc, r.left, r.top + ROUND_CORNER_SIZE); + LineTo (hdc, r.left + ROUND_CORNER_SIZE, r.top); + LineTo (hdc, r.right - ROUND_CORNER_SIZE, r.top); + + /* shadow */ + SelectObject(hdc, hbPen); + LineTo (hdc, r.right, r.top + ROUND_CORNER_SIZE); + LineTo (hdc, r.right, r.bottom + 1); + + + /* shade */ + SelectObject (hdc, hShade ); + MoveToEx (hdc, r.right - 1, r.top + ROUND_CORNER_SIZE, NULL); + LineTo (hdc, r.right - 1, r.bottom + 1); + } } } oldBkMode = SetBkMode(hdc, TRANSPARENT); - + /* This modifies r to be the text rectangle. */ TAB_DrawItemInterior(hwnd, hdc, iItem, &r); - /* - * Draw the focus rectangle - */ + /* Draw the focus rectangle */ if (((lStyle & TCS_FOCUSNEVER) == 0) && (GetFocus() == hwnd) && (iItem == infoPtr->uFocus) ) @@ -1580,15 +1840,13 @@ static void TAB_DrawItem( SelectObject(hdc, hfocusPen); MoveToEx (hdc, r.left, r.top, NULL); - LineTo (hdc, r.right-1, r.top); - LineTo (hdc, r.right-1, r.bottom -1); - LineTo (hdc, r.left, r.bottom -1); + LineTo (hdc, r.right - 1, r.top); + LineTo (hdc, r.right - 1, r.bottom - 1); + LineTo (hdc, r.left, r.bottom - 1); LineTo (hdc, r.left, r.top); } - /* - * Cleanup - */ + /* Cleanup */ SetBkMode(hdc, oldBkMode); SelectObject(hdc, holdPen); DeleteObject(hfocusPen); @@ -1610,19 +1868,28 @@ static void TAB_DrawBorder (HWND hwnd, HDC hdc) HPEN hbPen = GetSysColorPen (COLOR_3DDKSHADOW); HPEN hShade = GetSysColorPen (COLOR_BTNSHADOW); RECT rect; + DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE); GetClientRect (hwnd, &rect); /* * Adjust for the style */ - if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM) + if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL)) { rect.bottom -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2; } - else + else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL)) { - rect.top += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2; + rect.right -= (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2; + } + else if(lStyle & TCS_VERTICAL) + { + rect.left += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 2; + } + else /* not TCS_VERTICAL and not TCS_BOTTOM */ + { + rect.top += (infoPtr->tabHeight - 2) * (infoPtr->uNumRows + 1) + 1; } /* @@ -1634,9 +1901,9 @@ static void TAB_DrawBorder (HWND hwnd, HDC hdc) /* highlight */ htmPen = SelectObject (hdc, hwPen); - + MoveToEx (hdc, rect.left, rect.bottom, NULL); - LineTo (hdc, rect.left, rect.top); + LineTo (hdc, rect.left, rect.top); LineTo (hdc, rect.right, rect.top); /* Dark Shadow */ @@ -1646,9 +1913,9 @@ static void TAB_DrawBorder (HWND hwnd, HDC hdc) /* shade */ SelectObject (hdc, hShade ); - MoveToEx (hdc, rect.right-1, rect.top, NULL); - LineTo (hdc, rect.right-1, rect.bottom-1); - LineTo (hdc, rect.left, rect.bottom-1); + MoveToEx (hdc, rect.right - 1, rect.top, NULL); + LineTo (hdc, rect.right - 1, rect.bottom - 1); + LineTo (hdc, rect.left, rect.bottom - 1); SelectObject(hdc, htmPen); } @@ -1672,36 +1939,26 @@ static void TAB_Refresh (HWND hwnd, HDC hdc) if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BUTTONS) { for (i = 0; i < infoPtr->uNumItem; i++) - { - TAB_DrawItem (hwnd, hdc, i); - } + TAB_DrawItem (hwnd, hdc, i); } else { - /* - * Draw all the non selected item first. - */ + /* Draw all the non selected item first */ for (i = 0; i < infoPtr->uNumItem; i++) { if (i != infoPtr->iSelected) TAB_DrawItem (hwnd, hdc, i); } - /* - * Now, draw the border, draw it before the selected item - * since the selected item overwrites part of the border. - */ + /* Now, draw the border, draw it before the selected item + * since the selected item overwrites part of the border. */ TAB_DrawBorder (hwnd, hdc); - /* - * Then, draw the selected item - */ + /* Then, draw the selected item */ TAB_DrawItem (hwnd, hdc, infoPtr->iSelected); - /* - * If we haven't set the current focus yet, set it now. - * Only happens when we first paint the tab controls. - */ + /* If we haven't set the current focus yet, set it now. + * Only happens when we first paint the tab controls */ if (infoPtr->uFocus == -1) TAB_SetCurFocus(hwnd, infoPtr->iSelected); } @@ -1760,42 +2017,54 @@ static void TAB_EnsureSelectionVisible( TAB_INFO* infoPtr) { INT iSelected = infoPtr->iSelected; - + LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE); INT iOrigLeftmostVisible = infoPtr->leftmostVisible; - /* - * set the items row to the bottommost row or topmost row depending on - * style - */ - - if (infoPtr->uNumRows > 0) + /* set the items row to the bottommost row or topmost row depending on + * style */ + if ((infoPtr->uNumRows > 0) && !(lStyle & TCS_BUTTONS)) { - INT newselected=infoPtr->items[iSelected].rect.top; + INT newselected; INT iTargetRow; - LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE); - if (lStyle & TCS_BOTTOM) - iTargetRow = 0; + if(lStyle & TCS_VERTICAL) + newselected = infoPtr->items[iSelected].rect.left; else - iTargetRow = infoPtr->uNumRows; + newselected = infoPtr->items[iSelected].rect.top; + + /* the target row is always the number of rows as 0 is the row furthest from the clientRect */ + iTargetRow = infoPtr->uNumRows; if (newselected != iTargetRow) { INT i; - for (i=0; i < infoPtr->uNumItem; i++) + if(lStyle & TCS_VERTICAL) + { + for (i=0; i < infoPtr->uNumItem; i++) + { + /* move everything in the row of the selected item to the iTargetRow */ + if (infoPtr->items[i].rect.left == newselected ) + infoPtr->items[i].rect.left = iTargetRow; + else + { + if (infoPtr->items[i].rect.left > newselected) + infoPtr->items[i].rect.left-=1; + } + } + } + else + { + for (i=0; i < infoPtr->uNumItem; i++) + { if (infoPtr->items[i].rect.top == newselected ) infoPtr->items[i].rect.top = iTargetRow; - else if (lStyle&TCS_BOTTOM) - { - if (infoPtr->items[i].rect.top < newselected) - infoPtr->items[i].rect.top+=1; - } - else - { - if (infoPtr->items[i].rect.top > newselected) - infoPtr->items[i].rect.top-=1; - } - + else + { + if (infoPtr->items[i].rect.top > newselected) + infoPtr->items[i].rect.top-=1; + } + } + } TAB_RecalcHotTrack(hwnd, NULL, NULL, NULL); } } @@ -1804,7 +2073,7 @@ static void TAB_EnsureSelectionVisible( * Do the trivial cases first. */ if ( (!infoPtr->needsScrolling) || - (infoPtr->hwndUpDown==0) ) + (infoPtr->hwndUpDown==0) || (lStyle & TCS_VERTICAL)) return; if (infoPtr->leftmostVisible >= iSelected) @@ -1815,9 +2084,8 @@ static void TAB_EnsureSelectionVisible( { RECT r; INT width, i; - /* - * Calculate the part of the client area that is visible. - */ + + /* Calculate the part of the client area that is visible */ GetClientRect(hwnd, &r); width = r.right; @@ -1863,18 +2131,35 @@ static void TAB_InvalidateTabArea( TAB_INFO* infoPtr) { RECT clientRect; + DWORD lStyle = GetWindowLongA(hwnd, GWL_STYLE); GetClientRect(hwnd, &clientRect); - if (GetWindowLongA(hwnd, GWL_STYLE) & TCS_BOTTOM) + if ((lStyle & TCS_BOTTOM) && !(lStyle & TCS_VERTICAL)) { - clientRect.top = clientRect.bottom - (infoPtr->tabHeight * - (infoPtr->uNumRows + 1) + 3); + clientRect.top = clientRect.bottom - + infoPtr->tabHeight - + (infoPtr->uNumRows) * (infoPtr->tabHeight - 2) - + ((lStyle & TCS_BUTTONS) ? (infoPtr->uNumRows) * BUTTON_SPACINGY : 0) - 2; + } + else if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL)) + { + clientRect.left = clientRect.right - infoPtr->tabHeight - + (infoPtr->uNumRows) * (infoPtr->tabHeight - 2) - + ((lStyle & TCS_BUTTONS) ? (infoPtr->uNumRows) * BUTTON_SPACINGY : 0) - 2; + } + else if(lStyle & TCS_VERTICAL) + { + clientRect.right = clientRect.left + infoPtr->tabHeight + + (infoPtr->uNumRows) * (infoPtr->tabHeight - 2) - + ((lStyle & TCS_BUTTONS) ? (infoPtr->uNumRows) * BUTTON_SPACINGY : 0) + 1; + } else { - clientRect.bottom = clientRect.top + (infoPtr->tabHeight * - (infoPtr->uNumRows + 1) + 1); + clientRect.bottom = clientRect.top + infoPtr->tabHeight + + (infoPtr->uNumRows) * (infoPtr->tabHeight - 2) + + ((lStyle & TCS_BUTTONS) ? (infoPtr->uNumRows) * BUTTON_SPACINGY : 0) + 1; } InvalidateRect(hwnd, &clientRect, TRUE); @@ -1946,7 +2231,7 @@ TAB_InsertItem (HWND hwnd, WPARAM wParam, LPARAM lParam) infoPtr->items[iItem].mask = pti->mask; if (pti->mask & TCIF_TEXT) { - len = lstrlenA (pti->pszText); + len = strlen (pti->pszText); infoPtr->items[iItem].pszText = COMCTL32_Alloc (len+1); strcpy (infoPtr->items[iItem].pszText, pti->pszText); infoPtr->items[iItem].cchTextMax = pti->cchTextMax; @@ -1990,38 +2275,39 @@ TAB_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam) { TAB_INFO *infoPtr = TAB_GetInfoPtr(hwnd); TCITEMA *tabItem; - TAB_ITEM *wineItem; - INT iItem,len; + TAB_ITEM *wineItem; + INT iItem, len; - iItem=(INT) wParam; - tabItem=(LPTCITEMA ) lParam; - TRACE("%d %p\n",iItem, tabItem); + iItem = (INT)wParam; + tabItem = (LPTCITEMA)lParam; + + TRACE("%d %p\n", iItem, tabItem); if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE; - wineItem=& infoPtr->items[iItem]; + wineItem = &infoPtr->items[iItem]; if (tabItem->mask & TCIF_IMAGE) - wineItem->iImage=tabItem->iImage; + wineItem->iImage = tabItem->iImage; if (tabItem->mask & TCIF_PARAM) - wineItem->lParam=tabItem->lParam; + wineItem->lParam = tabItem->lParam; if (tabItem->mask & TCIF_RTLREADING) FIXME("TCIF_RTLREADING\n"); if (tabItem->mask & TCIF_STATE) - wineItem->dwState=tabItem->dwState; + wineItem->dwState = tabItem->dwState; if (tabItem->mask & TCIF_TEXT) { - len=lstrlenA (tabItem->pszText); - if (len>wineItem->cchTextMax) - wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1); + len = strlen (tabItem->pszText); + + if (len>wineItem->cchTextMax) + wineItem->pszText = COMCTL32_ReAlloc (wineItem->pszText, len+1); + strcpy (wineItem->pszText, tabItem->pszText); } - /* - * Update and repaint tabs. - */ + /* Update and repaint tabs */ TAB_SetItemBounds(hwnd); TAB_InvalidateTabArea(hwnd,infoPtr); @@ -2045,24 +2331,24 @@ TAB_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam) TAB_ITEM *wineItem; INT iItem; - iItem=(INT) wParam; - tabItem=(LPTCITEMA) lParam; + iItem = (INT)wParam; + tabItem = (LPTCITEMA)lParam; TRACE("\n"); if ((iItem<0) || (iItem>=infoPtr->uNumItem)) return FALSE; wineItem=& infoPtr->items[iItem]; if (tabItem->mask & TCIF_IMAGE) - tabItem->iImage=wineItem->iImage; + tabItem->iImage = wineItem->iImage; if (tabItem->mask & TCIF_PARAM) - tabItem->lParam=wineItem->lParam; + tabItem->lParam = wineItem->lParam; if (tabItem->mask & TCIF_RTLREADING) FIXME("TCIF_RTLREADING\n"); if (tabItem->mask & TCIF_STATE) - tabItem->dwState=wineItem->dwState; + tabItem->dwState = wineItem->dwState; if (tabItem->mask & TCIF_TEXT) lstrcpynA (tabItem->pszText, wineItem->pszText, tabItem->cchTextMax); @@ -2091,11 +2377,9 @@ TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam) memcpy(&infoPtr->items[iItem], &oldItems[iItem + 1], (infoPtr->uNumItem - iItem) * sizeof(TAB_ITEM)); - COMCTL32_Free (oldItems); + COMCTL32_Free(oldItems); - /* - * Readjust the selected index. - */ + /* Readjust the selected index */ if ((iItem == infoPtr->iSelected) && (iItem > 0)) infoPtr->iSelected--; @@ -2105,9 +2389,7 @@ TAB_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam) if (infoPtr->uNumItem == 0) infoPtr->iSelected = -1; - /* - * Reposition and repaint tabs. - */ + /* Reposition and repaint tabs */ TAB_SetItemBounds(hwnd); TAB_InvalidateTabArea(hwnd,infoPtr); @@ -2212,14 +2494,10 @@ TAB_Size (HWND hwnd, WPARAM wParam, LPARAM lParam) FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam); } */ - /* - * Recompute the size/position of the tabs. - */ + /* Recompute the size/position of the tabs. */ TAB_SetItemBounds (hwnd); - /* - * Force a repaint of the control. - */ + /* Force a repaint of the control. */ InvalidateRect(hwnd, NULL, TRUE); return 0; @@ -2291,21 +2569,17 @@ TAB_Create (HWND hwnd, WPARAM wParam, LPARAM lParam) hdc = GetDC(hwnd); hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT)); - /* - * Use the system font to determine the initial height of a tab. - */ + /* Use the system font to determine the initial height of a tab. */ GetTextMetricsA(hdc, &fontMetrics); /* * Make sure there is enough space for the letters + growing the * selected item + extra space for the selected item. */ - infoPtr->tabHeight = fontMetrics.tmHeight + 2*VERTICAL_ITEM_PADDING + + infoPtr->tabHeight = fontMetrics.tmHeight + 2 * VERTICAL_ITEM_PADDING + SELECTED_TAB_OFFSET; - /* - * Initialize the width of a tab. - */ + /* Initialize the width of a tab. */ infoPtr->tabWidth = DEFAULT_TAB_WIDTH; SelectObject (hdc, hOldFont); @@ -2554,4 +2828,3 @@ TAB_Unregister (void) { UnregisterClassA (WC_TABCONTROLA, (HINSTANCE)NULL); } -