From a249595957f8a718726a4fe5b7c4455e12abaf22 Mon Sep 17 00:00:00 2001 From: "Guy L. Albertelli" Date: Fri, 26 Jan 2001 21:00:10 +0000 Subject: [PATCH] - Rewrite WM_CREATE to match native (allocating own Edit control, etc). - Implement additional WNDPROCs for the Combo and Edit controls to handle different functions. - Implement CBEM_SETCURSEL and CBEM_HASEDITCHANGED. --- dlls/comctl32/comboex.c | 881 ++++++++++++++++++++++++++++++++-------- 1 file changed, 717 insertions(+), 164 deletions(-) diff --git a/dlls/comctl32/comboex.c b/dlls/comctl32/comboex.c index f4a069bc75..0f3e08e98d 100644 --- a/dlls/comctl32/comboex.c +++ b/dlls/comctl32/comboex.c @@ -1,5 +1,5 @@ /* - * ComboBoxEx control + * ComboBoxEx control v2 (mod3) * * Copyright 1998, 1999 Eric Kohl * @@ -16,21 +16,23 @@ * - should include "combo.h" * Changes Guy Albertelli - * 1. Implemented message CB_SETITEMHEIGHT - * 2. Implemented message WM_WINDOWPOSCHANGING - * 3. Implemented message WM_MEASUREITEM - * 4. Add code to WM_CREATE processing to set font of COMBOBOX and - * issue the CB_SETITEMHEIGHT to start the correct sizing process. - * The above 4 changes allow the window rect for the comboboxex - * to be set properly, which in turn allows the height of the - * rebar control it *may* be imbeded in to be correct. - * 5. Rewrite CBEM_INSERTITEMA to save the information. - * 6. Implemented message WM_DRAWITEM. The code will handle images - * but not "overlays" yet. - * 7. Fixed code in CBEM_SETIMAGELIST to resize control. - * 8. Add debugging code. + * v1 Implemented messages: CB_SETITEMHEIGHT, WM_WINDOWPOSCHANGING, + * WM_DRAWITEM, and WM_MEASUREITEM. Fixed WM_CREATE. Fixed height + * of window rect reported fixing rebar control. + * v2 + * 1. Rewrite of WM_Create for own created EDIT control. Also try to + * generate message sequence similar to native DLL. + * 2. Handle case where CBEM_SETITEM is called to display data in EDIT + * Control. + * 3. Add override for WNDPROC for the EDIT control (reqed for VK_RETURN). + * 4. Dump input data for things using COMBOBOXEXITEM{A|W}. + * 5. Handle positioning EDIT control based on whether icon present. + * 6. Make InsertItemA use InsertItemW, and store all data in ..W form. + * 7. Implement CB_SETCURSEL. + * 8. Add override for WNDPROC for the COMBO control. * - * Test vehicals were the ControlSpy modules (rebar.exe and comboboxex.exe) + * Test vehicals were the ControlSpy modules (rebar.exe and comboboxex.exe), + * and IE 4.0. * */ @@ -41,33 +43,51 @@ #include "wine/unicode.h" DEFAULT_DEBUG_CHANNEL(comboex); +/* + * The following is necessary for the test done in COMBOEX_DrawItem + * to determine whether to dump out the DRAWITEM structure or not. + */ DECLARE_DEBUG_CHANNEL(message); /* Item structure */ typedef struct { - VOID *next; - UINT mask; - LPWSTR pszText; - int cchTextMax; - int iImage; - int iSelectedImage; - int iOverlay; - int iIndent; - LPARAM lParam; + VOID *next; + UINT mask; + LPWSTR pszText; + int cchTextMax; + int iImage; + int iSelectedImage; + int iOverlay; + int iIndent; + LPARAM lParam; } CBE_ITEMDATA; /* ComboBoxEx structure */ typedef struct { HIMAGELIST himl; + HWND hwndSelf; /* my own hwnd */ HWND hwndCombo; + HWND hwndEdit; + WNDPROC prevEditWndProc; /* previous Edit WNDPROC value */ + WNDPROC prevComboWndProc; /* previous Combo WNDPROC value */ DWORD dwExtStyle; + DWORD flags; /* WINE internal flags */ HFONT font; INT nb_items; /* Number of items */ + CBE_ITEMDATA *edit; /* item data for edit item */ CBE_ITEMDATA *items; /* Array of items */ } COMBOEX_INFO; +/* internal flags in the COMBOEX_INFO structure */ +#define WCBE_ACTEDIT 0x00000001 /* Edit active i.e. + * CBEN_BEGINEDIT issued + * but CBEN_ENDEDIT{A|W} + * not yet issued. */ + + + #define ID_CB_EDIT 1001 /* Height in pixels of control over the amount of the selected font */ @@ -82,9 +102,16 @@ typedef struct /* Offset between image and text */ #define CBE_SEP 4 -#define COMBOEX_GetInfoPtr(wndPtr) ((COMBOEX_INFO *)GetWindowLongA (hwnd, 0)) +#define COMBOEX_GetInfoPtr(hwnd) ((COMBOEX_INFO *)GetWindowLongA (hwnd, 0)) +/* Things common to the entire DLL */ +static ATOM ComboExInfo; +static LRESULT WINAPI +COMBOEX_EditWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +static LRESULT WINAPI +COMBOEX_ComboWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + static void COMBOEX_DumpItem (CBE_ITEMDATA *item) { @@ -94,6 +121,30 @@ COMBOEX_DumpItem (CBE_ITEMDATA *item) item->iImage); TRACE("item %p - iSelectedImage=%d, iOverlay=%d, iIndent=%d, lParam=%08lx\n", item, item->iSelectedImage, item->iOverlay, item->iIndent, item->lParam); + if ((item->mask & CBEIF_TEXT) && item->pszText) + TRACE("item %p - pszText=%s\n", + item, debugstr_w((const WCHAR *)item->pszText)); + } +} + + +static void +COMBOEX_DumpInput (COMBOBOXEXITEMA *input, BOOL true_for_w) +{ + if (TRACE_ON(comboex)){ + TRACE("input - mask=%08x, iItem=%d, pszText=%p, cchTM=%d, iImage=%d\n", + input->mask, input->iItem, input->pszText, input->cchTextMax, + input->iImage); + if ((input->mask & CBEIF_TEXT) && input->pszText) { + if (true_for_w) + TRACE("input - pszText=<%s>\n", + debugstr_w((const WCHAR *)input->pszText)); + else + TRACE("input - pszText=<%s>\n", + debugstr_a((const char *)input->pszText)); + } + TRACE("input - iSelectedImage=%d, iOverlay=%d, iIndent=%d, lParam=%08lx\n", + input->iSelectedImage, input->iOverlay, input->iIndent, input->lParam); } } @@ -112,27 +163,84 @@ COMBOEX_Forward (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } +static INT +COMBOEX_Notify (COMBOEX_INFO *infoPtr, INT code, NMHDR *hdr) +{ + + hdr->idFrom = GetDlgCtrlID (infoPtr->hwndSelf); + hdr->hwndFrom = infoPtr->hwndSelf; + hdr->code = code; + return SendMessageA (GetParent(infoPtr->hwndSelf), WM_NOTIFY, 0, + (LPARAM)hdr); +} + + static void -COMBOEX_ReSize (HWND hwnd, COMBOEX_INFO *infoPtr) +COMBOEX_GetComboFontSize (COMBOEX_INFO *infoPtr, SIZE *size) { HFONT nfont, ofont; HDC mydc; - SIZE mysize; - UINT cy; - IMAGEINFO iinfo; mydc = GetDC (0); /* why the entire screen???? */ nfont = SendMessageA (infoPtr->hwndCombo, WM_GETFONT, 0, 0); ofont = (HFONT) SelectObject (mydc, nfont); - GetTextExtentPointA (mydc, "A", 1, &mysize); + GetTextExtentPointA (mydc, "A", 1, size); SelectObject (mydc, ofont); ReleaseDC (0, mydc); + TRACE("selected font hwnd=%08x, height=%ld\n", nfont, size->cy); +} + + +static void +COMBOEX_AdjustEditPos (COMBOEX_INFO *infoPtr) +{ + SIZE mysize; + IMAGEINFO iinfo; + INT x, y, w, h, xoff = 0; + RECT rect; + + if (!infoPtr->hwndEdit) return; + iinfo.rcImage.left = iinfo.rcImage.right = 0; + if (infoPtr->himl) { + ImageList_GetImageInfo(infoPtr->himl, 0, &iinfo); + xoff = iinfo.rcImage.right - iinfo.rcImage.left + CBE_SEP; + } + GetClientRect (infoPtr->hwndCombo, &rect); + InflateRect (&rect, -2, -2); + InvalidateRect (infoPtr->hwndCombo, &rect, TRUE); + + /* reposition the Edit control based on whether icon exists */ + COMBOEX_GetComboFontSize (infoPtr, &mysize); + TRACE("Combo font x=%ld, y=%ld\n", mysize.cx, mysize.cy); + x = xoff + CBE_STARTOFFSET; + y = CBE_EXTRA; + w = rect.right-rect.left - x - GetSystemMetrics(SM_CXVSCROLL) - 1; + h = mysize.cy + 1; + + TRACE("Combo client (%d,%d)-(%d,%d), setting Edit to (%d,%d)-(%d,%d)\n", + rect.left, rect.top, rect.right, rect.bottom, + x, y, x + w, y + h); + SetWindowPos(infoPtr->hwndEdit, HWND_TOP, + x, y, + w, h, + SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOZORDER); +} + + +static void +COMBOEX_ReSize (HWND hwnd, COMBOEX_INFO *infoPtr) +{ + SIZE mysize; + UINT cy; + IMAGEINFO iinfo; + + COMBOEX_GetComboFontSize (infoPtr, &mysize); cy = mysize.cy + CBE_EXTRA; if (infoPtr->himl) { ImageList_GetImageInfo(infoPtr->himl, 0, &iinfo); cy = max (iinfo.rcImage.bottom - iinfo.rcImage.top, cy); + TRACE("upgraded height due to image: height=%d\n", cy); } - TRACE("selected font hwnd=%08x, height=%d\n", nfont, cy); SendMessageA (hwnd, CB_SETITEMHEIGHT, (WPARAM) -1, (LPARAM) cy); if (infoPtr->hwndCombo) SendMessageA (infoPtr->hwndCombo, CB_SETITEMHEIGHT, @@ -140,6 +248,48 @@ COMBOEX_ReSize (HWND hwnd, COMBOEX_INFO *infoPtr) } +static void +COMBOEX_SetEditText (COMBOEX_INFO *infoPtr, CBE_ITEMDATA *item) +{ + if (!infoPtr->hwndEdit) return; + /* native issues the following messages to the {Edit} control */ + /* WM_SETTEXT (0,addr) */ + /* EM_SETSEL32 (0,0) */ + /* EM_SETSEL32 (0,-1) */ + if (item->mask & CBEIF_TEXT) { + SendMessageW (infoPtr->hwndEdit, WM_SETTEXT, 0, (LPARAM)item->pszText); + SendMessageA (infoPtr->hwndEdit, EM_SETSEL, 0, 0); + SendMessageA (infoPtr->hwndEdit, EM_SETSEL, 0, -1); + } +} + + +static CBE_ITEMDATA * +COMBOEX_FindItem(COMBOEX_INFO *infoPtr, INT index) +{ + CBE_ITEMDATA *item; + INT i; + + if ((index > infoPtr->nb_items) || (index < -1)) + return 0; + if (index == -1) + return infoPtr->edit; + item = infoPtr->items; + i = infoPtr->nb_items - 1; + + /* find the item in the list */ + while (item && (i > index)) { + item = (CBE_ITEMDATA *)item->next; + i--; + } + if (!item || (i != index)) { + FIXME("COMBOBOXEX item structures broken. Please report!\n"); + return 0; + } + return item; +} + + /* *** CBEM_xxx message support *** */ @@ -165,9 +315,9 @@ COMBOEX_GetEditControl (HWND hwnd, WPARAM wParam, LPARAM lParam) if ((GetWindowLongA (hwnd, GWL_STYLE) & CBS_DROPDOWNLIST) != CBS_DROPDOWN) return 0; - TRACE("-- 0x%x\n", GetDlgItem (infoPtr->hwndCombo, ID_CB_EDIT)); + TRACE("-- 0x%x\n", infoPtr->hwndEdit); - return (LRESULT)GetDlgItem (infoPtr->hwndCombo, ID_CB_EDIT); + return (LRESULT)infoPtr->hwndEdit; } @@ -197,84 +347,18 @@ COMBOEX_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam) /* << COMBOEX_GetUniCodeFormat >> */ -/* << COMBOEX_HasEditChanged >> */ - static LRESULT -COMBOEX_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam) +COMBOEX_HasEditChanged (HWND hwnd, WPARAM wParam, LPARAM lParam) { COMBOEX_INFO *infoPtr = COMBOEX_GetInfoPtr (hwnd); - COMBOBOXEXITEMA *cit = (COMBOBOXEXITEMA *) lParam; - INT index; - CBE_ITEMDATA *item; - - - /* get real index of item to insert */ - index = cit->iItem; - if (index == -1) index = infoPtr->nb_items; - if (index > infoPtr->nb_items) index = infoPtr->nb_items; - - /* get space and chain it in */ - item = (CBE_ITEMDATA *)COMCTL32_Alloc (sizeof (CBE_ITEMDATA)); - item->next = NULL; - item->pszText = NULL; - - /* locate position to insert new item in */ - if (index == infoPtr->nb_items) { - /* fast path for iItem = -1 */ - item->next = infoPtr->items; - infoPtr->items = item; - } - else { - int i = infoPtr->nb_items-1; - CBE_ITEMDATA *moving = infoPtr->items; - - while (i > index && moving) { - moving = (CBE_ITEMDATA *)moving->next; - } - if (!moving) { - FIXME("COMBOBOXEX item structures broken. Please report!\n"); - COMCTL32_Free(item); - return -1; - } - item->next = moving->next; - moving->next = item; - } - - /* fill in our hidden item structure */ - item->mask = cit->mask; - if (item->mask & CBEIF_TEXT) { - LPSTR str; - INT len; - - str = cit->pszText; - if (!str) str=""; - len = MultiByteToWideChar (CP_ACP, 0, str, -1, NULL, 0); - if (len > 0) { - item->pszText = (LPWSTR)COMCTL32_Alloc ((len + 1)*sizeof(WCHAR)); - MultiByteToWideChar (CP_ACP, 0, str, -1, item->pszText, len); - } - item->cchTextMax = cit->cchTextMax; - } - if (item->mask & CBEIF_IMAGE) - item->iImage = cit->iImage; - if (item->mask & CBEIF_SELECTEDIMAGE) - item->iSelectedImage = cit->iSelectedImage; - if (item->mask & CBEIF_OVERLAY) - item->iOverlay = cit->iOverlay; - if (item->mask & CBEIF_INDENT) - item->iIndent = cit->iIndent; - if (item->mask & CBEIF_LPARAM) - item->lParam = cit->lParam; - infoPtr->nb_items++; - - COMBOEX_DumpItem (item); - - SendMessageA (infoPtr->hwndCombo, CB_INSERTSTRING, - (WPARAM)cit->iItem, (LPARAM)item); - - return index; + if ((GetWindowLongA (hwnd, GWL_STYLE) & CBS_DROPDOWNLIST) != CBS_DROPDOWN) + return FALSE; + if (infoPtr->flags & WCBE_ACTEDIT) + /* FIXME: need to also test edit control */ + return TRUE; + return FALSE; } @@ -286,6 +370,8 @@ COMBOEX_InsertItemW (HWND hwnd, WPARAM wParam, LPARAM lParam) INT index; CBE_ITEMDATA *item; + COMBOEX_DumpInput ((COMBOBOXEXITEMA *) cit, TRUE); + /* get real index of item to insert */ index = cit->iItem; if (index == -1) index = infoPtr->nb_items; @@ -356,6 +442,34 @@ COMBOEX_InsertItemW (HWND hwnd, WPARAM wParam, LPARAM lParam) } +static LRESULT +COMBOEX_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + COMBOBOXEXITEMA *cit = (COMBOBOXEXITEMA *) lParam; + COMBOBOXEXITEMW citW; + LRESULT ret; + + memcpy(&citW,cit,sizeof(COMBOBOXEXITEMA)); + if (cit->mask & CBEIF_TEXT) { + LPSTR str; + INT len; + + str = cit->pszText; + if (!str) str=""; + len = MultiByteToWideChar (CP_ACP, 0, str, -1, NULL, 0); + if (len > 0) { + citW.pszText = (LPWSTR)COMCTL32_Alloc ((len + 1)*sizeof(WCHAR)); + MultiByteToWideChar (CP_ACP, 0, str, -1, citW.pszText, len); + } + } + ret = COMBOEX_InsertItemW(hwnd,wParam,(LPARAM)&citW);; + + if (cit->mask & CBEIF_TEXT) + COMCTL32_Free(citW.pszText); + return ret; +} + + static LRESULT COMBOEX_SetExtendedStyle (HWND hwnd, WPARAM wParam, LPARAM lParam) { @@ -390,8 +504,10 @@ COMBOEX_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam) infoPtr->himl = (HIMAGELIST)lParam; COMBOEX_ReSize (hwnd, infoPtr); - InvalidateRect (hwnd, NULL, TRUE); + InvalidateRect (infoPtr->hwndCombo, NULL, TRUE); + /* reposition the Edit control based on whether icon exists */ + COMBOEX_AdjustEditPos (infoPtr); return (LRESULT)himlTemp; } @@ -401,28 +517,26 @@ COMBOEX_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam) COMBOEX_INFO *infoPtr = COMBOEX_GetInfoPtr (hwnd); COMBOBOXEXITEMW *cit = (COMBOBOXEXITEMW *) lParam; INT index; - INT i; CBE_ITEMDATA *item; + COMBOEX_DumpInput ((COMBOBOXEXITEMA *) cit, TRUE); + /* get real index of item to insert */ index = cit->iItem; - if (index == -1) { - FIXME("NYI setting data for item in edit control\n"); + + /* if item number requested does not exist then return failure */ + if ((index > infoPtr->nb_items) || (index < -1)) { + ERR("attempt to set item that does not exist yet!\n"); return 0; } - /* if item number requested does not exist then return failure */ - if ((index > infoPtr->nb_items) || (index < 0)) return 0; + /* if the item is the edit control and there is no edit control, skip */ + if ((index == -1) && + ((GetWindowLongA (hwnd, GWL_STYLE) & CBS_DROPDOWNLIST) != CBS_DROPDOWN)) + return 0; - /* find the item in the list */ - item = infoPtr->items; - i = infoPtr->nb_items - 1; - while (item && (i > index)) { - item = (CBE_ITEMDATA *)item->next; - i--; - } - if (!item || (i != index)) { - FIXME("COMBOBOXEX item structures broken. Please report!\n"); + if (!(item = COMBOEX_FindItem(infoPtr, index))) { + ERR("attempt to set item that was not found!\n"); return 0; } @@ -455,6 +569,11 @@ COMBOEX_SetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam) COMBOEX_DumpItem (item); + /* if original request was to update edit control, do some fast foot work */ + if (cit->iItem == -1) { + COMBOEX_SetEditText (infoPtr, item); + RedrawWindow (infoPtr->hwndCombo, 0, 0, RDW_ERASE | RDW_INVALIDATE); + } return TRUE; } @@ -492,6 +611,27 @@ COMBOEX_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam) /* *** CB_xxx message support *** */ +static LRESULT +COMBOEX_SetCursel (HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + INT index = wParam; + COMBOEX_INFO *infoPtr = COMBOEX_GetInfoPtr (hwnd); + CBE_ITEMDATA *item; + + if (!(item = COMBOEX_FindItem(infoPtr, index))) { + /* FIXME: need to clear selection */ + return CB_ERR; + } + + TRACE("selecting item %d\n", index); + + COMBOEX_SetEditText (infoPtr, item); + if (infoPtr->hwndCombo) + return SendMessageA (infoPtr->hwndCombo, CB_SETCURSEL, wParam, lParam); + return CB_ERR; +} + + static LRESULT COMBOEX_SetItemHeight (HWND hwnd, WPARAM wParam, LPARAM lParam) { @@ -505,7 +645,6 @@ COMBOEX_SetItemHeight (HWND hwnd, WPARAM wParam, LPARAM lParam) if (infoPtr->hwndCombo) SendMessageA (infoPtr->hwndCombo, CB_SETITEMHEIGHT, wParam, lParam); - /* *** new *** */ GetWindowRect (infoPtr->hwndCombo, &cb_wrect); GetWindowRect (hwnd, &cbx_wrect); GetClientRect (hwnd, &cbx_crect); @@ -523,7 +662,6 @@ COMBOEX_SetItemHeight (HWND hwnd, WPARAM wParam, LPARAM lParam) cbx_wrect.right-cbx_wrect.left, height, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE); - /* *** end new *** */ return ret; } @@ -539,6 +677,9 @@ COMBOEX_Create (HWND hwnd, WPARAM wParam, LPARAM lParam) COMBOEX_INFO *infoPtr; DWORD dwComboStyle; LOGFONTA mylogfont; + CBE_ITEMDATA *item; + RECT wnrc1, clrc1, cmbwrc; + LONG test; /* allocate memory for info structure */ infoPtr = (COMBOEX_INFO *)COMCTL32_Alloc (sizeof(COMBOEX_INFO)); @@ -546,33 +687,169 @@ COMBOEX_Create (HWND hwnd, WPARAM wParam, LPARAM lParam) ERR("could not allocate info memory!\n"); return 0; } - infoPtr->items = NULL; - infoPtr->nb_items = 0; - - SetWindowLongA (hwnd, 0, (DWORD)infoPtr); - /* initialize info structure */ + infoPtr->items = NULL; + infoPtr->nb_items = 0; + infoPtr->hwndSelf = hwnd; + + SetWindowLongA (hwnd, 0, (DWORD)infoPtr); /* create combo box */ dwComboStyle = GetWindowLongA (hwnd, GWL_STYLE) & (CBS_SIMPLE|CBS_DROPDOWN|CBS_DROPDOWNLIST|WS_CHILD); + TRACE("combo style=%08lx, additional style=%08lx\n", dwComboStyle, + WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VSCROLL | + CBS_NOINTEGRALHEIGHT | CBS_DROPDOWNLIST | + WS_CHILD | WS_VISIBLE | CBS_OWNERDRAWFIXED); + + /* Native version of ComboEx creates the ComboBox with DROPDOWNLIST */ + /* specified. It then creates it's own version of the EDIT control */ + /* and makes the ComboBox the parent. This is because a normal */ + /* DROPDOWNLIST does not have a EDIT control, but we need one. */ + /* We also need to place the edit control at the proper location */ + /* (allow space for the icons). */ + infoPtr->hwndCombo = CreateWindowA ("ComboBox", "", /* following line added to match native */ - WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VSCROLL | CBS_NOINTEGRALHEIGHT | + WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VSCROLL | + CBS_NOINTEGRALHEIGHT | CBS_DROPDOWNLIST | /* was base and is necessary */ WS_CHILD | WS_VISIBLE | CBS_OWNERDRAWFIXED | dwComboStyle, - cs->y, cs->x, cs->cx, cs->cy, hwnd, (HMENU)0, - GetWindowLongA (hwnd, GWL_HINSTANCE), NULL); + cs->y, cs->x, cs->cx, cs->cy, hwnd, + /* (HMENU) GetWindowLongA (hwnd, GWL_ID), */ + /* */ (HMENU) 0, /* */ + GetWindowLongA (hwnd, GWL_HINSTANCE), NULL); - /* *** new *** */ - SystemParametersInfoA (SPI_GETICONTITLELOGFONT, sizeof(mylogfont), &mylogfont, 0); - infoPtr->font = CreateFontIndirectA (&mylogfont); +#if 0 + /* ***** not needed any more ***** */ + + LPHEADCOMBO lphc; + /* + * The following does not work (WIN98 or later) but we need + * the list box handle for later + * SendMessageA(infoPtr->hwndCombo, CB_GETCOMBOBOXINFO, 0, + * (LPARAM) &comboinfo); + * infoPtr->hwndLBox = comboinfo.hwndList; + * + * So we need to violate philosophical separation by the following + * code. + */ + lphc = (LPHEADCOMBO) GetWindowLongA(infoPtr->hwndCombo, 0); + infoPtr->hwndLBox = lphc->hWndLBox; + if (!infoPtr->hwndLBox) { + ERR("error error listbox handle zero\n"); + } +#endif + + /* + * native does the following at this point according to trace: + * GetWindowThreadProcessId(hwndCombo,0) + * GetCurrentThreadId() + * GetWindowThreadProcessId(hwndCombo, &???) + * GetCurrentProcessId() + */ + + /* + * Setup a property to hold the pointer to the COMBOBOXEX + * data structure. + */ + test = GetPropA(infoPtr->hwndCombo, (LPCSTR)(LONG)ComboExInfo); + if (!test || ((COMBOEX_INFO *)test != infoPtr)) { + SetPropA(infoPtr->hwndCombo, "CC32SubclassInfo", (LONG)infoPtr); + } + infoPtr->prevComboWndProc = (WNDPROC)SetWindowLongA(infoPtr->hwndCombo, + GWL_WNDPROC, (LONG)COMBOEX_ComboWndProc); + infoPtr->font = SendMessageA (infoPtr->hwndCombo, WM_GETFONT, 0, 0); + + + /* + * Now create our own EDIT control so we can position it. + * It is created only for CBS_DROPDOWN style + */ + if ((cs->style & CBS_DROPDOWNLIST) == CBS_DROPDOWN) { + infoPtr->hwndEdit = CreateWindowExA (0, "EDIT", "", + WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | ES_AUTOHSCROLL, + 0, 0, 0, 0, /* will set later */ + infoPtr->hwndCombo, + (HMENU) GetWindowLongA (hwnd, GWL_ID), + GetWindowLongA (hwnd, GWL_HINSTANCE), + NULL); + + /* native does the following at this point according to trace: + * GetWindowThreadProcessId(hwndEdit,0) + * GetCurrentThreadId() + * GetWindowThreadProcessId(hwndEdit, &???) + * GetCurrentProcessId() + */ + + /* + * Setup a property to hold the pointer to the COMBOBOXEX + * data structure. + */ + test = GetPropA(infoPtr->hwndEdit, (LPCSTR)(LONG)ComboExInfo); + if (!test || ((COMBOEX_INFO *)test != infoPtr)) { + SetPropA(infoPtr->hwndEdit, "CC32SubclassInfo", (LONG)infoPtr); + } + infoPtr->prevEditWndProc = (WNDPROC)SetWindowLongA(infoPtr->hwndEdit, + GWL_WNDPROC, (LONG)COMBOEX_EditWndProc); + infoPtr->font = SendMessageA (infoPtr->hwndCombo, WM_GETFONT, 0, 0); + } + else { + infoPtr->hwndEdit = 0; + infoPtr->font = 0; + } + + /* + * Locate the default font if necessary and then set it in + * all associated controls + */ + if (!infoPtr->font) { + SystemParametersInfoA (SPI_GETICONTITLELOGFONT, sizeof(mylogfont), + &mylogfont, 0); + infoPtr->font = CreateFontIndirectA (&mylogfont); + } SendMessageA (infoPtr->hwndCombo, WM_SETFONT, (WPARAM)infoPtr->font, 0); + if (infoPtr->hwndEdit) { + SendMessageA (infoPtr->hwndEdit, WM_SETFONT, (WPARAM)infoPtr->font, 0); + SendMessageA (infoPtr->hwndEdit, EM_SETMARGINS, (WPARAM)EC_USEFONTINFO, 0); + } + COMBOEX_ReSize (hwnd, infoPtr); - /* *** end new *** */ + + /* Above is fairly certain, below is much less certain. */ + + GetWindowRect(hwnd, &wnrc1); + GetClientRect(hwnd, &clrc1); + GetWindowRect(infoPtr->hwndCombo, &cmbwrc); + TRACE("Ex wnd=(%d,%d)-(%d,%d) Ex clt=(%d,%d)-(%d,%d) Cb wnd=(%d,%d)-(%d,%d)\n", + wnrc1.left, wnrc1.top, wnrc1.right, wnrc1.bottom, + clrc1.left, clrc1.top, clrc1.right, clrc1.bottom, + cmbwrc.left, cmbwrc.top, cmbwrc.right, cmbwrc.bottom); + SetWindowPos(infoPtr->hwndCombo, HWND_TOP, + 0, 0, wnrc1.right-wnrc1.left, wnrc1.bottom-wnrc1.top, + SWP_NOACTIVATE | SWP_NOREDRAW); + + GetWindowRect(infoPtr->hwndCombo, &cmbwrc); + TRACE("Ex wnd=(%d,%d)-(%d,%d)\n", + cmbwrc.left, cmbwrc.top, cmbwrc.right, cmbwrc.bottom); + SetWindowPos(hwnd, HWND_TOP, + 0, 0, cmbwrc.right-cmbwrc.left, cmbwrc.bottom-cmbwrc.top, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE); + + COMBOEX_AdjustEditPos (infoPtr); + + /* + * Create an item structure to represent the data in the + * EDIT control. + */ + item = (CBE_ITEMDATA *)COMCTL32_Alloc (sizeof (CBE_ITEMDATA)); + item->next = NULL; + item->pszText = NULL; + item->mask = 0; + infoPtr->edit = item; return 0; } @@ -583,12 +860,11 @@ COMBOEX_DrawItem (HWND hwnd, WPARAM wParam, LPARAM lParam) { COMBOEX_INFO *infoPtr = COMBOEX_GetInfoPtr (hwnd); DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam; - CBE_ITEMDATA *item; + CBE_ITEMDATA *item = 0; SIZE txtsize; - COLORREF nbkc, ntxc; RECT rect; int drawimage; - UINT x, xbase, y; + UINT xbase; UINT xioff = 0; /* size and spacer of image if any */ IMAGEINFO iinfo; INT len; @@ -607,25 +883,69 @@ COMBOEX_DrawItem (HWND hwnd, WPARAM wParam, LPARAM lParam) /* member determines whether the rectangle is to be drawn as */ /* though the list box or combo box has the focus. */ if (dis->itemID == 0xffffffff) { - if ( ( (dis->itemAction & ODA_FOCUS) && (dis->itemState & ODS_SELECTED)) || - ( (dis->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)) && (dis->itemState & ODS_FOCUS) ) ) { - TRACE("drawing item -1 special focus, rect=(%d,%d)-(%d,%d)\n", - dis->rcItem.left, dis->rcItem.top, - dis->rcItem.right, dis->rcItem.bottom); - DrawFocusRect(dis->hDC, &dis->rcItem); - return 0; - } - else { - TRACE("NOT drawing item -1 special focus, rect=(%d,%d)-(%d,%d), action=%08x, state=%08x\n", - dis->rcItem.left, dis->rcItem.top, - dis->rcItem.right, dis->rcItem.bottom, - dis->itemAction, dis->itemState); - return 0; - } + if ( ( (dis->itemAction & ODA_FOCUS) && (dis->itemState & ODS_SELECTED)) || + ( (dis->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)) && (dis->itemState & ODS_FOCUS) ) ) { + TRACE("drawing item -1 special focus, rect=(%d,%d)-(%d,%d)\n", + dis->rcItem.left, dis->rcItem.top, + dis->rcItem.right, dis->rcItem.bottom); + DrawFocusRect(dis->hDC, &dis->rcItem); + return 0; + } + else if ((dis->CtlType == ODT_COMBOBOX) && + (dis->itemAction == ODA_DRAWENTIRE) && + (dis->itemState == 0)) { + /* draw of edit control data */ + CHAR str[260]; + INT wlen, alen; + + TRACE("drawing edit control\n"); + item = infoPtr->edit; + + if (item->pszText) { + COMCTL32_Free(item->pszText); + item->pszText = 0; + item->mask &= ~CBEIF_TEXT; + } + if (infoPtr->hwndEdit) { + alen = SendMessageA (infoPtr->hwndEdit, WM_GETTEXT, 260, (LPARAM)&str); + TRACE("hwndEdit=%0x, text len=%d str=<%s>\n", infoPtr->hwndEdit, alen, str); + if (alen > 0) { + item->mask |= CBEIF_TEXT; + wlen = MultiByteToWideChar (CP_ACP, 0, str, -1, NULL, 0); + if (wlen > 0) { + item->pszText = (LPWSTR)COMCTL32_Alloc ((wlen + 1)*sizeof(WCHAR)); + MultiByteToWideChar (CP_ACP, 0, str, -1, item->pszText, wlen); + } + } + } + + + /* testing */ + { + RECT exrc, cbrc, edrc; + GetWindowRect (hwnd, &exrc); + GetWindowRect (infoPtr->hwndCombo, &cbrc); + edrc.left=edrc.top=edrc.right=edrc.bottom=-1; + if (infoPtr->hwndEdit) + GetWindowRect (infoPtr->hwndEdit, &edrc); + TRACE("window rects ex=(%d,%d)-(%d,%d), cb=(%d,%d)-(%d,%d), ed=(%d,%d)-(%d,%d)\n", + exrc.left, exrc.top, exrc.right, exrc.bottom, + cbrc.left, cbrc.top, cbrc.right, cbrc.bottom, + edrc.left, edrc.top, edrc.right, edrc.bottom); + } + } + else { + ERR("NOT drawing item -1 special focus, rect=(%d,%d)-(%d,%d), action=%08x, state=%08x\n", + dis->rcItem.left, dis->rcItem.top, + dis->rcItem.right, dis->rcItem.bottom, + dis->itemAction, dis->itemState); + return 0; + } } - item = (CBE_ITEMDATA *)SendMessageA (infoPtr->hwndCombo, CB_GETITEMDATA, - (WPARAM)dis->itemID, 0); + if (!item) + item = (CBE_ITEMDATA *)SendMessageA (infoPtr->hwndCombo, + CB_GETITEMDATA, (WPARAM)dis->itemID, 0); if (item == (CBE_ITEMDATA *)CB_ERR) { FIXME("invalid item for id %d \n",dis->itemID); @@ -640,7 +960,7 @@ COMBOEX_DrawItem (HWND hwnd, WPARAM wParam, LPARAM lParam) dis->hwndItem, dis->hDC, dis->rcItem.left, dis->rcItem.top, dis->rcItem.right, dis->rcItem.bottom, dis->itemData); - } + } COMBOEX_DumpItem (item); xbase = CBE_STARTOFFSET; @@ -682,7 +1002,10 @@ COMBOEX_DrawItem (HWND hwnd, WPARAM wParam, LPARAM lParam) (dis->itemState & ODS_SELECTED) ? ILD_SELECTED : ILD_NORMAL); } - if ((item->mask & CBEIF_TEXT) && item->pszText) { + if ((item->mask & CBEIF_TEXT) && item->pszText) { + UINT x, y; + COLORREF nbkc, ntxc; + len = strlenW (item->pszText); GetTextExtentPointW (dis->hDC, item->pszText, len, &txtsize); nbkc = GetSysColor ((dis->itemState & ODS_SELECTED) ? @@ -703,7 +1026,7 @@ COMBOEX_DrawItem (HWND hwnd, WPARAM wParam, LPARAM lParam) ExtTextOutW (dis->hDC, x, y, ETO_OPAQUE | ETO_CLIPPED, &rect, item->pszText, len, 0); if (dis->itemState & ODS_FOCUS) { - rect.top -= 1; + rect.top -= 1; rect.bottom += 1; rect.left -= 1; rect.right += 2; @@ -730,6 +1053,11 @@ COMBOEX_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam) if (infoPtr->hwndCombo) DestroyWindow (infoPtr->hwndCombo); + if (infoPtr->edit) { + COMCTL32_Free (infoPtr->edit); + infoPtr->edit = 0; + } + if (infoPtr->items) { CBE_ITEMDATA *this, *next; @@ -778,11 +1106,15 @@ COMBOEX_Size (HWND hwnd, WPARAM wParam, LPARAM lParam) COMBOEX_INFO *infoPtr = COMBOEX_GetInfoPtr (hwnd); RECT rect; - GetClientRect (hwnd, &rect); + GetWindowRect (hwnd, &rect); + TRACE("my rect (%d,%d)-(%d,%d)\n", + rect.left, rect.top, rect.right, rect.bottom); MoveWindow (infoPtr->hwndCombo, 0, 0, rect.right -rect.left, rect.bottom - rect.top, TRUE); + COMBOEX_AdjustEditPos (infoPtr); + return 0; } @@ -818,6 +1150,221 @@ COMBOEX_WindowPosChanging (HWND hwnd, WPARAM wParam, LPARAM lParam) cb_wrect.bottom-cb_wrect.top, SWP_NOACTIVATE); + /* ****** new # 3 ******* */ + COMBOEX_AdjustEditPos (infoPtr); + + return 0; +} + + +static LRESULT WINAPI +COMBOEX_EditWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + COMBOEX_INFO *infoPtr = (COMBOEX_INFO *)GetPropA (hwnd, (LPCSTR)(LONG) ComboExInfo); + + TRACE("hwnd=%x msg=%x wparam=%x lParam=%lx, info_ptr=%p\n", + hwnd, uMsg, wParam, lParam, infoPtr); + + if (!infoPtr) return 0; + + switch (uMsg) + { + + case WM_KEYDOWN: { + NMCBEENDEDITA cbeend; + INT oldItem; + CBE_ITEMDATA *item; + + switch ((INT)wParam) + { + case VK_ESCAPE: + /* native version seems to do following for COMBOEX */ + /* + * GetWindowTextA(Edit,&?, 0x104) x + * CB_GETCURSEL to Combo rets -1 x + * WM_NOTIFY to COMBOEX parent (rebar) x + * (CBEN_ENDEDIT{A|W} + * fChanged = FALSE x + * inewSelection = -1 x + * txt="www.hoho" x + * iWhy = 3 x + * CB_GETCURSEL to Combo rets -1 x + * InvalidateRect(Combo, 0) x + * WM_SETTEXT to Edit x + * EM_SETSEL to Edit (0,0) x + * EM_SETSEL to Edit (0,-1) x + * RedrawWindow(Combo, 0, 0, 5) x + */ + TRACE("special code for VK_ESCAPE\n"); + + GetWindowTextA (infoPtr->hwndEdit, cbeend.szText, 260); + + infoPtr->flags &= ~WCBE_ACTEDIT; + cbeend.fChanged = FALSE; + cbeend.iNewSelection = SendMessageA (infoPtr->hwndCombo, + CB_GETCURSEL, 0, 0); + cbeend.iWhy = CBENF_ESCAPE; + + if (COMBOEX_Notify (infoPtr, CBEN_ENDEDITA, + (NMHDR *)&cbeend)) { + /* abort the change */ + return 0; + } + oldItem = SendMessageA (infoPtr->hwndCombo,CB_GETCURSEL, 0, 0); + InvalidateRect (infoPtr->hwndCombo, 0, 0); + if (!(item = COMBOEX_FindItem(infoPtr, oldItem))) { + ERR("item %d not found. Problem!\n", oldItem); + break; + } + + COMBOEX_SetEditText (infoPtr, item); + RedrawWindow (infoPtr->hwndCombo, 0, 0, RDW_ERASE | + RDW_INVALIDATE); + break; + + case VK_RETURN: + /* native version seems to do following for COMBOEX */ + /* + * GetWindowTextA(Edit,&?, 0x104) x + * CB_GETCURSEL to Combo rets -1 x + * CB_GETCOUNT to Combo rets 0 + * WM_NOTIFY to COMBOEX parent (rebar) x + * (CBEN_ENDEDIT{A|W} + * fChanged = TRUE (-1) x + * iNewSelection = -1 x + * txt= x + * iWhy = 2 (CBENF_RETURN) x + * CB_GETCURSEL to Combo rets -1 x + * InvalidateRect(Combo, 0, 0) x + * SetFocus(Edit) x + * CallWindowProc(406615a8, Edit, 0x100, 0xd, 0x1c0001) + */ + + TRACE("special code for VK_RETURN\n"); + + GetWindowTextA (infoPtr->hwndEdit, cbeend.szText, 260); + + infoPtr->flags &= ~WCBE_ACTEDIT; + cbeend.iNewSelection = SendMessageA (infoPtr->hwndCombo, + CB_GETCURSEL, 0, 0); + cbeend.fChanged = TRUE; + cbeend.iWhy = CBENF_RETURN; + + if (COMBOEX_Notify (infoPtr, CBEN_ENDEDITA, + (NMHDR *)&cbeend)) { + /* abort the change */ + return 0; + } + oldItem = SendMessageA (infoPtr->hwndCombo,CB_GETCURSEL, 0, 0); + InvalidateRect (infoPtr->hwndCombo, 0, 0); + SetFocus(infoPtr->hwndEdit); + break; + + default: + return CallWindowProcA (infoPtr->prevEditWndProc, + hwnd, uMsg, wParam, lParam); + } + return 0; + } + case WM_KILLFOCUS: + /* + * should do NOTIFY CBEN_ENDEDIT with CBENF_KILLFOCUS + * ** fall through for now ** + */ + infoPtr->flags &= ~WCBE_ACTEDIT; + + case WM_CHAR: + case WM_ERASEBKGND: + /* The above messages need code - will be delivered later */ + default: + return CallWindowProcA (infoPtr->prevEditWndProc, + hwnd, uMsg, wParam, lParam); + } + return 0; +} + + +static LRESULT WINAPI +COMBOEX_ComboWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + COMBOEX_INFO *infoPtr = (COMBOEX_INFO *)GetPropA (hwnd, (LPCSTR)(LONG) ComboExInfo); + + TRACE("hwnd=%x msg=%x wparam=%x lParam=%lx, info_ptr=%p\n", + hwnd, uMsg, wParam, lParam, infoPtr); + + if (!infoPtr) return 0; + + switch (uMsg) + { + case WM_COMMAND: + /* traces show that COMBOEX does not issue CBN_EDITUPDATE + * on the EN_UPDATE + */ + if (HIWORD(wParam) == EN_UPDATE) return 0; + + if (HIWORD(wParam) == EN_SETFOCUS) { + /* + * For EN_SETFOCUS this issues the same calls and messages + * as the native seems to do. + */ + NMHDR hdr; + + SendMessageA (infoPtr->hwndEdit, EM_SETSEL, 0, 0); + SendMessageA (infoPtr->hwndEdit, EM_SETSEL, 0, -1); + COMBOEX_Notify (infoPtr, CBEN_BEGINEDIT, &hdr); + infoPtr->flags |= WCBE_ACTEDIT; + return 0; + } + + if (HIWORD(wParam) == EN_CHANGE) { + /* + * For EN_CHANGE this issues the same calls and messages + * as the native seems to do. + */ + WCHAR selected_text[260]; + INT selected, cnt; + CBE_ITEMDATA *item; + + selected = SendMessageA (infoPtr->hwndCombo, + CB_GETCURSEL, 0, 0); + + /* lstrlenA( lastworkingURL ) */ + + if (selected == -1) + GetWindowTextW (infoPtr->hwndEdit, selected_text, 260); + else { + item = COMBOEX_FindItem (infoPtr, selected); + cnt = lstrlenW (item->pszText); + if (cnt >= 259) cnt = 259; + lstrcpynW (selected_text, item->pszText, cnt); + selected_text[cnt+1] = 0; + } + + TRACE("handling EN_CHANGE, selected = %d, text=%s\n", + selected, debugstr_w(selected_text)); + + /* lstrcmpiW is between lastworkingURL and GetWindowText */ + + if (lstrcmpiW (infoPtr->edit->pszText, selected_text)) { + /* strings not equal -- what to do???? */ + ERR("strings do not match\n"); + } + SendMessageA ( GetParent(infoPtr->hwndSelf), WM_COMMAND, + MAKEWPARAM(GetDlgCtrlID (infoPtr->hwndSelf), + CBN_EDITCHANGE), + infoPtr->hwndSelf); + return 0; + } + + /* fall through */ + + case WM_ERASEBKGND: + case WM_SETCURSOR: + /* The above messages need code - will be delivered later */ + default: + return CallWindowProcA (infoPtr->prevComboWndProc, + hwnd, uMsg, wParam, lParam); + } return 0; } @@ -848,9 +1395,11 @@ COMBOEX_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) /* case CBEM_GETITEMA: case CBEM_GETITEMW: case CBEM_GETUNICODEFORMAT: - case CBEM_HASEDITCHANGED: */ + case CBEM_HASEDITCHANGED: + return COMBOEX_HasEditChanged (hwnd, wParam, lParam); + case CBEM_INSERTITEMA: return COMBOEX_InsertItemA (hwnd, wParam, lParam); @@ -887,15 +1436,17 @@ COMBOEX_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case CB_LIMITTEXT: case CB_RESETCONTENT: case CB_SELECTSTRING: - case CB_SETCURSEL: - case CB_SETDROPPEDWIDTH: - case CB_SETEXTENDEDUI: + case CB_SETDROPPEDWIDTH: /* used by IE 4 */ + case CB_SETEXTENDEDUI: /* used by IE 4 */ case CB_SETITEMDATA: - case CB_SHOWDROPDOWN: + case CB_SHOWDROPDOWN: /* used by IE 4 */ case WM_SETTEXT: case WM_GETTEXT: return COMBOEX_Forward (hwnd, uMsg, wParam, lParam); + case CB_SETCURSEL: + return COMBOEX_SetCursel (hwnd, wParam, lParam); + case CB_SETITEMHEIGHT: return COMBOEX_SetItemHeight (hwnd, wParam, lParam); @@ -949,6 +1500,8 @@ COMBOEX_Register (void) wndClass.lpszClassName = WC_COMBOBOXEXA; RegisterClassA (&wndClass); + + ComboExInfo = GlobalAddAtomA("CC32SubclassInfo"); }