Reorganization of the loader to correctly load and free libraries and
[wine/multimedia.git] / controls / combo.c
blob6b1b3f8ba235af3ddbbb0da25f4f6a7af8ff6c05
1 /*
2 * Combo controls
3 *
4 * Copyright 1997 Alex Korobka
5 *
6 * FIXME: roll up in Netscape 3.01.
7 */
9 #include <string.h>
11 #include "winuser.h"
12 #include "wine/winuser16.h"
13 #include "sysmetrics.h"
14 #include "win.h"
15 #include "spy.h"
16 #include "user.h"
17 #include "heap.h"
18 #include "combo.h"
19 #include "drive.h"
20 #include "debug.h"
21 #include "tweak.h"
23 /* bits in the dwKeyData */
24 #define KEYDATA_ALT 0x2000
25 #define KEYDATA_PREVSTATE 0x4000
28 * Additional combo box definitions
31 #define CB_GETPTR( wnd ) (*(LPHEADCOMBO*)((wnd)->wExtra))
32 #define CB_NOTIFY( lphc, code ) \
33 (SendMessageA( (lphc)->owner, WM_COMMAND, \
34 MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf))
35 #define CB_GETEDITTEXTLENGTH( lphc ) \
36 (SendMessageA( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
39 * Drawing globals
41 static HBITMAP hComboBmp = 0;
42 static UINT CBitHeight, CBitWidth;
45 * Look and feel dependant "constants"
47 #define COMBO_XBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
48 #define COMBO_YBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
49 #define COMBO_EDITBUTTONSPACE() ( (TWEAK_WineLook == WIN31_LOOK) ? 8 : 0 )
50 #define EDIT_CONTROL_PADDING() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 1 )
52 /***********************************************************************
53 * COMBO_Init
55 * Load combo button bitmap.
57 static BOOL COMBO_Init()
59 HDC hDC;
61 if( hComboBmp ) return TRUE;
62 if( (hDC = CreateCompatibleDC(0)) )
64 BOOL bRet = FALSE;
65 if( (hComboBmp = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_COMBO))) )
67 BITMAP bm;
68 HBITMAP hPrevB;
69 RECT r;
71 GetObjectA( hComboBmp, sizeof(bm), &bm );
72 CBitHeight = bm.bmHeight;
73 CBitWidth = bm.bmWidth;
75 TRACE(combo, "combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
77 hPrevB = SelectObject16( hDC, hComboBmp);
78 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
79 InvertRect( hDC, &r );
80 SelectObject( hDC, hPrevB );
81 bRet = TRUE;
83 DeleteDC( hDC );
84 return bRet;
86 return FALSE;
89 /***********************************************************************
90 * COMBO_NCCreate
92 static LRESULT COMBO_NCCreate(WND* wnd, LPARAM lParam)
94 LPHEADCOMBO lphc;
96 if ( wnd && COMBO_Init() &&
97 (lphc = HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO))) )
99 LPCREATESTRUCTA lpcs = (CREATESTRUCTA*)lParam;
101 memset( lphc, 0, sizeof(HEADCOMBO) );
102 *(LPHEADCOMBO*)wnd->wExtra = lphc;
104 /* some braindead apps do try to use scrollbar/border flags */
106 lphc->dwStyle = (lpcs->style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL));
107 wnd->dwStyle &= ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
110 * We also have to remove the client edge style to make sure
111 * we don't end-up with a non client area.
113 wnd->dwExStyle &= ~(WS_EX_CLIENTEDGE);
115 if( !(lpcs->style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
116 lphc->dwStyle |= CBS_HASSTRINGS;
117 if( !(wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) )
118 lphc->wState |= CBF_NOTIFY;
120 TRACE(combo, "[0x%08x], style = %08x\n",
121 (UINT)lphc, lphc->dwStyle );
123 return (LRESULT)(UINT)wnd->hwndSelf;
125 return (LRESULT)FALSE;
128 /***********************************************************************
129 * COMBO_NCDestroy
131 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
134 if( lphc )
136 WND* wnd = lphc->self;
138 TRACE(combo,"[%04x]: freeing storage\n", CB_HWND(lphc));
140 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
141 DestroyWindow( lphc->hWndLBox );
143 HeapFree( GetProcessHeap(), 0, lphc );
144 wnd->wExtra[0] = 0;
146 return 0;
149 /***********************************************************************
150 * CBForceDummyResize
152 * The dummy resize is used for listboxes that have a popup to trigger
153 * a re-arranging of the contents of the combobox and the recalculation
154 * of the size of the "real" control window.
156 static void CBForceDummyResize(
157 HWND hwnd)
159 RECT windowRect;
161 GetWindowRect(hwnd, &windowRect);
163 SetWindowPos( hwnd,
164 (HWND)NULL,
165 0, 0,
166 windowRect.right - windowRect.left,
167 windowRect.bottom- windowRect.top + 1, /* dummy value adjusted later */
168 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
171 /***********************************************************************
172 * CBGetTextAreaHeight
174 * This method will calculate the height of the text area of the
175 * combobox.
176 * The height of the text area is set in two ways.
177 * It can be set explicitely through a combobox message of through a
178 * WM_MEASUREITEM callback.
179 * If this is not the case, the height is set to 13 dialog units.
180 * This height was determined through experimentation.
182 static INT CBGetTextAreaHeight(
183 HWND hwnd,
184 LPHEADCOMBO lphc)
186 INT iTextItemHeight;
188 if( lphc->editHeight ) /* explicitly set height */
190 iTextItemHeight = lphc->editHeight;
192 else
194 TEXTMETRICA tm;
195 HDC hDC = GetDC(hwnd);
196 HFONT hPrevFont = 0;
197 INT baseUnitY;
199 if (lphc->hFont)
200 hPrevFont = SelectObject( hDC, lphc->hFont );
202 GetTextMetricsA(hDC, &tm);
204 baseUnitY = tm.tmHeight;
206 if( hPrevFont )
207 SelectObject( hDC, hPrevFont );
209 ReleaseDC(hwnd, hDC);
211 iTextItemHeight = ((13 * baseUnitY) / 8);
214 * This "formula" calculates the height of the complete control.
215 * To calculate the height of the text area, we have to remove the
216 * borders.
218 iTextItemHeight -= 2*COMBO_YBORDERSIZE();
222 * Check the ownerdraw case if we haven't asked the parent the size
223 * of the item yet.
225 if ( CB_OWNERDRAWN(lphc) &&
226 (lphc->wState & CBF_MEASUREITEM) )
228 MEASUREITEMSTRUCT measureItem;
229 RECT clientRect;
230 INT originalItemHeight = iTextItemHeight;
233 * We use the client rect for the width of the item.
235 GetClientRect(hwnd, &clientRect);
237 lphc->wState &= ~CBF_MEASUREITEM;
240 * Send a first one to measure the size of the text area
242 measureItem.CtlType = ODT_COMBOBOX;
243 measureItem.CtlID = lphc->self->wIDmenu;
244 measureItem.itemID = -1;
245 measureItem.itemWidth = clientRect.right;
246 measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
247 measureItem.itemData = 0;
248 SendMessageA(lphc->owner, WM_MEASUREITEM,
249 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
250 iTextItemHeight = 6 + measureItem.itemHeight;
253 * Send a second one in the case of a fixed ownerdraw list to calculate the
254 * size of the list items. (we basically do this on behalf of the listbox)
256 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
258 measureItem.CtlType = ODT_COMBOBOX;
259 measureItem.CtlID = lphc->self->wIDmenu;
260 measureItem.itemID = 0;
261 measureItem.itemWidth = clientRect.right;
262 measureItem.itemHeight = originalItemHeight;
263 measureItem.itemData = 0;
264 SendMessageA(lphc->owner, WM_MEASUREITEM,
265 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
266 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
270 * Keep the size for the next time
272 lphc->editHeight = iTextItemHeight;
275 return iTextItemHeight;
279 /***********************************************************************
280 * CBCalcPlacement
282 * Set up component coordinates given valid lphc->RectCombo.
284 static void CBCalcPlacement(
285 HWND hwnd,
286 LPHEADCOMBO lphc,
287 LPRECT lprEdit,
288 LPRECT lprButton,
289 LPRECT lprLB)
292 * Again, start with the client rectangle.
294 GetClientRect(hwnd, lprEdit);
297 * Remove the borders
299 InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
302 * Chop off the bottom part to fit with the height of the text area.
304 lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
307 * The button starts the same vertical position as the text area.
309 CopyRect(lprButton, lprEdit);
312 * If the combobox is "simple" there is no button.
314 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
315 lprButton->left = lprButton->right = lprButton->bottom = 0;
316 else
319 * Let's assume the combobox button is the same width as the
320 * scrollbar button.
321 * size the button horizontally and cut-off the text area.
323 lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
324 lprEdit->right = lprButton->left;
328 * In the case of a dropdown, there is an additional spacing between the
329 * text area and the button.
331 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
333 lprEdit->right -= COMBO_EDITBUTTONSPACE();
337 * If we have an edit control, we space it away from the borders slightly.
339 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
341 InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
345 * Adjust the size of the listbox popup.
347 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
350 * Use the client rectangle to initialize the listbox rectangle
352 GetClientRect(hwnd, lprLB);
355 * Then, chop-off the top part.
357 lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
359 else
362 * Make sure the dropped width is as large as the combobox itself.
364 if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
366 lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
369 * In the case of a dropdown, the popup listbox is offset to the right.
370 * so, we want to make sure it's flush with the right side of the
371 * combobox
373 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
374 lprLB->right -= COMBO_EDITBUTTONSPACE();
376 else
377 lprLB->right = lprLB->left + lphc->droppedWidth;
380 TRACE(combo,"\ttext\t= (%i,%i-%i,%i)\n",
381 lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
383 TRACE(combo,"\tbutton\t= (%i,%i-%i,%i)\n",
384 lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
386 TRACE(combo,"\tlbox\t= (%i,%i-%i,%i)\n",
387 lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
390 /***********************************************************************
391 * CBGetDroppedControlRect
393 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
395 CopyRect(lpRect, &lphc->droppedRect);
398 /***********************************************************************
399 * COMBO_WindowPosChanging
401 static LRESULT COMBO_WindowPosChanging(
402 HWND hwnd,
403 LPHEADCOMBO lphc,
404 WINDOWPOS* posChanging)
407 * We need to override the WM_WINDOWPOSCHANGING method to handle all
408 * the non-simple comboboxes. The problem is that those controls are
409 * always the same height. We have to make sure they are not resized
410 * to another value.
412 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
414 posChanging->cy = CBGetTextAreaHeight(hwnd,lphc) +
415 2*COMBO_YBORDERSIZE();
418 return 0;
421 /***********************************************************************
422 * COMBO_Create
424 static LRESULT COMBO_Create( LPHEADCOMBO lphc, WND* wnd, LPARAM lParam)
426 static char clbName[] = "ComboLBox";
427 static char editName[] = "Edit";
429 LPCREATESTRUCTA lpcs = (CREATESTRUCTA*)lParam;
431 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
432 else if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
434 lphc->self = wnd;
435 lphc->owner = lpcs->hwndParent;
438 * The item height and dropped width are not set when the control
439 * is created.
441 lphc->droppedWidth = lphc->editHeight = 0;
444 * The first time we go through, we want to measure the ownerdraw item
446 lphc->wState |= CBF_MEASUREITEM;
448 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
450 if( lphc->owner || !(lpcs->style & WS_VISIBLE) )
452 UINT lbeStyle;
455 * Initialize the dropped rect to the size of the client area of the
456 * control and then, force all the areas of the combobox to be
457 * recalculated.
459 GetClientRect( wnd->hwndSelf, &lphc->droppedRect );
461 CBCalcPlacement(wnd->hwndSelf,
462 lphc,
463 &lphc->textRect,
464 &lphc->buttonRect,
465 &lphc->droppedRect );
468 * Adjust the position of the popup listbox if it's necessary
470 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
472 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
475 * If it's a dropdown, the listbox is offset
477 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
478 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
480 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect);
481 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect.right);
484 /* create listbox popup */
486 lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS) |
487 (lpcs->style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
489 if( lphc->dwStyle & CBS_SORT )
490 lbeStyle |= LBS_SORT;
491 if( lphc->dwStyle & CBS_HASSTRINGS )
492 lbeStyle |= LBS_HASSTRINGS;
493 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
494 lbeStyle |= LBS_NOINTEGRALHEIGHT;
495 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
496 lbeStyle |= LBS_DISABLENOSCROLL;
498 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
499 lbeStyle |= WS_CHILD | WS_VISIBLE;
500 else /* popup listbox */
501 lbeStyle |= WS_POPUP;
503 /* Dropdown ComboLBox is not a child window and we cannot pass
504 * ID_CB_LISTBOX directly because it will be treated as a menu handle.
506 lphc->hWndLBox = CreateWindowExA(0,
507 clbName,
508 NULL,
509 lbeStyle,
510 lphc->droppedRect.left,
511 lphc->droppedRect.top,
512 lphc->droppedRect.right - lphc->droppedRect.left,
513 lphc->droppedRect.bottom - lphc->droppedRect.top,
514 lphc->self->hwndSelf,
515 (lphc->dwStyle & CBS_DROPDOWN)? (HMENU)0 : (HMENU)ID_CB_LISTBOX,
516 lphc->self->hInstance,
517 (LPVOID)lphc );
519 if( lphc->hWndLBox )
521 BOOL bEdit = TRUE;
522 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT;
525 * In Win95 look, the border fo the edit control is
526 * provided by the combobox
528 if (TWEAK_WineLook == WIN31_LOOK)
529 lbeStyle |= WS_BORDER;
531 if( lphc->wState & CBF_EDIT )
533 if( lphc->dwStyle & CBS_OEMCONVERT )
534 lbeStyle |= ES_OEMCONVERT;
535 if( lphc->dwStyle & CBS_AUTOHSCROLL )
536 lbeStyle |= ES_AUTOHSCROLL;
537 if( lphc->dwStyle & CBS_LOWERCASE )
538 lbeStyle |= ES_LOWERCASE;
539 else if( lphc->dwStyle & CBS_UPPERCASE )
540 lbeStyle |= ES_UPPERCASE;
542 lphc->hWndEdit = CreateWindowExA(0,
543 editName,
544 NULL,
545 lbeStyle,
546 lphc->textRect.left, lphc->textRect.top,
547 lphc->textRect.right - lphc->textRect.left,
548 lphc->textRect.bottom - lphc->textRect.top,
549 lphc->self->hwndSelf,
550 (HMENU)ID_CB_EDIT,
551 lphc->self->hInstance,
552 NULL );
554 if( !lphc->hWndEdit )
555 bEdit = FALSE;
558 if( bEdit )
561 * If the combo is a dropdown, we must resize the control to fit only
562 * the text area and button. To do this, we send a dummy resize and the
563 * WM_WINDOWPOSCHANGING message will take care of setting the height for
564 * us.
566 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
568 CBForceDummyResize(CB_HWND(lphc));
571 TRACE(combo,"init done\n");
572 return wnd->hwndSelf;
574 ERR(combo, "edit control failure.\n");
575 } else ERR(combo, "listbox failure.\n");
576 } else ERR(combo, "no owner for visible combo.\n");
578 /* CreateWindow() will send WM_NCDESTROY to cleanup */
580 return -1;
583 /***********************************************************************
584 * CBPaintButton
586 * Paint combo button (normal, pressed, and disabled states).
588 static void CBPaintButton(
589 LPHEADCOMBO lphc,
590 HDC hdc,
591 RECT rectButton)
593 UINT x, y;
594 BOOL bBool;
595 HDC hMemDC;
596 HBRUSH hPrevBrush;
597 COLORREF oldTextColor, oldBkColor;
599 if( lphc->wState & CBF_NOREDRAW )
600 return;
602 hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
605 * Draw the button background
607 PatBlt( hdc,
608 rectButton.left,
609 rectButton.top,
610 rectButton.right-rectButton.left,
611 rectButton.bottom-rectButton.top,
612 PATCOPY );
614 if( (bBool = lphc->wState & CBF_BUTTONDOWN) )
616 DrawEdge( hdc, &rectButton, EDGE_SUNKEN, BF_RECT );
618 else
620 DrawEdge( hdc, &rectButton, EDGE_RAISED, BF_RECT );
624 * Remove the edge of the button from the rectangle
625 * and calculate the position of the bitmap.
627 InflateRect( &rectButton, -2, -2);
629 x = (rectButton.left + rectButton.right - CBitWidth) >> 1;
630 y = (rectButton.top + rectButton.bottom - CBitHeight) >> 1;
633 hMemDC = CreateCompatibleDC( hdc );
634 SelectObject( hMemDC, hComboBmp );
635 oldTextColor = SetTextColor( hdc, GetSysColor(COLOR_BTNFACE) );
636 oldBkColor = SetBkColor( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) :
637 RGB(0,0,0) );
638 BitBlt( hdc, x, y, CBitWidth, CBitHeight, hMemDC, 0, 0, SRCCOPY );
639 SetBkColor( hdc, oldBkColor );
640 SetTextColor( hdc, oldTextColor );
641 DeleteDC( hMemDC );
642 SelectObject( hdc, hPrevBrush );
645 /***********************************************************************
646 * CBPaintText
648 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
650 static void CBPaintText(
651 LPHEADCOMBO lphc,
652 HDC hdc,
653 RECT rectEdit)
655 INT id, size = 0;
656 LPSTR pText = NULL;
658 if( lphc->wState & CBF_NOREDRAW ) return;
660 /* follow Windows combobox that sends a bunch of text
661 * inquiries to its listbox while processing WM_PAINT. */
663 if( (id = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
665 size = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
666 if( (pText = HeapAlloc( GetProcessHeap(), 0, size + 1)) )
668 SendMessageA( lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText );
669 pText[size] = '\0'; /* just in case */
670 } else return;
673 if( lphc->wState & CBF_EDIT )
675 if( CB_HASSTRINGS(lphc) ) SetWindowTextA( lphc->hWndEdit, pText ? pText : "" );
676 if( lphc->wState & CBF_FOCUSED )
677 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
679 else /* paint text field ourselves */
681 HBRUSH hPrevBrush = 0;
682 HDC hDC = hdc;
684 if( !hDC )
686 if ((hDC = GetDC(lphc->self->hwndSelf)))
688 HBRUSH hBrush = SendMessageA( lphc->owner,
689 WM_CTLCOLORLISTBOX,
690 hDC, lphc->self->hwndSelf );
691 hPrevBrush = SelectObject( hDC,
692 (hBrush) ? hBrush : GetStockObject(WHITE_BRUSH) );
695 if( hDC )
697 UINT itemState;
698 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hDC, lphc->hFont) : 0;
701 * Give ourselves some space.
703 InflateRect( &rectEdit, -1, -1 );
705 if ( (lphc->wState & CBF_FOCUSED) &&
706 !(lphc->wState & CBF_DROPPED) )
708 /* highlight */
710 FillRect( hDC, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
711 SetBkColor( hDC, GetSysColor( COLOR_HIGHLIGHT ) );
712 SetTextColor( hDC, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
713 itemState = ODS_SELECTED | ODS_FOCUS;
715 else
716 itemState = 0;
718 if( CB_OWNERDRAWN(lphc) )
720 DRAWITEMSTRUCT dis;
721 HRGN clipRegion;
724 * Save the current clip region.
725 * To retrieve the clip region, we need to create one "dummy"
726 * clip region.
728 clipRegion = CreateRectRgnIndirect(&rectEdit);
730 if (GetClipRgn(hDC, clipRegion)!=1)
732 DeleteObject(clipRegion);
733 clipRegion=(HRGN)NULL;
736 if ( lphc->self->dwStyle & WS_DISABLED )
737 itemState |= ODS_DISABLED;
739 dis.CtlType = ODT_COMBOBOX;
740 dis.CtlID = lphc->self->wIDmenu;
741 dis.hwndItem = lphc->self->hwndSelf;
742 dis.itemAction = ODA_DRAWENTIRE;
743 dis.itemID = id;
744 dis.itemState = itemState;
745 dis.hDC = hDC;
746 dis.rcItem = rectEdit;
747 dis.itemData = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA,
748 (WPARAM)id, 0 );
751 * Clip the DC and have the parent draw the item.
753 IntersectClipRect(hDC,
754 rectEdit.left, rectEdit.top,
755 rectEdit.right, rectEdit.bottom);
757 SendMessageA(lphc->owner, WM_DRAWITEM,
758 lphc->self->wIDmenu, (LPARAM)&dis );
761 * Reset the clipping region.
763 SelectClipRgn(hDC, clipRegion);
765 else
767 ExtTextOutA( hDC,
768 rectEdit.left + 1,
769 rectEdit.top + 1,
770 ETO_OPAQUE | ETO_CLIPPED,
771 &rectEdit,
772 pText ? pText : "" , size, NULL );
774 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
775 DrawFocusRect( hDC, &rectEdit );
778 if( hPrevFont )
779 SelectObject(hDC, hPrevFont );
781 if( !hdc )
783 if( hPrevBrush )
784 SelectObject( hDC, hPrevBrush );
786 ReleaseDC( lphc->self->hwndSelf, hDC );
790 if (pText)
791 HeapFree( GetProcessHeap(), 0, pText );
794 /***********************************************************************
795 * CBPaintBorder
797 static void CBPaintBorder(
798 HWND hwnd,
799 LPHEADCOMBO lphc,
800 HDC hdc)
802 RECT clientRect;
804 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
806 GetClientRect(hwnd, &clientRect);
808 else
810 CopyRect(&clientRect, &lphc->textRect);
812 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
813 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
816 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
819 /***********************************************************************
820 * COMBO_EraseBackground
822 static LRESULT COMBO_EraseBackground(
823 HWND hwnd,
824 LPHEADCOMBO lphc,
825 HDC hParamDC)
827 HBRUSH hBkgBrush;
828 RECT clientRect;
829 HDC hDC;
831 hDC = (hParamDC) ? hParamDC
832 : GetDC(hwnd);
835 * Calculate the area that we want to erase.
837 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
839 GetClientRect(hwnd, &clientRect);
841 else
843 CopyRect(&clientRect, &lphc->textRect);
845 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
848 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
849 hDC, hwnd);
851 if( !hBkgBrush )
852 hBkgBrush = GetStockObject(WHITE_BRUSH);
854 FillRect(hDC, &clientRect, hBkgBrush);
856 if (!hParamDC)
857 ReleaseDC(hwnd, hDC);
859 return TRUE;
862 /***********************************************************************
863 * COMBO_Paint
865 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
867 PAINTSTRUCT ps;
868 HDC hDC;
870 hDC = (hParamDC) ? hParamDC
871 : BeginPaint( lphc->self->hwndSelf, &ps);
874 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
876 HBRUSH hPrevBrush, hBkgBrush;
878 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
879 hDC, lphc->self->hwndSelf );
881 if( !hBkgBrush )
882 hBkgBrush = GetStockObject(WHITE_BRUSH);
884 hPrevBrush = SelectObject( hDC, hBkgBrush );
887 * In non 3.1 look, there is a sunken border on the combobox
889 if (TWEAK_WineLook != WIN31_LOOK)
891 CBPaintBorder(CB_HWND(lphc), lphc, hDC);
894 if( !IsRectEmpty(&lphc->buttonRect) )
896 CBPaintButton(lphc, hDC, lphc->buttonRect);
899 if( !(lphc->wState & CBF_EDIT) )
902 * The text area has a border only in Win 3.1 look.
904 if (TWEAK_WineLook == WIN31_LOOK)
906 HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
908 Rectangle( hDC,
909 lphc->textRect.left, lphc->textRect.top,
910 lphc->textRect.right - 1, lphc->textRect.bottom - 1);
912 SelectObject( hDC, hPrevPen );
915 CBPaintText( lphc, hDC, lphc->textRect);
918 if( hPrevBrush )
919 SelectObject( hDC, hPrevBrush );
922 if( !hParamDC )
923 EndPaint(lphc->self->hwndSelf, &ps);
925 return 0;
928 /***********************************************************************
929 * CBUpdateLBox
931 * Select listbox entry according to the contents of the edit control.
933 static INT CBUpdateLBox( LPHEADCOMBO lphc )
935 INT length, idx, ret;
936 LPSTR pText = NULL;
938 idx = ret = LB_ERR;
939 length = CB_GETEDITTEXTLENGTH( lphc );
941 if( length > 0 )
942 pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1);
944 TRACE(combo,"\t edit text length %i\n", length );
946 if( pText )
948 if( length ) GetWindowTextA( lphc->hWndEdit, pText, length + 1);
949 else pText[0] = '\0';
950 idx = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
951 (WPARAM)(-1), (LPARAM)pText );
952 if( idx == LB_ERR ) idx = 0; /* select first item */
953 else ret = idx;
954 HeapFree( GetProcessHeap(), 0, pText );
957 /* select entry */
959 SendMessageA( lphc->hWndLBox, LB_SETCURSEL, (WPARAM)idx, 0 );
961 if( idx >= 0 )
963 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)idx, 0 );
964 /* probably superfluous but Windows sends this too */
965 SendMessageA( lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)idx, 0 );
967 return ret;
970 /***********************************************************************
971 * CBUpdateEdit
973 * Copy a listbox entry to the edit control.
975 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
977 INT length;
978 LPSTR pText = NULL;
980 TRACE(combo,"\t %i\n", index );
982 if( index == -1 )
984 length = CB_GETEDITTEXTLENGTH( lphc );
985 if( length )
987 if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
989 GetWindowTextA( lphc->hWndEdit, pText, length + 1 );
990 index = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
991 (WPARAM)(-1), (LPARAM)pText );
992 HeapFree( GetProcessHeap(), 0, pText );
997 if( index >= 0 ) /* got an entry */
999 length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1000 if( length )
1002 if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
1004 SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1005 (WPARAM)index, (LPARAM)pText );
1006 SendMessageA( lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)pText );
1007 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1008 HeapFree( GetProcessHeap(), 0, pText );
1014 /***********************************************************************
1015 * CBDropDown
1017 * Show listbox popup.
1019 static void CBDropDown( LPHEADCOMBO lphc )
1021 INT index;
1022 RECT rect;
1024 TRACE(combo,"[%04x]: drop down\n", CB_HWND(lphc));
1026 CB_NOTIFY( lphc, CBN_DROPDOWN );
1028 /* set selection */
1030 lphc->wState |= CBF_DROPPED;
1031 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1033 index = CBUpdateLBox( lphc );
1034 if( !(lphc->wState & CBF_CAPTURE) ) CBUpdateEdit( lphc, index );
1036 else
1038 index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1039 if( index == LB_ERR ) index = 0;
1040 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)index, 0 );
1041 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1044 /* now set popup position */
1045 GetWindowRect( lphc->self->hwndSelf, &rect );
1049 * If it's a dropdown, the listbox is offset
1051 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1052 rect.left += COMBO_EDITBUTTONSPACE();
1054 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1055 rect.right - rect.left, rect.bottom - rect.top,
1056 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOREDRAW);
1058 if( !(lphc->wState & CBF_NOREDRAW) )
1059 RedrawWindow( lphc->self->hwndSelf, NULL, 0, RDW_INVALIDATE |
1060 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1062 ShowWindow( lphc->hWndLBox, SW_SHOWNA );
1065 /***********************************************************************
1066 * CBRollUp
1068 * Hide listbox popup.
1070 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1072 HWND hWnd = lphc->self->hwndSelf;
1074 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1076 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1079 TRACE(combo,"[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
1081 /* always send WM_LBUTTONUP? */
1082 SendMessageA( lphc->hWndLBox, WM_LBUTTONUP, 0, (LPARAM)(-1) );
1084 if( lphc->wState & CBF_DROPPED )
1086 RECT rect;
1088 lphc->wState &= ~CBF_DROPPED;
1089 ShowWindow( lphc->hWndLBox, SW_HIDE );
1091 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1093 INT index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1094 CBUpdateEdit( lphc, index );
1095 rect = lphc->buttonRect;
1097 else
1099 if( bButton )
1101 UnionRect( &rect,
1102 &lphc->buttonRect,
1103 &lphc->textRect);
1105 else
1106 rect = lphc->textRect;
1108 bButton = TRUE;
1111 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1112 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1113 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1114 CB_NOTIFY( lphc, CBN_CLOSEUP );
1119 /***********************************************************************
1120 * COMBO_FlipListbox
1122 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1124 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL bRedrawButton )
1126 if( lphc->wState & CBF_DROPPED )
1128 CBRollUp( lphc, TRUE, bRedrawButton );
1129 return FALSE;
1132 CBDropDown( lphc );
1133 return TRUE;
1136 /***********************************************************************
1137 * COMBO_GetLBWindow
1139 * Edit control helper.
1141 HWND COMBO_GetLBWindow( WND* pWnd )
1143 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1144 if( lphc ) return lphc->hWndLBox;
1145 return 0;
1149 /***********************************************************************
1150 * CBRepaintButton
1152 static void CBRepaintButton( LPHEADCOMBO lphc )
1154 InvalidateRect(CB_HWND(lphc), &lphc->buttonRect, TRUE);
1155 UpdateWindow(CB_HWND(lphc));
1158 /***********************************************************************
1159 * COMBO_SetFocus
1161 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1163 if( !(lphc->wState & CBF_FOCUSED) )
1165 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1166 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1168 if( lphc->wState & CBF_EDIT )
1169 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1170 lphc->wState |= CBF_FOCUSED;
1171 if( !(lphc->wState & CBF_EDIT) )
1173 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1176 CB_NOTIFY( lphc, CBN_SETFOCUS );
1180 /***********************************************************************
1181 * COMBO_KillFocus
1183 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1185 HWND hWnd = lphc->self->hwndSelf;
1187 if( lphc->wState & CBF_FOCUSED )
1189 SendMessageA( hWnd, WM_LBUTTONUP, 0, (LPARAM)(-1) );
1191 CBRollUp( lphc, FALSE, TRUE );
1192 if( IsWindow( hWnd ) )
1194 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1195 SendMessageA( lphc->hWndLBox, LB_CARETOFF, 0, 0 );
1197 lphc->wState &= ~CBF_FOCUSED;
1199 /* redraw text */
1200 if( lphc->wState & CBF_EDIT )
1201 SendMessageA( lphc->hWndEdit, EM_SETSEL, (WPARAM)(-1), 0 );
1202 else
1204 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1207 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1212 /***********************************************************************
1213 * COMBO_Command
1215 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1217 if( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1219 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1221 switch( HIWORD(wParam) >> 8 )
1223 case (EN_SETFOCUS >> 8):
1225 TRACE(combo,"[%04x]: edit [%04x] got focus\n",
1226 CB_HWND(lphc), lphc->hWndEdit );
1228 if( !(lphc->wState & CBF_FOCUSED) ) COMBO_SetFocus( lphc );
1229 break;
1231 case (EN_KILLFOCUS >> 8):
1233 TRACE(combo,"[%04x]: edit [%04x] lost focus\n",
1234 CB_HWND(lphc), lphc->hWndEdit );
1236 /* NOTE: it seems that Windows' edit control sends an
1237 * undocumented message WM_USER + 0x1B instead of this
1238 * notification (only when it happens to be a part of
1239 * the combo). ?? - AK.
1242 COMBO_KillFocus( lphc );
1243 break;
1246 case (EN_CHANGE >> 8):
1247 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1248 CBUpdateLBox( lphc );
1249 break;
1251 case (EN_UPDATE >> 8):
1252 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1253 break;
1255 case (EN_ERRSPACE >> 8):
1256 CB_NOTIFY( lphc, CBN_ERRSPACE );
1259 else if( lphc->hWndLBox == hWnd )
1261 switch( HIWORD(wParam) )
1263 case LBN_ERRSPACE:
1264 CB_NOTIFY( lphc, CBN_ERRSPACE );
1265 break;
1267 case LBN_DBLCLK:
1268 CB_NOTIFY( lphc, CBN_DBLCLK );
1269 break;
1271 case LBN_SELCHANGE:
1272 case LBN_SELCANCEL:
1274 TRACE(combo,"[%04x]: lbox selection change [%04x]\n",
1275 CB_HWND(lphc), lphc->wState );
1277 /* do not roll up if selection is being tracked
1278 * by arrowkeys in the dropdown listbox */
1280 if( (lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP) )
1281 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1282 else lphc->wState &= ~CBF_NOROLLUP;
1284 CB_NOTIFY( lphc, CBN_SELCHANGE );
1285 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1286 /* fall through */
1288 case LBN_SETFOCUS:
1289 case LBN_KILLFOCUS:
1290 /* nothing to do here since ComboLBox always resets the focus to its
1291 * combo/edit counterpart */
1292 break;
1295 return 0;
1298 /***********************************************************************
1299 * COMBO_ItemOp
1301 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1303 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg,
1304 WPARAM wParam, LPARAM lParam )
1306 HWND hWnd = lphc->self->hwndSelf;
1308 TRACE(combo,"[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
1310 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
1312 /* two first items are the same in all 4 structs */
1313 lpIS->CtlType = ODT_COMBOBOX;
1314 lpIS->CtlID = lphc->self->wIDmenu;
1316 switch( msg ) /* patch window handle */
1318 case WM_DELETEITEM:
1319 lpIS->hwndItem = hWnd;
1320 #undef lpIS
1321 break;
1322 case WM_DRAWITEM:
1323 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
1324 lpIS->hwndItem = hWnd;
1325 #undef lpIS
1326 break;
1327 case WM_COMPAREITEM:
1328 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
1329 lpIS->hwndItem = hWnd;
1330 #undef lpIS
1331 break;
1334 return SendMessageA( lphc->owner, msg, lphc->self->wIDmenu, lParam );
1337 /***********************************************************************
1338 * COMBO_GetText
1340 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT N, LPSTR lpText)
1342 if( lphc->wState & CBF_EDIT )
1343 return SendMessageA( lphc->hWndEdit, WM_GETTEXT,
1344 (WPARAM)N, (LPARAM)lpText );
1346 /* get it from the listbox */
1348 if( lphc->hWndLBox )
1350 INT idx = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1351 if( idx != LB_ERR )
1353 LPSTR lpBuffer;
1354 INT length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN,
1355 (WPARAM)idx, 0 );
1357 /* 'length' is without the terminating character */
1358 if( length >= N )
1359 lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 );
1360 else
1361 lpBuffer = lpText;
1363 if( lpBuffer )
1365 INT n = SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1366 (WPARAM)idx, (LPARAM)lpBuffer );
1368 /* truncate if buffer is too short */
1370 if( length >= N )
1372 if (N && lpText) {
1373 if( n != LB_ERR ) memcpy( lpText, lpBuffer, (N>n) ? n+1 : N-1 );
1374 lpText[N - 1] = '\0';
1376 HeapFree( GetProcessHeap(), 0, lpBuffer );
1378 return (LRESULT)n;
1382 return 0;
1386 /***********************************************************************
1387 * CBResetPos
1389 * This function sets window positions according to the updated
1390 * component placement struct.
1392 static void CBResetPos(
1393 LPHEADCOMBO lphc,
1394 LPRECT rectEdit,
1395 LPRECT rectLB,
1396 BOOL bRedraw)
1398 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1400 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1401 * sizing messages */
1403 if( lphc->wState & CBF_EDIT )
1404 SetWindowPos( lphc->hWndEdit, 0,
1405 rectEdit->left, rectEdit->top,
1406 rectEdit->right - rectEdit->left,
1407 rectEdit->bottom - rectEdit->top,
1408 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1410 SetWindowPos( lphc->hWndLBox, 0,
1411 rectLB->left, rectLB->top,
1412 rectLB->right - rectLB->left,
1413 rectLB->bottom - rectLB->top,
1414 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1416 if( bDrop )
1418 if( lphc->wState & CBF_DROPPED )
1420 lphc->wState &= ~CBF_DROPPED;
1421 ShowWindow( lphc->hWndLBox, SW_HIDE );
1424 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1425 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
1426 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1431 /***********************************************************************
1432 * COMBO_Size
1434 static void COMBO_Size( LPHEADCOMBO lphc )
1436 CBCalcPlacement(lphc->self->hwndSelf,
1437 lphc,
1438 &lphc->textRect,
1439 &lphc->buttonRect,
1440 &lphc->droppedRect);
1442 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1446 /***********************************************************************
1447 * COMBO_Font
1449 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1452 * Set the font
1454 lphc->hFont = hFont;
1457 * Propagate to owned windows.
1459 if( lphc->wState & CBF_EDIT )
1460 SendMessageA( lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw );
1461 SendMessageA( lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw );
1464 * Redo the layout of the control.
1466 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1468 CBCalcPlacement(lphc->self->hwndSelf,
1469 lphc,
1470 &lphc->textRect,
1471 &lphc->buttonRect,
1472 &lphc->droppedRect);
1474 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1476 else
1478 CBForceDummyResize(CB_HWND(lphc));
1483 /***********************************************************************
1484 * COMBO_SetItemHeight
1486 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1488 LRESULT lRet = CB_ERR;
1490 if( index == -1 ) /* set text field height */
1492 if( height < 32768 )
1494 lphc->editHeight = height;
1497 * Redo the layout of the control.
1499 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1501 CBCalcPlacement(lphc->self->hwndSelf,
1502 lphc,
1503 &lphc->textRect,
1504 &lphc->buttonRect,
1505 &lphc->droppedRect);
1507 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1509 else
1511 CBForceDummyResize(CB_HWND(lphc));
1514 lRet = height;
1517 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1518 lRet = SendMessageA( lphc->hWndLBox, LB_SETITEMHEIGHT,
1519 (WPARAM)index, (LPARAM)height );
1520 return lRet;
1523 /***********************************************************************
1524 * COMBO_SelectString
1526 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPCSTR pText )
1528 INT index = SendMessageA( lphc->hWndLBox, LB_SELECTSTRING,
1529 (WPARAM)start, (LPARAM)pText );
1530 if( index >= 0 )
1532 if( lphc->wState & CBF_EDIT )
1533 CBUpdateEdit( lphc, index );
1534 else
1536 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1539 return (LRESULT)index;
1542 /***********************************************************************
1543 * COMBO_LButtonDown
1545 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1547 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
1548 BOOL bButton;
1549 HWND hWnd = lphc->self->hwndSelf;
1551 bButton = PtInRect(&lphc->buttonRect, pt);
1553 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1554 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1556 lphc->wState |= CBF_BUTTONDOWN;
1557 if( lphc->wState & CBF_DROPPED )
1559 /* got a click to cancel selection */
1561 lphc->wState &= ~CBF_BUTTONDOWN;
1562 CBRollUp( lphc, TRUE, FALSE );
1563 if( !IsWindow( hWnd ) ) return;
1565 if( lphc->wState & CBF_CAPTURE )
1567 lphc->wState &= ~CBF_CAPTURE;
1568 ReleaseCapture();
1571 else
1573 /* drop down the listbox and start tracking */
1575 lphc->wState |= CBF_CAPTURE;
1576 CBDropDown( lphc );
1577 SetCapture( hWnd );
1579 if( bButton ) CBRepaintButton( lphc );
1583 /***********************************************************************
1584 * COMBO_LButtonUp
1586 * Release capture and stop tracking if needed.
1588 static void COMBO_LButtonUp( LPHEADCOMBO lphc, LPARAM lParam )
1590 if( lphc->wState & CBF_CAPTURE )
1592 lphc->wState &= ~CBF_CAPTURE;
1593 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1595 INT index = CBUpdateLBox( lphc );
1596 CBUpdateEdit( lphc, index );
1598 ReleaseCapture();
1601 if( lphc->wState & CBF_BUTTONDOWN )
1603 lphc->wState &= ~CBF_BUTTONDOWN;
1604 CBRepaintButton( lphc );
1608 /***********************************************************************
1609 * COMBO_MouseMove
1611 * Two things to do - track combo button and release capture when
1612 * pointer goes into the listbox.
1614 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1616 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
1617 RECT lbRect;
1619 if( lphc->wState & CBF_BUTTONDOWN )
1621 BOOL bButton;
1623 bButton = PtInRect(&lphc->buttonRect, pt);
1625 if( !bButton )
1627 lphc->wState &= ~CBF_BUTTONDOWN;
1628 CBRepaintButton( lphc );
1632 GetClientRect( lphc->hWndLBox, &lbRect );
1633 MapWindowPoints( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 );
1634 if( PtInRect(&lbRect, pt) )
1636 lphc->wState &= ~CBF_CAPTURE;
1637 ReleaseCapture();
1638 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc );
1640 /* hand over pointer tracking */
1641 SendMessageA( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam );
1646 /***********************************************************************
1647 * ComboWndProc
1649 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1651 LRESULT WINAPI ComboWndProc( HWND hwnd, UINT message,
1652 WPARAM wParam, LPARAM lParam )
1654 LRESULT retvalue;
1655 WND* pWnd = WIN_FindWndPtr(hwnd);
1657 if( pWnd )
1659 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1661 TRACE(combo, "[%04x]: msg %s wp %08x lp %08lx\n",
1662 pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
1664 if( lphc || message == WM_NCCREATE )
1665 switch(message)
1668 /* System messages */
1670 case WM_NCCREATE:
1671 retvalue = COMBO_NCCreate(pWnd, lParam);
1672 goto END;
1673 case WM_NCDESTROY:
1674 COMBO_NCDestroy(lphc);
1675 break;
1677 case WM_CREATE:
1678 retvalue = COMBO_Create(lphc, pWnd, lParam);
1679 goto END;
1681 case WM_PRINTCLIENT:
1682 if (lParam & PRF_ERASEBKGND)
1683 COMBO_EraseBackground(hwnd, lphc, wParam);
1685 /* Fallthrough */
1686 case WM_PAINT:
1687 /* wParam may contain a valid HDC! */
1688 retvalue = COMBO_Paint(lphc, wParam);
1689 goto END;
1690 case WM_ERASEBKGND:
1691 retvalue = COMBO_EraseBackground(hwnd, lphc, wParam);
1692 goto END;
1693 case WM_GETDLGCODE:
1694 retvalue = (LRESULT)(DLGC_WANTARROWS | DLGC_WANTCHARS);
1695 goto END;
1696 case WM_WINDOWPOSCHANGING:
1697 retvalue = COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1698 return retvalue;
1699 case WM_SIZE:
1700 if( lphc->hWndLBox &&
1701 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1702 retvalue = TRUE;
1703 goto END;
1704 case WM_SETFONT:
1705 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1706 retvalue = TRUE;
1707 goto END;
1708 case WM_GETFONT:
1709 retvalue = (LRESULT)lphc->hFont;
1710 goto END;
1711 case WM_SETFOCUS:
1712 if( lphc->wState & CBF_EDIT )
1713 SetFocus( lphc->hWndEdit );
1714 else
1715 COMBO_SetFocus( lphc );
1716 retvalue = TRUE;
1717 goto END;
1718 case WM_KILLFOCUS:
1719 #define hwndFocus ((HWND16)wParam)
1720 if( !hwndFocus ||
1721 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1722 COMBO_KillFocus( lphc );
1723 #undef hwndFocus
1724 retvalue = TRUE;
1725 goto END;
1726 case WM_COMMAND:
1727 retvalue = COMBO_Command( lphc, wParam, (HWND)lParam );
1728 goto END;
1729 case WM_GETTEXT:
1730 retvalue = COMBO_GetText( lphc, (UINT)wParam, (LPSTR)lParam );
1731 goto END;
1732 case WM_SETTEXT:
1733 case WM_GETTEXTLENGTH:
1734 case WM_CLEAR:
1735 case WM_CUT:
1736 case WM_PASTE:
1737 case WM_COPY:
1738 if( lphc->wState & CBF_EDIT )
1740 retvalue = SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1741 goto END;
1743 retvalue = CB_ERR;
1744 goto END;
1745 case WM_DRAWITEM:
1746 case WM_DELETEITEM:
1747 case WM_COMPAREITEM:
1748 case WM_MEASUREITEM:
1749 retvalue = COMBO_ItemOp( lphc, message, wParam, lParam );
1750 goto END;
1751 case WM_ENABLE:
1752 if( lphc->wState & CBF_EDIT )
1753 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1754 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1755 retvalue = TRUE;
1756 goto END;
1757 case WM_SETREDRAW:
1758 if( wParam )
1759 lphc->wState &= ~CBF_NOREDRAW;
1760 else
1761 lphc->wState |= CBF_NOREDRAW;
1763 if( lphc->wState & CBF_EDIT )
1764 SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1765 SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1766 retvalue = 0;
1767 goto END;
1768 case WM_SYSKEYDOWN:
1769 if( KEYDATA_ALT & HIWORD(lParam) )
1770 if( wParam == VK_UP || wParam == VK_DOWN )
1771 COMBO_FlipListbox( lphc, TRUE );
1772 break;
1774 case WM_CHAR:
1775 case WM_KEYDOWN:
1776 if( lphc->wState & CBF_EDIT )
1777 retvalue = SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1778 else
1779 retvalue = SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1780 goto END;
1781 case WM_LBUTTONDOWN:
1782 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf );
1783 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1784 retvalue = TRUE;
1785 goto END;
1786 case WM_LBUTTONUP:
1787 COMBO_LButtonUp( lphc, lParam );
1788 retvalue = TRUE;
1789 goto END;
1790 case WM_MOUSEMOVE:
1791 if( lphc->wState & CBF_CAPTURE )
1792 COMBO_MouseMove( lphc, wParam, lParam );
1793 retvalue = TRUE;
1794 goto END;
1795 /* Combo messages */
1797 case CB_ADDSTRING16:
1798 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1799 case CB_ADDSTRING:
1800 retvalue = SendMessageA( lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1801 goto END;
1802 case CB_INSERTSTRING16:
1803 wParam = (INT)(INT16)wParam;
1804 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1805 case CB_INSERTSTRING:
1806 retvalue = SendMessageA( lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1807 goto END;
1808 case CB_DELETESTRING16:
1809 case CB_DELETESTRING:
1810 retvalue = SendMessageA( lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1811 goto END;
1812 case CB_SELECTSTRING16:
1813 wParam = (INT)(INT16)wParam;
1814 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1815 case CB_SELECTSTRING:
1816 retvalue = COMBO_SelectString( lphc, (INT)wParam, (LPSTR)lParam );
1817 goto END;
1818 case CB_FINDSTRING16:
1819 wParam = (INT)(INT16)wParam;
1820 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1821 case CB_FINDSTRING:
1822 retvalue = SendMessageA( lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1823 goto END;
1824 case CB_FINDSTRINGEXACT16:
1825 wParam = (INT)(INT16)wParam;
1826 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1827 case CB_FINDSTRINGEXACT:
1828 retvalue = SendMessageA( lphc->hWndLBox, LB_FINDSTRINGEXACT,
1829 wParam, lParam );
1830 goto END;
1831 case CB_SETITEMHEIGHT16:
1832 wParam = (INT)(INT16)wParam; /* signed integer */
1833 case CB_SETITEMHEIGHT:
1834 retvalue = COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1835 goto END;
1836 case CB_GETITEMHEIGHT16:
1837 wParam = (INT)(INT16)wParam;
1838 case CB_GETITEMHEIGHT:
1839 if( (INT)wParam >= 0 ) /* listbox item */
1841 retvalue = SendMessageA( lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1842 goto END;
1844 retvalue = CBGetTextAreaHeight(hwnd, lphc);
1845 goto END;
1846 case CB_RESETCONTENT16:
1847 case CB_RESETCONTENT:
1848 SendMessageA( lphc->hWndLBox, LB_RESETCONTENT, 0, 0 );
1849 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
1850 retvalue = TRUE;
1851 goto END;
1852 case CB_INITSTORAGE:
1853 retvalue = SendMessageA( lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
1854 goto END;
1855 case CB_GETHORIZONTALEXTENT:
1856 retvalue = SendMessageA( lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
1857 goto END;
1858 case CB_SETHORIZONTALEXTENT:
1859 retvalue = SendMessageA( lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
1860 goto END;
1861 case CB_GETTOPINDEX:
1862 retvalue = SendMessageA( lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
1863 goto END;
1864 case CB_GETLOCALE:
1865 retvalue = SendMessageA( lphc->hWndLBox, LB_GETLOCALE, 0, 0);
1866 goto END;
1867 case CB_SETLOCALE:
1868 retvalue = SendMessageA( lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
1869 goto END;
1870 case CB_GETDROPPEDWIDTH:
1871 if( lphc->droppedWidth )
1873 retvalue = lphc->droppedWidth;
1874 goto END;
1876 retvalue = lphc->droppedRect.right - lphc->droppedRect.left;
1877 goto END;
1878 case CB_SETDROPPEDWIDTH:
1879 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
1880 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
1881 retvalue = CB_ERR;
1882 goto END;
1883 case CB_GETDROPPEDCONTROLRECT16:
1884 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1885 if( lParam )
1887 RECT r;
1888 CBGetDroppedControlRect( lphc, &r );
1889 CONV_RECT32TO16( &r, (LPRECT16)lParam );
1891 retvalue = CB_OKAY;
1892 goto END;
1893 case CB_GETDROPPEDCONTROLRECT:
1894 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
1895 retvalue = CB_OKAY;
1896 goto END;
1897 case CB_GETDROPPEDSTATE16:
1898 case CB_GETDROPPEDSTATE:
1899 retvalue = (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
1900 goto END;
1901 case CB_DIR16:
1902 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1903 /* fall through */
1904 case CB_DIR:
1905 retvalue = COMBO_Directory( lphc, (UINT)wParam,
1906 (LPSTR)lParam, (message == CB_DIR));
1907 goto END;
1908 case CB_SHOWDROPDOWN16:
1909 case CB_SHOWDROPDOWN:
1910 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1912 if( wParam )
1914 if( !(lphc->wState & CBF_DROPPED) )
1915 CBDropDown( lphc );
1917 else
1918 if( lphc->wState & CBF_DROPPED )
1919 CBRollUp( lphc, FALSE, TRUE );
1921 retvalue = TRUE;
1922 goto END;
1923 case CB_GETCOUNT16:
1924 case CB_GETCOUNT:
1925 retvalue = SendMessageA( lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1926 goto END;
1927 case CB_GETCURSEL16:
1928 case CB_GETCURSEL:
1929 retvalue = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1930 goto END;
1931 case CB_SETCURSEL16:
1932 wParam = (INT)(INT16)wParam;
1933 case CB_SETCURSEL:
1934 lParam = SendMessageA( lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
1935 if( lphc->wState & CBF_SELCHANGE )
1937 /* no LBN_SELCHANGE in this case, update manually */
1938 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1939 lphc->wState &= ~CBF_SELCHANGE;
1941 retvalue = lParam;
1942 goto END;
1943 case CB_GETLBTEXT16:
1944 wParam = (INT)(INT16)wParam;
1945 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1946 case CB_GETLBTEXT:
1947 retvalue = SendMessageA( lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
1948 goto END;
1949 case CB_GETLBTEXTLEN16:
1950 wParam = (INT)(INT16)wParam;
1951 case CB_GETLBTEXTLEN:
1952 retvalue = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
1953 goto END;
1954 case CB_GETITEMDATA16:
1955 wParam = (INT)(INT16)wParam;
1956 case CB_GETITEMDATA:
1957 retvalue = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
1958 goto END;
1959 case CB_SETITEMDATA16:
1960 wParam = (INT)(INT16)wParam;
1961 case CB_SETITEMDATA:
1962 retvalue = SendMessageA( lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
1963 goto END;
1964 case CB_GETEDITSEL16:
1965 wParam = lParam = 0; /* just in case */
1966 case CB_GETEDITSEL:
1967 if( lphc->wState & CBF_EDIT )
1969 INT a, b;
1971 retvalue = SendMessageA( lphc->hWndEdit, EM_GETSEL,
1972 (wParam) ? wParam : (WPARAM)&a,
1973 (lParam) ? lParam : (LPARAM)&b );
1974 goto END;
1976 retvalue = CB_ERR;
1977 goto END;
1978 case CB_SETEDITSEL16:
1979 case CB_SETEDITSEL:
1980 if( lphc->wState & CBF_EDIT )
1982 retvalue = SendMessageA( lphc->hWndEdit, EM_SETSEL,
1983 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
1984 goto END;
1986 retvalue = CB_ERR;
1987 goto END;
1988 case CB_SETEXTENDEDUI16:
1989 case CB_SETEXTENDEDUI:
1990 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
1992 retvalue = CB_ERR;
1993 goto END;
1995 if( wParam )
1996 lphc->wState |= CBF_EUI;
1997 else lphc->wState &= ~CBF_EUI;
1998 retvalue = CB_OKAY;
1999 goto END;
2000 case CB_GETEXTENDEDUI16:
2001 case CB_GETEXTENDEDUI:
2002 retvalue = (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2003 goto END;
2004 case (WM_USER + 0x1B):
2005 WARN(combo, "[%04x]: undocumented msg!\n", hwnd );
2007 retvalue = DefWindowProcA(hwnd, message, wParam, lParam);
2008 goto END;
2010 retvalue = CB_ERR;
2011 END:
2012 WIN_ReleaseWndPtr(pWnd);
2013 return retvalue;