New debug scheme with explicit debug channels declaration.
[wine/testsucceed.git] / controls / combo.c
blobd99d48cd0d7aedce845075960bad49234001b40b
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 DEFAULT_DEBUG_CHANNEL(combo)
25 /* bits in the dwKeyData */
26 #define KEYDATA_ALT 0x2000
27 #define KEYDATA_PREVSTATE 0x4000
30 * Additional combo box definitions
33 #define CB_GETPTR( wnd ) (*(LPHEADCOMBO*)((wnd)->wExtra))
34 #define CB_NOTIFY( lphc, code ) \
35 (SendMessageA( (lphc)->owner, WM_COMMAND, \
36 MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf))
37 #define CB_GETEDITTEXTLENGTH( lphc ) \
38 (SendMessageA( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
41 * Drawing globals
43 static HBITMAP hComboBmp = 0;
44 static UINT CBitHeight, CBitWidth;
47 * Look and feel dependant "constants"
49 #define COMBO_XBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
50 #define COMBO_YBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
51 #define COMBO_EDITBUTTONSPACE() ( (TWEAK_WineLook == WIN31_LOOK) ? 8 : 0 )
52 #define EDIT_CONTROL_PADDING() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 1 )
54 /***********************************************************************
55 * COMBO_Init
57 * Load combo button bitmap.
59 static BOOL COMBO_Init()
61 HDC hDC;
63 if( hComboBmp ) return TRUE;
64 if( (hDC = CreateCompatibleDC(0)) )
66 BOOL bRet = FALSE;
67 if( (hComboBmp = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_COMBO))) )
69 BITMAP bm;
70 HBITMAP hPrevB;
71 RECT r;
73 GetObjectA( hComboBmp, sizeof(bm), &bm );
74 CBitHeight = bm.bmHeight;
75 CBitWidth = bm.bmWidth;
77 TRACE(combo, "combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
79 hPrevB = SelectObject16( hDC, hComboBmp);
80 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
81 InvertRect( hDC, &r );
82 SelectObject( hDC, hPrevB );
83 bRet = TRUE;
85 DeleteDC( hDC );
86 return bRet;
88 return FALSE;
91 /***********************************************************************
92 * COMBO_NCCreate
94 static LRESULT COMBO_NCCreate(WND* wnd, LPARAM lParam)
96 LPHEADCOMBO lphc;
98 if ( wnd && COMBO_Init() &&
99 (lphc = HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO))) )
101 LPCREATESTRUCTA lpcs = (CREATESTRUCTA*)lParam;
103 memset( lphc, 0, sizeof(HEADCOMBO) );
104 *(LPHEADCOMBO*)wnd->wExtra = lphc;
106 /* some braindead apps do try to use scrollbar/border flags */
108 lphc->dwStyle = (lpcs->style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL));
109 wnd->dwStyle &= ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
112 * We also have to remove the client edge style to make sure
113 * we don't end-up with a non client area.
115 wnd->dwExStyle &= ~(WS_EX_CLIENTEDGE);
117 if( !(lpcs->style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
118 lphc->dwStyle |= CBS_HASSTRINGS;
119 if( !(wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) )
120 lphc->wState |= CBF_NOTIFY;
122 TRACE(combo, "[0x%08x], style = %08x\n",
123 (UINT)lphc, lphc->dwStyle );
125 return (LRESULT)(UINT)wnd->hwndSelf;
127 return (LRESULT)FALSE;
130 /***********************************************************************
131 * COMBO_NCDestroy
133 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
136 if( lphc )
138 WND* wnd = lphc->self;
140 TRACE(combo,"[%04x]: freeing storage\n", CB_HWND(lphc));
142 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
143 DestroyWindow( lphc->hWndLBox );
145 HeapFree( GetProcessHeap(), 0, lphc );
146 wnd->wExtra[0] = 0;
148 return 0;
151 /***********************************************************************
152 * CBForceDummyResize
154 * The dummy resize is used for listboxes that have a popup to trigger
155 * a re-arranging of the contents of the combobox and the recalculation
156 * of the size of the "real" control window.
158 static void CBForceDummyResize(
159 HWND hwnd)
161 RECT windowRect;
163 GetWindowRect(hwnd, &windowRect);
165 SetWindowPos( hwnd,
166 (HWND)NULL,
167 0, 0,
168 windowRect.right - windowRect.left,
169 windowRect.bottom- windowRect.top + 1, /* dummy value adjusted later */
170 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
173 /***********************************************************************
174 * CBGetTextAreaHeight
176 * This method will calculate the height of the text area of the
177 * combobox.
178 * The height of the text area is set in two ways.
179 * It can be set explicitely through a combobox message of through a
180 * WM_MEASUREITEM callback.
181 * If this is not the case, the height is set to 13 dialog units.
182 * This height was determined through experimentation.
184 static INT CBGetTextAreaHeight(
185 HWND hwnd,
186 LPHEADCOMBO lphc)
188 INT iTextItemHeight;
190 if( lphc->editHeight ) /* explicitly set height */
192 iTextItemHeight = lphc->editHeight;
194 else
196 TEXTMETRICA tm;
197 HDC hDC = GetDC(hwnd);
198 HFONT hPrevFont = 0;
199 INT baseUnitY;
201 if (lphc->hFont)
202 hPrevFont = SelectObject( hDC, lphc->hFont );
204 GetTextMetricsA(hDC, &tm);
206 baseUnitY = tm.tmHeight;
208 if( hPrevFont )
209 SelectObject( hDC, hPrevFont );
211 ReleaseDC(hwnd, hDC);
213 iTextItemHeight = ((13 * baseUnitY) / 8);
216 * This "formula" calculates the height of the complete control.
217 * To calculate the height of the text area, we have to remove the
218 * borders.
220 iTextItemHeight -= 2*COMBO_YBORDERSIZE();
224 * Check the ownerdraw case if we haven't asked the parent the size
225 * of the item yet.
227 if ( CB_OWNERDRAWN(lphc) &&
228 (lphc->wState & CBF_MEASUREITEM) )
230 MEASUREITEMSTRUCT measureItem;
231 RECT clientRect;
232 INT originalItemHeight = iTextItemHeight;
235 * We use the client rect for the width of the item.
237 GetClientRect(hwnd, &clientRect);
239 lphc->wState &= ~CBF_MEASUREITEM;
242 * Send a first one to measure the size of the text area
244 measureItem.CtlType = ODT_COMBOBOX;
245 measureItem.CtlID = lphc->self->wIDmenu;
246 measureItem.itemID = -1;
247 measureItem.itemWidth = clientRect.right;
248 measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
249 measureItem.itemData = 0;
250 SendMessageA(lphc->owner, WM_MEASUREITEM,
251 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
252 iTextItemHeight = 6 + measureItem.itemHeight;
255 * Send a second one in the case of a fixed ownerdraw list to calculate the
256 * size of the list items. (we basically do this on behalf of the listbox)
258 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
260 measureItem.CtlType = ODT_COMBOBOX;
261 measureItem.CtlID = lphc->self->wIDmenu;
262 measureItem.itemID = 0;
263 measureItem.itemWidth = clientRect.right;
264 measureItem.itemHeight = originalItemHeight;
265 measureItem.itemData = 0;
266 SendMessageA(lphc->owner, WM_MEASUREITEM,
267 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
268 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
272 * Keep the size for the next time
274 lphc->editHeight = iTextItemHeight;
277 return iTextItemHeight;
281 /***********************************************************************
282 * CBCalcPlacement
284 * Set up component coordinates given valid lphc->RectCombo.
286 static void CBCalcPlacement(
287 HWND hwnd,
288 LPHEADCOMBO lphc,
289 LPRECT lprEdit,
290 LPRECT lprButton,
291 LPRECT lprLB)
294 * Again, start with the client rectangle.
296 GetClientRect(hwnd, lprEdit);
299 * Remove the borders
301 InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
304 * Chop off the bottom part to fit with the height of the text area.
306 lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
309 * The button starts the same vertical position as the text area.
311 CopyRect(lprButton, lprEdit);
314 * If the combobox is "simple" there is no button.
316 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
317 lprButton->left = lprButton->right = lprButton->bottom = 0;
318 else
321 * Let's assume the combobox button is the same width as the
322 * scrollbar button.
323 * size the button horizontally and cut-off the text area.
325 lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
326 lprEdit->right = lprButton->left;
330 * In the case of a dropdown, there is an additional spacing between the
331 * text area and the button.
333 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
335 lprEdit->right -= COMBO_EDITBUTTONSPACE();
339 * If we have an edit control, we space it away from the borders slightly.
341 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
343 InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
347 * Adjust the size of the listbox popup.
349 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
352 * Use the client rectangle to initialize the listbox rectangle
354 GetClientRect(hwnd, lprLB);
357 * Then, chop-off the top part.
359 lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
361 else
364 * Make sure the dropped width is as large as the combobox itself.
366 if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
368 lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
371 * In the case of a dropdown, the popup listbox is offset to the right.
372 * so, we want to make sure it's flush with the right side of the
373 * combobox
375 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
376 lprLB->right -= COMBO_EDITBUTTONSPACE();
378 else
379 lprLB->right = lprLB->left + lphc->droppedWidth;
382 TRACE(combo,"\ttext\t= (%i,%i-%i,%i)\n",
383 lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
385 TRACE(combo,"\tbutton\t= (%i,%i-%i,%i)\n",
386 lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
388 TRACE(combo,"\tlbox\t= (%i,%i-%i,%i)\n",
389 lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
392 /***********************************************************************
393 * CBGetDroppedControlRect
395 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
397 CopyRect(lpRect, &lphc->droppedRect);
400 /***********************************************************************
401 * COMBO_WindowPosChanging
403 static LRESULT COMBO_WindowPosChanging(
404 HWND hwnd,
405 LPHEADCOMBO lphc,
406 WINDOWPOS* posChanging)
409 * We need to override the WM_WINDOWPOSCHANGING method to handle all
410 * the non-simple comboboxes. The problem is that those controls are
411 * always the same height. We have to make sure they are not resized
412 * to another value.
414 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
416 posChanging->cy = CBGetTextAreaHeight(hwnd,lphc) +
417 2*COMBO_YBORDERSIZE();
420 return 0;
423 /***********************************************************************
424 * COMBO_Create
426 static LRESULT COMBO_Create( LPHEADCOMBO lphc, WND* wnd, LPARAM lParam)
428 static char clbName[] = "ComboLBox";
429 static char editName[] = "Edit";
431 LPCREATESTRUCTA lpcs = (CREATESTRUCTA*)lParam;
433 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
434 else if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
436 lphc->self = wnd;
437 lphc->owner = lpcs->hwndParent;
440 * The item height and dropped width are not set when the control
441 * is created.
443 lphc->droppedWidth = lphc->editHeight = 0;
446 * The first time we go through, we want to measure the ownerdraw item
448 lphc->wState |= CBF_MEASUREITEM;
450 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
452 if( lphc->owner || !(lpcs->style & WS_VISIBLE) )
454 UINT lbeStyle;
457 * Initialize the dropped rect to the size of the client area of the
458 * control and then, force all the areas of the combobox to be
459 * recalculated.
461 GetClientRect( wnd->hwndSelf, &lphc->droppedRect );
463 CBCalcPlacement(wnd->hwndSelf,
464 lphc,
465 &lphc->textRect,
466 &lphc->buttonRect,
467 &lphc->droppedRect );
470 * Adjust the position of the popup listbox if it's necessary
472 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
474 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
477 * If it's a dropdown, the listbox is offset
479 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
480 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
482 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect);
483 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect.right);
486 /* create listbox popup */
488 lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS) |
489 (lpcs->style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
491 if( lphc->dwStyle & CBS_SORT )
492 lbeStyle |= LBS_SORT;
493 if( lphc->dwStyle & CBS_HASSTRINGS )
494 lbeStyle |= LBS_HASSTRINGS;
495 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
496 lbeStyle |= LBS_NOINTEGRALHEIGHT;
497 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
498 lbeStyle |= LBS_DISABLENOSCROLL;
500 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
501 lbeStyle |= WS_CHILD | WS_VISIBLE;
502 else /* popup listbox */
503 lbeStyle |= WS_POPUP;
505 /* Dropdown ComboLBox is not a child window and we cannot pass
506 * ID_CB_LISTBOX directly because it will be treated as a menu handle.
508 lphc->hWndLBox = CreateWindowExA(0,
509 clbName,
510 NULL,
511 lbeStyle,
512 lphc->droppedRect.left,
513 lphc->droppedRect.top,
514 lphc->droppedRect.right - lphc->droppedRect.left,
515 lphc->droppedRect.bottom - lphc->droppedRect.top,
516 lphc->self->hwndSelf,
517 (lphc->dwStyle & CBS_DROPDOWN)? (HMENU)0 : (HMENU)ID_CB_LISTBOX,
518 lphc->self->hInstance,
519 (LPVOID)lphc );
521 if( lphc->hWndLBox )
523 BOOL bEdit = TRUE;
524 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT;
527 * In Win95 look, the border fo the edit control is
528 * provided by the combobox
530 if (TWEAK_WineLook == WIN31_LOOK)
531 lbeStyle |= WS_BORDER;
533 if( lphc->wState & CBF_EDIT )
535 if( lphc->dwStyle & CBS_OEMCONVERT )
536 lbeStyle |= ES_OEMCONVERT;
537 if( lphc->dwStyle & CBS_AUTOHSCROLL )
538 lbeStyle |= ES_AUTOHSCROLL;
539 if( lphc->dwStyle & CBS_LOWERCASE )
540 lbeStyle |= ES_LOWERCASE;
541 else if( lphc->dwStyle & CBS_UPPERCASE )
542 lbeStyle |= ES_UPPERCASE;
544 lphc->hWndEdit = CreateWindowExA(0,
545 editName,
546 NULL,
547 lbeStyle,
548 lphc->textRect.left, lphc->textRect.top,
549 lphc->textRect.right - lphc->textRect.left,
550 lphc->textRect.bottom - lphc->textRect.top,
551 lphc->self->hwndSelf,
552 (HMENU)ID_CB_EDIT,
553 lphc->self->hInstance,
554 NULL );
556 if( !lphc->hWndEdit )
557 bEdit = FALSE;
560 if( bEdit )
563 * If the combo is a dropdown, we must resize the control to fit only
564 * the text area and button. To do this, we send a dummy resize and the
565 * WM_WINDOWPOSCHANGING message will take care of setting the height for
566 * us.
568 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
570 CBForceDummyResize(CB_HWND(lphc));
573 TRACE(combo,"init done\n");
574 return wnd->hwndSelf;
576 ERR(combo, "edit control failure.\n");
577 } else ERR(combo, "listbox failure.\n");
578 } else ERR(combo, "no owner for visible combo.\n");
580 /* CreateWindow() will send WM_NCDESTROY to cleanup */
582 return -1;
585 /***********************************************************************
586 * CBPaintButton
588 * Paint combo button (normal, pressed, and disabled states).
590 static void CBPaintButton(
591 LPHEADCOMBO lphc,
592 HDC hdc,
593 RECT rectButton)
595 UINT x, y;
596 BOOL bBool;
597 HDC hMemDC;
598 HBRUSH hPrevBrush;
599 COLORREF oldTextColor, oldBkColor;
601 if( lphc->wState & CBF_NOREDRAW )
602 return;
604 hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
607 * Draw the button background
609 PatBlt( hdc,
610 rectButton.left,
611 rectButton.top,
612 rectButton.right-rectButton.left,
613 rectButton.bottom-rectButton.top,
614 PATCOPY );
616 if( (bBool = lphc->wState & CBF_BUTTONDOWN) )
618 DrawEdge( hdc, &rectButton, EDGE_SUNKEN, BF_RECT );
620 else
622 DrawEdge( hdc, &rectButton, EDGE_RAISED, BF_RECT );
626 * Remove the edge of the button from the rectangle
627 * and calculate the position of the bitmap.
629 InflateRect( &rectButton, -2, -2);
631 x = (rectButton.left + rectButton.right - CBitWidth) >> 1;
632 y = (rectButton.top + rectButton.bottom - CBitHeight) >> 1;
635 hMemDC = CreateCompatibleDC( hdc );
636 SelectObject( hMemDC, hComboBmp );
637 oldTextColor = SetTextColor( hdc, GetSysColor(COLOR_BTNFACE) );
638 oldBkColor = SetBkColor( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) :
639 RGB(0,0,0) );
640 BitBlt( hdc, x, y, CBitWidth, CBitHeight, hMemDC, 0, 0, SRCCOPY );
641 SetBkColor( hdc, oldBkColor );
642 SetTextColor( hdc, oldTextColor );
643 DeleteDC( hMemDC );
644 SelectObject( hdc, hPrevBrush );
647 /***********************************************************************
648 * CBPaintText
650 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
652 static void CBPaintText(
653 LPHEADCOMBO lphc,
654 HDC hdc,
655 RECT rectEdit)
657 INT id, size = 0;
658 LPSTR pText = NULL;
660 if( lphc->wState & CBF_NOREDRAW ) return;
662 /* follow Windows combobox that sends a bunch of text
663 * inquiries to its listbox while processing WM_PAINT. */
665 if( (id = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
667 size = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
668 if( (pText = HeapAlloc( GetProcessHeap(), 0, size + 1)) )
670 SendMessageA( lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText );
671 pText[size] = '\0'; /* just in case */
672 } else return;
675 if( lphc->wState & CBF_EDIT )
677 if( CB_HASSTRINGS(lphc) ) SetWindowTextA( lphc->hWndEdit, pText ? pText : "" );
678 if( lphc->wState & CBF_FOCUSED )
679 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
681 else /* paint text field ourselves */
683 HBRUSH hPrevBrush = 0;
684 HDC hDC = hdc;
686 if( !hDC )
688 if ((hDC = GetDC(lphc->self->hwndSelf)))
690 HBRUSH hBrush = SendMessageA( lphc->owner,
691 WM_CTLCOLORLISTBOX,
692 hDC, lphc->self->hwndSelf );
693 hPrevBrush = SelectObject( hDC,
694 (hBrush) ? hBrush : GetStockObject(WHITE_BRUSH) );
697 if( hDC )
699 UINT itemState;
700 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hDC, lphc->hFont) : 0;
703 * Give ourselves some space.
705 InflateRect( &rectEdit, -1, -1 );
707 if ( (lphc->wState & CBF_FOCUSED) &&
708 !(lphc->wState & CBF_DROPPED) )
710 /* highlight */
712 FillRect( hDC, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
713 SetBkColor( hDC, GetSysColor( COLOR_HIGHLIGHT ) );
714 SetTextColor( hDC, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
715 itemState = ODS_SELECTED | ODS_FOCUS;
717 else
718 itemState = 0;
720 if( CB_OWNERDRAWN(lphc) )
722 DRAWITEMSTRUCT dis;
723 HRGN clipRegion;
726 * Save the current clip region.
727 * To retrieve the clip region, we need to create one "dummy"
728 * clip region.
730 clipRegion = CreateRectRgnIndirect(&rectEdit);
732 if (GetClipRgn(hDC, clipRegion)!=1)
734 DeleteObject(clipRegion);
735 clipRegion=(HRGN)NULL;
738 if ( lphc->self->dwStyle & WS_DISABLED )
739 itemState |= ODS_DISABLED;
741 dis.CtlType = ODT_COMBOBOX;
742 dis.CtlID = lphc->self->wIDmenu;
743 dis.hwndItem = lphc->self->hwndSelf;
744 dis.itemAction = ODA_DRAWENTIRE;
745 dis.itemID = id;
746 dis.itemState = itemState;
747 dis.hDC = hDC;
748 dis.rcItem = rectEdit;
749 dis.itemData = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA,
750 (WPARAM)id, 0 );
753 * Clip the DC and have the parent draw the item.
755 IntersectClipRect(hDC,
756 rectEdit.left, rectEdit.top,
757 rectEdit.right, rectEdit.bottom);
759 SendMessageA(lphc->owner, WM_DRAWITEM,
760 lphc->self->wIDmenu, (LPARAM)&dis );
763 * Reset the clipping region.
765 SelectClipRgn(hDC, clipRegion);
767 else
769 ExtTextOutA( hDC,
770 rectEdit.left + 1,
771 rectEdit.top + 1,
772 ETO_OPAQUE | ETO_CLIPPED,
773 &rectEdit,
774 pText ? pText : "" , size, NULL );
776 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
777 DrawFocusRect( hDC, &rectEdit );
780 if( hPrevFont )
781 SelectObject(hDC, hPrevFont );
783 if( !hdc )
785 if( hPrevBrush )
786 SelectObject( hDC, hPrevBrush );
788 ReleaseDC( lphc->self->hwndSelf, hDC );
792 if (pText)
793 HeapFree( GetProcessHeap(), 0, pText );
796 /***********************************************************************
797 * CBPaintBorder
799 static void CBPaintBorder(
800 HWND hwnd,
801 LPHEADCOMBO lphc,
802 HDC hdc)
804 RECT clientRect;
806 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
808 GetClientRect(hwnd, &clientRect);
810 else
812 CopyRect(&clientRect, &lphc->textRect);
814 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
815 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
818 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
821 /***********************************************************************
822 * COMBO_EraseBackground
824 static LRESULT COMBO_EraseBackground(
825 HWND hwnd,
826 LPHEADCOMBO lphc,
827 HDC hParamDC)
829 HBRUSH hBkgBrush;
830 RECT clientRect;
831 HDC hDC;
833 hDC = (hParamDC) ? hParamDC
834 : GetDC(hwnd);
837 * Calculate the area that we want to erase.
839 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
841 GetClientRect(hwnd, &clientRect);
843 else
845 CopyRect(&clientRect, &lphc->textRect);
847 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
850 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
851 hDC, hwnd);
853 if( !hBkgBrush )
854 hBkgBrush = GetStockObject(WHITE_BRUSH);
856 FillRect(hDC, &clientRect, hBkgBrush);
858 if (!hParamDC)
859 ReleaseDC(hwnd, hDC);
861 return TRUE;
864 /***********************************************************************
865 * COMBO_Paint
867 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
869 PAINTSTRUCT ps;
870 HDC hDC;
872 hDC = (hParamDC) ? hParamDC
873 : BeginPaint( lphc->self->hwndSelf, &ps);
876 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
878 HBRUSH hPrevBrush, hBkgBrush;
880 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
881 hDC, lphc->self->hwndSelf );
883 if( !hBkgBrush )
884 hBkgBrush = GetStockObject(WHITE_BRUSH);
886 hPrevBrush = SelectObject( hDC, hBkgBrush );
889 * In non 3.1 look, there is a sunken border on the combobox
891 if (TWEAK_WineLook != WIN31_LOOK)
893 CBPaintBorder(CB_HWND(lphc), lphc, hDC);
896 if( !IsRectEmpty(&lphc->buttonRect) )
898 CBPaintButton(lphc, hDC, lphc->buttonRect);
901 if( !(lphc->wState & CBF_EDIT) )
904 * The text area has a border only in Win 3.1 look.
906 if (TWEAK_WineLook == WIN31_LOOK)
908 HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
910 Rectangle( hDC,
911 lphc->textRect.left, lphc->textRect.top,
912 lphc->textRect.right - 1, lphc->textRect.bottom - 1);
914 SelectObject( hDC, hPrevPen );
917 CBPaintText( lphc, hDC, lphc->textRect);
920 if( hPrevBrush )
921 SelectObject( hDC, hPrevBrush );
924 if( !hParamDC )
925 EndPaint(lphc->self->hwndSelf, &ps);
927 return 0;
930 /***********************************************************************
931 * CBUpdateLBox
933 * Select listbox entry according to the contents of the edit control.
935 static INT CBUpdateLBox( LPHEADCOMBO lphc )
937 INT length, idx, ret;
938 LPSTR pText = NULL;
940 idx = ret = LB_ERR;
941 length = CB_GETEDITTEXTLENGTH( lphc );
943 if( length > 0 )
944 pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1);
946 TRACE(combo,"\t edit text length %i\n", length );
948 if( pText )
950 if( length ) GetWindowTextA( lphc->hWndEdit, pText, length + 1);
951 else pText[0] = '\0';
952 idx = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
953 (WPARAM)(-1), (LPARAM)pText );
954 if( idx == LB_ERR ) idx = 0; /* select first item */
955 else ret = idx;
956 HeapFree( GetProcessHeap(), 0, pText );
959 /* select entry */
961 SendMessageA( lphc->hWndLBox, LB_SETCURSEL, (WPARAM)idx, 0 );
963 if( idx >= 0 )
965 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)idx, 0 );
966 /* probably superfluous but Windows sends this too */
967 SendMessageA( lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)idx, 0 );
969 return ret;
972 /***********************************************************************
973 * CBUpdateEdit
975 * Copy a listbox entry to the edit control.
977 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
979 INT length;
980 LPSTR pText = NULL;
982 TRACE(combo,"\t %i\n", index );
984 if( index == -1 )
986 length = CB_GETEDITTEXTLENGTH( lphc );
987 if( length )
989 if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
991 GetWindowTextA( lphc->hWndEdit, pText, length + 1 );
992 index = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
993 (WPARAM)(-1), (LPARAM)pText );
994 HeapFree( GetProcessHeap(), 0, pText );
999 if( index >= 0 ) /* got an entry */
1001 length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1002 if( length )
1004 if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
1006 SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1007 (WPARAM)index, (LPARAM)pText );
1008 SendMessageA( lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)pText );
1009 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1010 HeapFree( GetProcessHeap(), 0, pText );
1016 /***********************************************************************
1017 * CBDropDown
1019 * Show listbox popup.
1021 static void CBDropDown( LPHEADCOMBO lphc )
1023 INT index;
1024 RECT rect;
1026 TRACE(combo,"[%04x]: drop down\n", CB_HWND(lphc));
1028 CB_NOTIFY( lphc, CBN_DROPDOWN );
1030 /* set selection */
1032 lphc->wState |= CBF_DROPPED;
1033 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1035 index = CBUpdateLBox( lphc );
1036 if( !(lphc->wState & CBF_CAPTURE) ) CBUpdateEdit( lphc, index );
1038 else
1040 index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1041 if( index == LB_ERR ) index = 0;
1042 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)index, 0 );
1043 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1046 /* now set popup position */
1047 GetWindowRect( lphc->self->hwndSelf, &rect );
1051 * If it's a dropdown, the listbox is offset
1053 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1054 rect.left += COMBO_EDITBUTTONSPACE();
1056 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1057 rect.right - rect.left, rect.bottom - rect.top,
1058 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOREDRAW);
1060 if( !(lphc->wState & CBF_NOREDRAW) )
1061 RedrawWindow( lphc->self->hwndSelf, NULL, 0, RDW_INVALIDATE |
1062 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1064 ShowWindow( lphc->hWndLBox, SW_SHOWNA );
1067 /***********************************************************************
1068 * CBRollUp
1070 * Hide listbox popup.
1072 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1074 HWND hWnd = lphc->self->hwndSelf;
1076 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1078 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1081 TRACE(combo,"[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
1083 /* always send WM_LBUTTONUP? */
1084 SendMessageA( lphc->hWndLBox, WM_LBUTTONUP, 0, (LPARAM)(-1) );
1086 if( lphc->wState & CBF_DROPPED )
1088 RECT rect;
1090 lphc->wState &= ~CBF_DROPPED;
1091 ShowWindow( lphc->hWndLBox, SW_HIDE );
1093 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1095 INT index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1096 CBUpdateEdit( lphc, index );
1097 rect = lphc->buttonRect;
1099 else
1101 if( bButton )
1103 UnionRect( &rect,
1104 &lphc->buttonRect,
1105 &lphc->textRect);
1107 else
1108 rect = lphc->textRect;
1110 bButton = TRUE;
1113 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1114 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1115 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1116 CB_NOTIFY( lphc, CBN_CLOSEUP );
1121 /***********************************************************************
1122 * COMBO_FlipListbox
1124 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1126 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL bRedrawButton )
1128 if( lphc->wState & CBF_DROPPED )
1130 CBRollUp( lphc, TRUE, bRedrawButton );
1131 return FALSE;
1134 CBDropDown( lphc );
1135 return TRUE;
1138 /***********************************************************************
1139 * COMBO_GetLBWindow
1141 * Edit control helper.
1143 HWND COMBO_GetLBWindow( WND* pWnd )
1145 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1146 if( lphc ) return lphc->hWndLBox;
1147 return 0;
1151 /***********************************************************************
1152 * CBRepaintButton
1154 static void CBRepaintButton( LPHEADCOMBO lphc )
1156 InvalidateRect(CB_HWND(lphc), &lphc->buttonRect, TRUE);
1157 UpdateWindow(CB_HWND(lphc));
1160 /***********************************************************************
1161 * COMBO_SetFocus
1163 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1165 if( !(lphc->wState & CBF_FOCUSED) )
1167 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1168 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1170 if( lphc->wState & CBF_EDIT )
1171 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1172 lphc->wState |= CBF_FOCUSED;
1173 if( !(lphc->wState & CBF_EDIT) )
1175 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1178 CB_NOTIFY( lphc, CBN_SETFOCUS );
1182 /***********************************************************************
1183 * COMBO_KillFocus
1185 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1187 HWND hWnd = lphc->self->hwndSelf;
1189 if( lphc->wState & CBF_FOCUSED )
1191 SendMessageA( hWnd, WM_LBUTTONUP, 0, (LPARAM)(-1) );
1193 CBRollUp( lphc, FALSE, TRUE );
1194 if( IsWindow( hWnd ) )
1196 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1197 SendMessageA( lphc->hWndLBox, LB_CARETOFF, 0, 0 );
1199 lphc->wState &= ~CBF_FOCUSED;
1201 /* redraw text */
1202 if( lphc->wState & CBF_EDIT )
1203 SendMessageA( lphc->hWndEdit, EM_SETSEL, (WPARAM)(-1), 0 );
1204 else
1206 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1209 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1214 /***********************************************************************
1215 * COMBO_Command
1217 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1219 if( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1221 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1223 switch( HIWORD(wParam) >> 8 )
1225 case (EN_SETFOCUS >> 8):
1227 TRACE(combo,"[%04x]: edit [%04x] got focus\n",
1228 CB_HWND(lphc), lphc->hWndEdit );
1230 if( !(lphc->wState & CBF_FOCUSED) ) COMBO_SetFocus( lphc );
1231 break;
1233 case (EN_KILLFOCUS >> 8):
1235 TRACE(combo,"[%04x]: edit [%04x] lost focus\n",
1236 CB_HWND(lphc), lphc->hWndEdit );
1238 /* NOTE: it seems that Windows' edit control sends an
1239 * undocumented message WM_USER + 0x1B instead of this
1240 * notification (only when it happens to be a part of
1241 * the combo). ?? - AK.
1244 COMBO_KillFocus( lphc );
1245 break;
1248 case (EN_CHANGE >> 8):
1249 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1250 CBUpdateLBox( lphc );
1251 break;
1253 case (EN_UPDATE >> 8):
1254 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1255 break;
1257 case (EN_ERRSPACE >> 8):
1258 CB_NOTIFY( lphc, CBN_ERRSPACE );
1261 else if( lphc->hWndLBox == hWnd )
1263 switch( HIWORD(wParam) )
1265 case LBN_ERRSPACE:
1266 CB_NOTIFY( lphc, CBN_ERRSPACE );
1267 break;
1269 case LBN_DBLCLK:
1270 CB_NOTIFY( lphc, CBN_DBLCLK );
1271 break;
1273 case LBN_SELCHANGE:
1274 case LBN_SELCANCEL:
1276 TRACE(combo,"[%04x]: lbox selection change [%04x]\n",
1277 CB_HWND(lphc), lphc->wState );
1279 /* do not roll up if selection is being tracked
1280 * by arrowkeys in the dropdown listbox */
1282 if( (lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP) )
1283 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1284 else lphc->wState &= ~CBF_NOROLLUP;
1286 CB_NOTIFY( lphc, CBN_SELCHANGE );
1287 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1288 /* fall through */
1290 case LBN_SETFOCUS:
1291 case LBN_KILLFOCUS:
1292 /* nothing to do here since ComboLBox always resets the focus to its
1293 * combo/edit counterpart */
1294 break;
1297 return 0;
1300 /***********************************************************************
1301 * COMBO_ItemOp
1303 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1305 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg,
1306 WPARAM wParam, LPARAM lParam )
1308 HWND hWnd = lphc->self->hwndSelf;
1310 TRACE(combo,"[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
1312 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
1314 /* two first items are the same in all 4 structs */
1315 lpIS->CtlType = ODT_COMBOBOX;
1316 lpIS->CtlID = lphc->self->wIDmenu;
1318 switch( msg ) /* patch window handle */
1320 case WM_DELETEITEM:
1321 lpIS->hwndItem = hWnd;
1322 #undef lpIS
1323 break;
1324 case WM_DRAWITEM:
1325 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
1326 lpIS->hwndItem = hWnd;
1327 #undef lpIS
1328 break;
1329 case WM_COMPAREITEM:
1330 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
1331 lpIS->hwndItem = hWnd;
1332 #undef lpIS
1333 break;
1336 return SendMessageA( lphc->owner, msg, lphc->self->wIDmenu, lParam );
1339 /***********************************************************************
1340 * COMBO_GetText
1342 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT N, LPSTR lpText)
1344 if( lphc->wState & CBF_EDIT )
1345 return SendMessageA( lphc->hWndEdit, WM_GETTEXT,
1346 (WPARAM)N, (LPARAM)lpText );
1348 /* get it from the listbox */
1350 if( lphc->hWndLBox )
1352 INT idx = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1353 if( idx != LB_ERR )
1355 LPSTR lpBuffer;
1356 INT length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN,
1357 (WPARAM)idx, 0 );
1359 /* 'length' is without the terminating character */
1360 if( length >= N )
1361 lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 );
1362 else
1363 lpBuffer = lpText;
1365 if( lpBuffer )
1367 INT n = SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1368 (WPARAM)idx, (LPARAM)lpBuffer );
1370 /* truncate if buffer is too short */
1372 if( length >= N )
1374 if (N && lpText) {
1375 if( n != LB_ERR ) memcpy( lpText, lpBuffer, (N>n) ? n+1 : N-1 );
1376 lpText[N - 1] = '\0';
1378 HeapFree( GetProcessHeap(), 0, lpBuffer );
1380 return (LRESULT)n;
1384 return 0;
1388 /***********************************************************************
1389 * CBResetPos
1391 * This function sets window positions according to the updated
1392 * component placement struct.
1394 static void CBResetPos(
1395 LPHEADCOMBO lphc,
1396 LPRECT rectEdit,
1397 LPRECT rectLB,
1398 BOOL bRedraw)
1400 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1402 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1403 * sizing messages */
1405 if( lphc->wState & CBF_EDIT )
1406 SetWindowPos( lphc->hWndEdit, 0,
1407 rectEdit->left, rectEdit->top,
1408 rectEdit->right - rectEdit->left,
1409 rectEdit->bottom - rectEdit->top,
1410 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1412 SetWindowPos( lphc->hWndLBox, 0,
1413 rectLB->left, rectLB->top,
1414 rectLB->right - rectLB->left,
1415 rectLB->bottom - rectLB->top,
1416 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1418 if( bDrop )
1420 if( lphc->wState & CBF_DROPPED )
1422 lphc->wState &= ~CBF_DROPPED;
1423 ShowWindow( lphc->hWndLBox, SW_HIDE );
1426 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1427 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
1428 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1433 /***********************************************************************
1434 * COMBO_Size
1436 static void COMBO_Size( LPHEADCOMBO lphc )
1438 CBCalcPlacement(lphc->self->hwndSelf,
1439 lphc,
1440 &lphc->textRect,
1441 &lphc->buttonRect,
1442 &lphc->droppedRect);
1444 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1448 /***********************************************************************
1449 * COMBO_Font
1451 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1454 * Set the font
1456 lphc->hFont = hFont;
1459 * Propagate to owned windows.
1461 if( lphc->wState & CBF_EDIT )
1462 SendMessageA( lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw );
1463 SendMessageA( lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw );
1466 * Redo the layout of the control.
1468 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1470 CBCalcPlacement(lphc->self->hwndSelf,
1471 lphc,
1472 &lphc->textRect,
1473 &lphc->buttonRect,
1474 &lphc->droppedRect);
1476 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1478 else
1480 CBForceDummyResize(CB_HWND(lphc));
1485 /***********************************************************************
1486 * COMBO_SetItemHeight
1488 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1490 LRESULT lRet = CB_ERR;
1492 if( index == -1 ) /* set text field height */
1494 if( height < 32768 )
1496 lphc->editHeight = height;
1499 * Redo the layout of the control.
1501 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1503 CBCalcPlacement(lphc->self->hwndSelf,
1504 lphc,
1505 &lphc->textRect,
1506 &lphc->buttonRect,
1507 &lphc->droppedRect);
1509 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1511 else
1513 CBForceDummyResize(CB_HWND(lphc));
1516 lRet = height;
1519 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1520 lRet = SendMessageA( lphc->hWndLBox, LB_SETITEMHEIGHT,
1521 (WPARAM)index, (LPARAM)height );
1522 return lRet;
1525 /***********************************************************************
1526 * COMBO_SelectString
1528 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPCSTR pText )
1530 INT index = SendMessageA( lphc->hWndLBox, LB_SELECTSTRING,
1531 (WPARAM)start, (LPARAM)pText );
1532 if( index >= 0 )
1534 if( lphc->wState & CBF_EDIT )
1535 CBUpdateEdit( lphc, index );
1536 else
1538 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1541 return (LRESULT)index;
1544 /***********************************************************************
1545 * COMBO_LButtonDown
1547 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1549 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
1550 BOOL bButton;
1551 HWND hWnd = lphc->self->hwndSelf;
1553 bButton = PtInRect(&lphc->buttonRect, pt);
1555 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1556 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1558 lphc->wState |= CBF_BUTTONDOWN;
1559 if( lphc->wState & CBF_DROPPED )
1561 /* got a click to cancel selection */
1563 lphc->wState &= ~CBF_BUTTONDOWN;
1564 CBRollUp( lphc, TRUE, FALSE );
1565 if( !IsWindow( hWnd ) ) return;
1567 if( lphc->wState & CBF_CAPTURE )
1569 lphc->wState &= ~CBF_CAPTURE;
1570 ReleaseCapture();
1573 else
1575 /* drop down the listbox and start tracking */
1577 lphc->wState |= CBF_CAPTURE;
1578 CBDropDown( lphc );
1579 SetCapture( hWnd );
1581 if( bButton ) CBRepaintButton( lphc );
1585 /***********************************************************************
1586 * COMBO_LButtonUp
1588 * Release capture and stop tracking if needed.
1590 static void COMBO_LButtonUp( LPHEADCOMBO lphc, LPARAM lParam )
1592 if( lphc->wState & CBF_CAPTURE )
1594 lphc->wState &= ~CBF_CAPTURE;
1595 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1597 INT index = CBUpdateLBox( lphc );
1598 CBUpdateEdit( lphc, index );
1600 ReleaseCapture();
1603 if( lphc->wState & CBF_BUTTONDOWN )
1605 lphc->wState &= ~CBF_BUTTONDOWN;
1606 CBRepaintButton( lphc );
1610 /***********************************************************************
1611 * COMBO_MouseMove
1613 * Two things to do - track combo button and release capture when
1614 * pointer goes into the listbox.
1616 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1618 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
1619 RECT lbRect;
1621 if( lphc->wState & CBF_BUTTONDOWN )
1623 BOOL bButton;
1625 bButton = PtInRect(&lphc->buttonRect, pt);
1627 if( !bButton )
1629 lphc->wState &= ~CBF_BUTTONDOWN;
1630 CBRepaintButton( lphc );
1634 GetClientRect( lphc->hWndLBox, &lbRect );
1635 MapWindowPoints( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 );
1636 if( PtInRect(&lbRect, pt) )
1638 lphc->wState &= ~CBF_CAPTURE;
1639 ReleaseCapture();
1640 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc );
1642 /* hand over pointer tracking */
1643 SendMessageA( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam );
1648 /***********************************************************************
1649 * ComboWndProc
1651 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1653 LRESULT WINAPI ComboWndProc( HWND hwnd, UINT message,
1654 WPARAM wParam, LPARAM lParam )
1656 LRESULT retvalue;
1657 WND* pWnd = WIN_FindWndPtr(hwnd);
1659 if( pWnd )
1661 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1663 TRACE(combo, "[%04x]: msg %s wp %08x lp %08lx\n",
1664 pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
1666 if( lphc || message == WM_NCCREATE )
1667 switch(message)
1670 /* System messages */
1672 case WM_NCCREATE:
1673 retvalue = COMBO_NCCreate(pWnd, lParam);
1674 goto END;
1675 case WM_NCDESTROY:
1676 COMBO_NCDestroy(lphc);
1677 break;
1679 case WM_CREATE:
1680 retvalue = COMBO_Create(lphc, pWnd, lParam);
1681 goto END;
1683 case WM_PRINTCLIENT:
1684 if (lParam & PRF_ERASEBKGND)
1685 COMBO_EraseBackground(hwnd, lphc, wParam);
1687 /* Fallthrough */
1688 case WM_PAINT:
1689 /* wParam may contain a valid HDC! */
1690 retvalue = COMBO_Paint(lphc, wParam);
1691 goto END;
1692 case WM_ERASEBKGND:
1693 retvalue = COMBO_EraseBackground(hwnd, lphc, wParam);
1694 goto END;
1695 case WM_GETDLGCODE:
1696 retvalue = (LRESULT)(DLGC_WANTARROWS | DLGC_WANTCHARS);
1697 goto END;
1698 case WM_WINDOWPOSCHANGING:
1699 retvalue = COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1700 return retvalue;
1701 case WM_SIZE:
1702 if( lphc->hWndLBox &&
1703 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1704 retvalue = TRUE;
1705 goto END;
1706 case WM_SETFONT:
1707 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1708 retvalue = TRUE;
1709 goto END;
1710 case WM_GETFONT:
1711 retvalue = (LRESULT)lphc->hFont;
1712 goto END;
1713 case WM_SETFOCUS:
1714 if( lphc->wState & CBF_EDIT )
1715 SetFocus( lphc->hWndEdit );
1716 else
1717 COMBO_SetFocus( lphc );
1718 retvalue = TRUE;
1719 goto END;
1720 case WM_KILLFOCUS:
1721 #define hwndFocus ((HWND16)wParam)
1722 if( !hwndFocus ||
1723 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1724 COMBO_KillFocus( lphc );
1725 #undef hwndFocus
1726 retvalue = TRUE;
1727 goto END;
1728 case WM_COMMAND:
1729 retvalue = COMBO_Command( lphc, wParam, (HWND)lParam );
1730 goto END;
1731 case WM_GETTEXT:
1732 retvalue = COMBO_GetText( lphc, (UINT)wParam, (LPSTR)lParam );
1733 goto END;
1734 case WM_SETTEXT:
1735 case WM_GETTEXTLENGTH:
1736 case WM_CLEAR:
1737 case WM_CUT:
1738 case WM_PASTE:
1739 case WM_COPY:
1740 if( lphc->wState & CBF_EDIT )
1742 retvalue = SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1743 goto END;
1745 retvalue = CB_ERR;
1746 goto END;
1747 case WM_DRAWITEM:
1748 case WM_DELETEITEM:
1749 case WM_COMPAREITEM:
1750 case WM_MEASUREITEM:
1751 retvalue = COMBO_ItemOp( lphc, message, wParam, lParam );
1752 goto END;
1753 case WM_ENABLE:
1754 if( lphc->wState & CBF_EDIT )
1755 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1756 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1757 retvalue = TRUE;
1758 goto END;
1759 case WM_SETREDRAW:
1760 if( wParam )
1761 lphc->wState &= ~CBF_NOREDRAW;
1762 else
1763 lphc->wState |= CBF_NOREDRAW;
1765 if( lphc->wState & CBF_EDIT )
1766 SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1767 SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1768 retvalue = 0;
1769 goto END;
1770 case WM_SYSKEYDOWN:
1771 if( KEYDATA_ALT & HIWORD(lParam) )
1772 if( wParam == VK_UP || wParam == VK_DOWN )
1773 COMBO_FlipListbox( lphc, TRUE );
1774 break;
1776 case WM_CHAR:
1777 case WM_KEYDOWN:
1778 if( lphc->wState & CBF_EDIT )
1779 retvalue = SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1780 else
1781 retvalue = SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1782 goto END;
1783 case WM_LBUTTONDOWN:
1784 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf );
1785 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1786 retvalue = TRUE;
1787 goto END;
1788 case WM_LBUTTONUP:
1789 COMBO_LButtonUp( lphc, lParam );
1790 retvalue = TRUE;
1791 goto END;
1792 case WM_MOUSEMOVE:
1793 if( lphc->wState & CBF_CAPTURE )
1794 COMBO_MouseMove( lphc, wParam, lParam );
1795 retvalue = TRUE;
1796 goto END;
1797 /* Combo messages */
1799 case CB_ADDSTRING16:
1800 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1801 case CB_ADDSTRING:
1802 retvalue = SendMessageA( lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1803 goto END;
1804 case CB_INSERTSTRING16:
1805 wParam = (INT)(INT16)wParam;
1806 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1807 case CB_INSERTSTRING:
1808 retvalue = SendMessageA( lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1809 goto END;
1810 case CB_DELETESTRING16:
1811 case CB_DELETESTRING:
1812 retvalue = SendMessageA( lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1813 goto END;
1814 case CB_SELECTSTRING16:
1815 wParam = (INT)(INT16)wParam;
1816 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1817 case CB_SELECTSTRING:
1818 retvalue = COMBO_SelectString( lphc, (INT)wParam, (LPSTR)lParam );
1819 goto END;
1820 case CB_FINDSTRING16:
1821 wParam = (INT)(INT16)wParam;
1822 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1823 case CB_FINDSTRING:
1824 retvalue = SendMessageA( lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1825 goto END;
1826 case CB_FINDSTRINGEXACT16:
1827 wParam = (INT)(INT16)wParam;
1828 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1829 case CB_FINDSTRINGEXACT:
1830 retvalue = SendMessageA( lphc->hWndLBox, LB_FINDSTRINGEXACT,
1831 wParam, lParam );
1832 goto END;
1833 case CB_SETITEMHEIGHT16:
1834 wParam = (INT)(INT16)wParam; /* signed integer */
1835 case CB_SETITEMHEIGHT:
1836 retvalue = COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1837 goto END;
1838 case CB_GETITEMHEIGHT16:
1839 wParam = (INT)(INT16)wParam;
1840 case CB_GETITEMHEIGHT:
1841 if( (INT)wParam >= 0 ) /* listbox item */
1843 retvalue = SendMessageA( lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1844 goto END;
1846 retvalue = CBGetTextAreaHeight(hwnd, lphc);
1847 goto END;
1848 case CB_RESETCONTENT16:
1849 case CB_RESETCONTENT:
1850 SendMessageA( lphc->hWndLBox, LB_RESETCONTENT, 0, 0 );
1851 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
1852 retvalue = TRUE;
1853 goto END;
1854 case CB_INITSTORAGE:
1855 retvalue = SendMessageA( lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
1856 goto END;
1857 case CB_GETHORIZONTALEXTENT:
1858 retvalue = SendMessageA( lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
1859 goto END;
1860 case CB_SETHORIZONTALEXTENT:
1861 retvalue = SendMessageA( lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
1862 goto END;
1863 case CB_GETTOPINDEX:
1864 retvalue = SendMessageA( lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
1865 goto END;
1866 case CB_GETLOCALE:
1867 retvalue = SendMessageA( lphc->hWndLBox, LB_GETLOCALE, 0, 0);
1868 goto END;
1869 case CB_SETLOCALE:
1870 retvalue = SendMessageA( lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
1871 goto END;
1872 case CB_GETDROPPEDWIDTH:
1873 if( lphc->droppedWidth )
1875 retvalue = lphc->droppedWidth;
1876 goto END;
1878 retvalue = lphc->droppedRect.right - lphc->droppedRect.left;
1879 goto END;
1880 case CB_SETDROPPEDWIDTH:
1881 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
1882 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
1883 retvalue = CB_ERR;
1884 goto END;
1885 case CB_GETDROPPEDCONTROLRECT16:
1886 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1887 if( lParam )
1889 RECT r;
1890 CBGetDroppedControlRect( lphc, &r );
1891 CONV_RECT32TO16( &r, (LPRECT16)lParam );
1893 retvalue = CB_OKAY;
1894 goto END;
1895 case CB_GETDROPPEDCONTROLRECT:
1896 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
1897 retvalue = CB_OKAY;
1898 goto END;
1899 case CB_GETDROPPEDSTATE16:
1900 case CB_GETDROPPEDSTATE:
1901 retvalue = (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
1902 goto END;
1903 case CB_DIR16:
1904 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1905 /* fall through */
1906 case CB_DIR:
1907 retvalue = COMBO_Directory( lphc, (UINT)wParam,
1908 (LPSTR)lParam, (message == CB_DIR));
1909 goto END;
1910 case CB_SHOWDROPDOWN16:
1911 case CB_SHOWDROPDOWN:
1912 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1914 if( wParam )
1916 if( !(lphc->wState & CBF_DROPPED) )
1917 CBDropDown( lphc );
1919 else
1920 if( lphc->wState & CBF_DROPPED )
1921 CBRollUp( lphc, FALSE, TRUE );
1923 retvalue = TRUE;
1924 goto END;
1925 case CB_GETCOUNT16:
1926 case CB_GETCOUNT:
1927 retvalue = SendMessageA( lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1928 goto END;
1929 case CB_GETCURSEL16:
1930 case CB_GETCURSEL:
1931 retvalue = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1932 goto END;
1933 case CB_SETCURSEL16:
1934 wParam = (INT)(INT16)wParam;
1935 case CB_SETCURSEL:
1936 lParam = SendMessageA( lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
1937 if( lphc->wState & CBF_SELCHANGE )
1939 /* no LBN_SELCHANGE in this case, update manually */
1940 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1941 lphc->wState &= ~CBF_SELCHANGE;
1943 retvalue = lParam;
1944 goto END;
1945 case CB_GETLBTEXT16:
1946 wParam = (INT)(INT16)wParam;
1947 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1948 case CB_GETLBTEXT:
1949 retvalue = SendMessageA( lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
1950 goto END;
1951 case CB_GETLBTEXTLEN16:
1952 wParam = (INT)(INT16)wParam;
1953 case CB_GETLBTEXTLEN:
1954 retvalue = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
1955 goto END;
1956 case CB_GETITEMDATA16:
1957 wParam = (INT)(INT16)wParam;
1958 case CB_GETITEMDATA:
1959 retvalue = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
1960 goto END;
1961 case CB_SETITEMDATA16:
1962 wParam = (INT)(INT16)wParam;
1963 case CB_SETITEMDATA:
1964 retvalue = SendMessageA( lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
1965 goto END;
1966 case CB_GETEDITSEL16:
1967 wParam = lParam = 0; /* just in case */
1968 case CB_GETEDITSEL:
1969 if( lphc->wState & CBF_EDIT )
1971 INT a, b;
1973 retvalue = SendMessageA( lphc->hWndEdit, EM_GETSEL,
1974 (wParam) ? wParam : (WPARAM)&a,
1975 (lParam) ? lParam : (LPARAM)&b );
1976 goto END;
1978 retvalue = CB_ERR;
1979 goto END;
1980 case CB_SETEDITSEL16:
1981 case CB_SETEDITSEL:
1982 if( lphc->wState & CBF_EDIT )
1984 retvalue = SendMessageA( lphc->hWndEdit, EM_SETSEL,
1985 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
1986 goto END;
1988 retvalue = CB_ERR;
1989 goto END;
1990 case CB_SETEXTENDEDUI16:
1991 case CB_SETEXTENDEDUI:
1992 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
1994 retvalue = CB_ERR;
1995 goto END;
1997 if( wParam )
1998 lphc->wState |= CBF_EUI;
1999 else lphc->wState &= ~CBF_EUI;
2000 retvalue = CB_OKAY;
2001 goto END;
2002 case CB_GETEXTENDEDUI16:
2003 case CB_GETEXTENDEDUI:
2004 retvalue = (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2005 goto END;
2006 case (WM_USER + 0x1B):
2007 WARN(combo, "[%04x]: undocumented msg!\n", hwnd );
2009 retvalue = DefWindowProcA(hwnd, message, wParam, lParam);
2010 goto END;
2012 retvalue = CB_ERR;
2013 END:
2014 WIN_ReleaseWndPtr(pWnd);
2015 return retvalue;