diff --git a/dll/win32/comctl32/combo.c b/dll/win32/comctl32/combo.c index 0c67b2ee729..e083a58034f 100644 --- a/dll/win32/comctl32/combo.c +++ b/dll/win32/comctl32/combo.c @@ -32,7 +32,6 @@ #include "uxtheme.h" #include "vssym32.h" #include "commctrl.h" -#include "wine/unicode.h" #include "wine/debug.h" #include "wine/heap.h" @@ -79,6 +78,9 @@ static UINT CBitHeight, CBitWidth; #define ID_CB_LISTBOX 1000 #define ID_CB_EDIT 1001 +static void CBCalcPlacement(HEADCOMBO *combo); +static void CBResetPos(HEADCOMBO *combo); + /*********************************************************************** * COMBO_Init * @@ -170,6 +172,25 @@ static LRESULT COMBO_NCDestroy( HEADCOMBO *lphc ) return 0; } +static INT combo_get_text_height(const HEADCOMBO *combo) +{ + HDC hdc = GetDC(combo->self); + HFONT prev_font = 0; + TEXTMETRICW tm; + + if (combo->hFont) + prev_font = SelectObject(hdc, combo->hFont); + + GetTextMetricsW(hdc, &tm); + + if (prev_font) + SelectObject(hdc, prev_font); + + ReleaseDC(combo->self, hdc); + + return tm.tmHeight + 4; +} + /*********************************************************************** * CBGetTextAreaHeight * @@ -182,37 +203,18 @@ static LRESULT COMBO_NCDestroy( HEADCOMBO *lphc ) * This height was determined through experimentation. * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border */ -static INT CBGetTextAreaHeight( - HWND hwnd, - LPHEADCOMBO lphc) +static INT CBGetTextAreaHeight(HEADCOMBO *lphc, BOOL clip_item_height) { - INT iTextItemHeight; + INT item_height, text_height; - if( lphc->editHeight ) /* explicitly set height */ + if (clip_item_height && !CB_OWNERDRAWN(lphc)) { - iTextItemHeight = lphc->editHeight; + text_height = combo_get_text_height(lphc); + if (lphc->item_height < text_height) + lphc->item_height = text_height; } - else - { - TEXTMETRICW tm; - HDC hDC = GetDC(hwnd); - HFONT hPrevFont = 0; - INT baseUnitY; - - if (lphc->hFont) - hPrevFont = SelectObject( hDC, lphc->hFont ); - - GetTextMetricsW(hDC, &tm); - - baseUnitY = tm.tmHeight; - - if( hPrevFont ) - SelectObject( hDC, hPrevFont ); - - ReleaseDC(hwnd, hDC); + item_height = lphc->item_height; - iTextItemHeight = baseUnitY + 4; - } /* * Check the ownerdraw case if we haven't asked the parent the size @@ -223,13 +225,13 @@ static INT CBGetTextAreaHeight( { MEASUREITEMSTRUCT measureItem; RECT clientRect; - INT originalItemHeight = iTextItemHeight; + INT originalItemHeight = item_height; UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID ); /* * We use the client rect for the width of the item. */ - GetClientRect(hwnd, &clientRect); + GetClientRect(lphc->self, &clientRect); lphc->wState &= ~CBF_MEASUREITEM; @@ -240,10 +242,10 @@ static INT CBGetTextAreaHeight( measureItem.CtlID = id; measureItem.itemID = -1; measureItem.itemWidth = clientRect.right; - measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */ + measureItem.itemHeight = item_height - 6; /* ownerdrawn cb is taller */ measureItem.itemData = 0; SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem); - iTextItemHeight = 6 + measureItem.itemHeight; + item_height = 6 + measureItem.itemHeight; /* * Send a second one in the case of a fixed ownerdraw list to calculate the @@ -264,10 +266,10 @@ static INT CBGetTextAreaHeight( /* * Keep the size for the next time */ - lphc->editHeight = iTextItemHeight; + lphc->item_height = item_height; } - return iTextItemHeight; + return item_height; } /*********************************************************************** @@ -277,13 +279,12 @@ static INT CBGetTextAreaHeight( * a re-arranging of the contents of the combobox and the recalculation * of the size of the "real" control window. */ -static void CBForceDummyResize( - LPHEADCOMBO lphc) +static void CBForceDummyResize(LPHEADCOMBO lphc) { RECT windowRect; int newComboHeight; - newComboHeight = CBGetTextAreaHeight(lphc->self,lphc) + 2*COMBO_YBORDERSIZE(); + newComboHeight = CBGetTextAreaHeight(lphc, FALSE) + 2*COMBO_YBORDERSIZE(); GetWindowRect(lphc->self, &windowRect); @@ -295,12 +296,17 @@ static void CBForceDummyResize( * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING * message. */ + lphc->wState |= CBF_NORESIZE; SetWindowPos( lphc->self, NULL, 0, 0, windowRect.right - windowRect.left, newComboHeight, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE ); + lphc->wState &= ~CBF_NORESIZE; + + CBCalcPlacement(lphc); + CBResetPos(lphc); } /*********************************************************************** @@ -308,111 +314,70 @@ static void CBForceDummyResize( * * Set up component coordinates given valid lphc->RectCombo. */ -static void CBCalcPlacement( - HWND hwnd, - LPHEADCOMBO lphc, - LPRECT lprEdit, - LPRECT lprButton, - LPRECT lprLB) +static void CBCalcPlacement(HEADCOMBO *combo) { - /* - * Again, start with the client rectangle. - */ - GetClientRect(hwnd, lprEdit); + /* Start with the client rectangle. */ + GetClientRect(combo->self, &combo->textRect); - /* - * Remove the borders - */ - InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE()); + /* Remove the borders */ + InflateRect(&combo->textRect, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE()); - /* - * Chop off the bottom part to fit with the height of the text area. - */ - lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc); - - /* - * The button starts the same vertical position as the text area. - */ - CopyRect(lprButton, lprEdit); + /* Chop off the bottom part to fit with the height of the text area. */ + combo->textRect.bottom = combo->textRect.top + CBGetTextAreaHeight(combo, FALSE); - /* - * If the combobox is "simple" there is no button. - */ - if( CB_GETTYPE(lphc) == CBS_SIMPLE ) - lprButton->left = lprButton->right = lprButton->bottom = 0; - else - { - /* - * Let's assume the combobox button is the same width as the - * scrollbar button. - * size the button horizontally and cut-off the text area. - */ - lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL); - lprEdit->right = lprButton->left; - } + /* The button starts the same vertical position as the text area. */ + combo->buttonRect = combo->textRect; - /* - * In the case of a dropdown, there is an additional spacing between the - * text area and the button. - */ - if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) - { - lprEdit->right -= COMBO_EDITBUTTONSPACE(); - } + /* If the combobox is "simple" there is no button. */ + if (CB_GETTYPE(combo) == CBS_SIMPLE) + combo->buttonRect.left = combo->buttonRect.right = combo->buttonRect.bottom = 0; + else + { + /* + * Let's assume the combobox button is the same width as the + * scrollbar button. + * size the button horizontally and cut-off the text area. + */ + combo->buttonRect.left = combo->buttonRect.right - GetSystemMetrics(SM_CXVSCROLL); + combo->textRect.right = combo->buttonRect.left; + } - /* - * If we have an edit control, we space it away from the borders slightly. - */ - if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST) - { - InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING()); - } + /* In the case of a dropdown, there is an additional spacing between the text area and the button. */ + if (CB_GETTYPE(combo) == CBS_DROPDOWN) + combo->textRect.right -= COMBO_EDITBUTTONSPACE(); - /* - * Adjust the size of the listbox popup. - */ - if( CB_GETTYPE(lphc) == CBS_SIMPLE ) - { - /* - * Use the client rectangle to initialize the listbox rectangle - */ - GetClientRect(hwnd, lprLB); + /* If we have an edit control, we space it away from the borders slightly. */ + if (CB_GETTYPE(combo) != CBS_DROPDOWNLIST) + InflateRect(&combo->textRect, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING()); - /* - * Then, chop-off the top part. - */ - lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE(); - } - else - { - /* - * Make sure the dropped width is as large as the combobox itself. - */ - if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE())) + /* Adjust the size of the listbox popup. */ + if (CB_GETTYPE(combo) == CBS_SIMPLE) { - lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE()); - - /* - * In the case of a dropdown, the popup listbox is offset to the right. - * so, we want to make sure it's flush with the right side of the - * combobox - */ - if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) - lprLB->right -= COMBO_EDITBUTTONSPACE(); + GetClientRect(combo->self, &combo->droppedRect); + combo->droppedRect.top = combo->textRect.bottom + COMBO_YBORDERSIZE(); } else - lprLB->right = lprLB->left + lphc->droppedWidth; - } - - /* don't allow negative window width */ - if (lprEdit->right < lprEdit->left) - lprEdit->right = lprEdit->left; + { + /* Make sure the dropped width is as large as the combobox itself. */ + if (combo->droppedWidth < (combo->buttonRect.right + COMBO_XBORDERSIZE())) + { + combo->droppedRect.right = combo->droppedRect.left + (combo->buttonRect.right + COMBO_XBORDERSIZE()); - TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit)); + /* In the case of a dropdown, the popup listbox is offset to the right. We want to make sure it's flush + with the right side of the combobox */ + if (CB_GETTYPE(combo) == CBS_DROPDOWN) + combo->droppedRect.right -= COMBO_EDITBUTTONSPACE(); + } + else + combo->droppedRect.right = combo->droppedRect.left + combo->droppedWidth; + } - TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton)); + /* Disallow negative window width */ + if (combo->textRect.right < combo->textRect.left) + combo->textRect.right = combo->textRect.left; - TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB)); + TRACE("text %s, button %s, lbox %s.\n", wine_dbgstr_rect(&combo->textRect), wine_dbgstr_rect(&combo->buttonRect), + wine_dbgstr_rect(&combo->droppedRect)); } /*********************************************************************** @@ -444,11 +409,9 @@ static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG lphc->owner = hwndParent; - /* - * The item height and dropped width are not set when the control - * is created. - */ - lphc->droppedWidth = lphc->editHeight = 0; + lphc->droppedWidth = 0; + + lphc->item_height = combo_get_text_height(lphc); /* * The first time we go through, we want to measure the ownerdraw item @@ -473,7 +436,7 @@ static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG * recalculated. */ GetClientRect( hwnd, &lphc->droppedRect ); - CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect ); + CBCalcPlacement(lphc); /* * Adjust the position of the popup listbox if it's necessary @@ -589,10 +552,13 @@ static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG * * Paint combo button (normal, pressed, and disabled states). */ -static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton) +static void CBPaintButton(HEADCOMBO *lphc, HDC hdc) { UINT buttonState = DFCS_SCROLLCOMBOBOX; + if (IsRectEmpty(&lphc->buttonRect)) + return; + if( lphc->wState & CBF_NOREDRAW ) return; @@ -603,7 +569,7 @@ static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton) if (CB_DISABLED(lphc)) buttonState |= DFCS_INACTIVE; - DrawFrameControl(hdc, &rectButton, DFC_SCROLL, buttonState); + DrawFrameControl(hdc, &lphc->buttonRect, DFC_SCROLL, buttonState); } /*********************************************************************** @@ -777,16 +743,13 @@ static void CBPaintText(HEADCOMBO *lphc, HDC hdc_paint) /*********************************************************************** * CBPaintBorder */ -static void CBPaintBorder( - HWND hwnd, - const HEADCOMBO *lphc, - HDC hdc) +static void CBPaintBorder(const HEADCOMBO *lphc, HDC hdc) { RECT clientRect; if (CB_GETTYPE(lphc) != CBS_SIMPLE) { - GetClientRect(hwnd, &clientRect); + GetClientRect(lphc->self, &clientRect); } else { @@ -857,10 +820,9 @@ static LRESULT COMBO_Paint(HEADCOMBO *lphc, HDC hdc) /* * In non 3.1 look, there is a sunken border on the combobox */ - CBPaintBorder(lphc->self, lphc, hdc); + CBPaintBorder(lphc, hdc); - if (!IsRectEmpty(&lphc->buttonRect)) - CBPaintButton(lphc, hdc, lphc->buttonRect); + CBPaintButton(lphc, hdc); /* paint the edit control padding area */ if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST) @@ -1391,50 +1353,45 @@ static LRESULT COMBO_GetText( HEADCOMBO *lphc, INT count, LPWSTR buf ) * This function sets window positions according to the updated * component placement struct. */ -static void CBResetPos( - LPHEADCOMBO lphc, - const RECT *rectEdit, - const RECT *rectLB, - BOOL bRedraw) +static void CBResetPos(HEADCOMBO *combo) { - BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE); - - /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of - * sizing messages */ - - if( lphc->wState & CBF_EDIT ) - SetWindowPos( lphc->hWndEdit, 0, - rectEdit->left, rectEdit->top, - rectEdit->right - rectEdit->left, - rectEdit->bottom - rectEdit->top, - SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) ); - - SetWindowPos( lphc->hWndLBox, 0, - rectLB->left, rectLB->top, - rectLB->right - rectLB->left, - rectLB->bottom - rectLB->top, - SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) ); - - if( bDrop ) - { - if( lphc->wState & CBF_DROPPED ) - { - lphc->wState &= ~CBF_DROPPED; - ShowWindow( lphc->hWndLBox, SW_HIDE ); - } + BOOL drop = CB_GETTYPE(combo) != CBS_SIMPLE; + + /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of + * sizing messages */ + if (combo->wState & CBF_EDIT) + SetWindowPos(combo->hWndEdit, 0, combo->textRect.left, combo->textRect.top, + combo->textRect.right - combo->textRect.left, + combo->textRect.bottom - combo->textRect.top, + SWP_NOZORDER | SWP_NOACTIVATE | (drop ? SWP_NOREDRAW : 0)); + + SetWindowPos(combo->hWndLBox, 0, combo->droppedRect.left, combo->droppedRect.top, + combo->droppedRect.right - combo->droppedRect.left, + combo->droppedRect.bottom - combo->droppedRect.top, + SWP_NOACTIVATE | SWP_NOZORDER | (drop ? SWP_NOREDRAW : 0)); + + if (drop) + { + if (combo->wState & CBF_DROPPED) + { + combo->wState &= ~CBF_DROPPED; + ShowWindow(combo->hWndLBox, SW_HIDE); + } - if( bRedraw && !(lphc->wState & CBF_NOREDRAW) ) - RedrawWindow( lphc->self, NULL, 0, - RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW ); - } + if (!(combo->wState & CBF_NOREDRAW)) + RedrawWindow(combo->self, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW); + } } /*********************************************************************** * COMBO_Size */ -static void COMBO_Size( LPHEADCOMBO lphc ) +static void COMBO_Size( HEADCOMBO *lphc ) { + if (!lphc->hWndLBox || (lphc->wState & CBF_NORESIZE)) + return; + /* * Those controls are always the same height. So we have to make sure * they are not resized to another value. @@ -1447,7 +1404,7 @@ static void COMBO_Size( LPHEADCOMBO lphc ) GetWindowRect(lphc->self, &rc); curComboHeight = rc.bottom - rc.top; curComboWidth = rc.right - rc.left; - newComboHeight = CBGetTextAreaHeight(lphc->self, lphc) + 2*COMBO_YBORDERSIZE(); + newComboHeight = CBGetTextAreaHeight(lphc, TRUE) + 2*COMBO_YBORDERSIZE(); /* * Resizing a combobox has another side effect, it resizes the dropped @@ -1467,18 +1424,18 @@ static void COMBO_Size( LPHEADCOMBO lphc ) /* * Restore original height */ - if( curComboHeight != newComboHeight ) - SetWindowPos(lphc->self, 0, 0, 0, curComboWidth, newComboHeight, - SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOREDRAW); + if (curComboHeight != newComboHeight) + { + lphc->wState |= CBF_NORESIZE; + SetWindowPos(lphc->self, 0, 0, 0, curComboWidth, newComboHeight, + SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW); + lphc->wState &= ~CBF_NORESIZE; + } } - CBCalcPlacement(lphc->self, - lphc, - &lphc->textRect, - &lphc->buttonRect, - &lphc->droppedRect); + CBCalcPlacement(lphc); - CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE ); + CBResetPos(lphc); } @@ -1487,10 +1444,8 @@ static void COMBO_Size( LPHEADCOMBO lphc ) */ static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw ) { - /* - * Set the font - */ lphc->hFont = hFont; + lphc->item_height = combo_get_text_height(lphc); /* * Propagate to owned windows. @@ -1504,13 +1459,9 @@ static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw ) */ if ( CB_GETTYPE(lphc) == CBS_SIMPLE) { - CBCalcPlacement(lphc->self, - lphc, - &lphc->textRect, - &lphc->buttonRect, - &lphc->droppedRect); + CBCalcPlacement(lphc); - CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE ); + CBResetPos(lphc); } else { @@ -1530,20 +1481,16 @@ static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height ) { if( height < 32768 ) { - lphc->editHeight = height + 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */ + lphc->item_height = height + 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */ /* * Redo the layout of the control. */ if ( CB_GETTYPE(lphc) == CBS_SIMPLE) { - CBCalcPlacement(lphc->self, - lphc, - &lphc->textRect, - &lphc->buttonRect, - &lphc->droppedRect); + CBCalcPlacement(lphc); - CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE ); + CBResetPos(lphc); } else { @@ -1794,8 +1741,7 @@ static LRESULT CALLBACK COMBO_WindowProc( HWND hwnd, UINT message, WPARAM wParam } case WM_SIZE: - if (lphc->hWndLBox && !(lphc->wState & CBF_NORESIZE)) - COMBO_Size( lphc ); + COMBO_Size( lphc ); return TRUE; case WM_SETFONT: @@ -2017,7 +1963,7 @@ static LRESULT CALLBACK COMBO_WindowProc( HWND hwnd, UINT message, WPARAM wParam case CB_GETITEMHEIGHT: if ((INT)wParam >= 0) /* listbox item */ return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0); - return CBGetTextAreaHeight(hwnd, lphc); + return CBGetTextAreaHeight(lphc, FALSE); case CB_RESETCONTENT: SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0); @@ -2060,7 +2006,7 @@ static LRESULT CALLBACK COMBO_WindowProc( HWND hwnd, UINT message, WPARAM wParam lphc->droppedWidth = 0; /* recalculate the combobox area */ - CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect ); + CBCalcPlacement(lphc); /* fall through */ case CB_GETDROPPEDWIDTH: diff --git a/dll/win32/comctl32/comctl32.h b/dll/win32/comctl32/comctl32.h index 40baabb24ff..f2dfbe48c6e 100644 --- a/dll/win32/comctl32/comctl32.h +++ b/dll/win32/comctl32/comctl32.h @@ -153,8 +153,8 @@ typedef struct RECT droppedRect; INT droppedIndex; INT fixedOwnerDrawHeight; - INT droppedWidth; /* last two are not used unless set */ - INT editHeight; /* explicitly */ + INT droppedWidth; /* not used unless set explicitly */ + INT item_height; INT visibleItems; } HEADCOMBO, *LPHEADCOMBO;