Authors: Chris Morgan <cmorgan@wpi.edu>, James Abbatiello <abbejy@wpi.edu>
[wine/multimedia.git] / controls / combo.c
blob9d8ba698ac1a2f172a7c85ca7667e47765cd1be1
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 LPHEADCOMBO lphc)
161 RECT windowRect;
163 GetWindowRect(CB_HWND(lphc), &windowRect);
166 * We have to be careful, resizing a combobox also has the meaning that the
167 * dropped rect will be resized. In this case, we want to trigger a resize
168 * to recalculate layout but we don't want to change the dropped rectangle
169 * So, we add the size of the dropped rectangle to the size of the control.
170 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
171 * message.
173 SetWindowPos( CB_HWND(lphc),
174 (HWND)NULL,
175 0, 0,
176 windowRect.right - windowRect.left,
177 windowRect.bottom - windowRect.top +
178 lphc->droppedRect.bottom - lphc->droppedRect.top,
179 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
182 /***********************************************************************
183 * CBGetTextAreaHeight
185 * This method will calculate the height of the text area of the
186 * combobox.
187 * The height of the text area is set in two ways.
188 * It can be set explicitely through a combobox message of through a
189 * WM_MEASUREITEM callback.
190 * If this is not the case, the height is set to 13 dialog units.
191 * This height was determined through experimentation.
193 static INT CBGetTextAreaHeight(
194 HWND hwnd,
195 LPHEADCOMBO lphc)
197 INT iTextItemHeight;
199 if( lphc->editHeight ) /* explicitly set height */
201 iTextItemHeight = lphc->editHeight;
203 else
205 TEXTMETRICA tm;
206 HDC hDC = GetDC(hwnd);
207 HFONT hPrevFont = 0;
208 INT baseUnitY;
210 if (lphc->hFont)
211 hPrevFont = SelectObject( hDC, lphc->hFont );
213 GetTextMetricsA(hDC, &tm);
215 baseUnitY = tm.tmHeight;
217 if( hPrevFont )
218 SelectObject( hDC, hPrevFont );
220 ReleaseDC(hwnd, hDC);
222 iTextItemHeight = ((13 * baseUnitY) / 8);
225 * This "formula" calculates the height of the complete control.
226 * To calculate the height of the text area, we have to remove the
227 * borders.
229 iTextItemHeight -= 2*COMBO_YBORDERSIZE();
233 * Check the ownerdraw case if we haven't asked the parent the size
234 * of the item yet.
236 if ( CB_OWNERDRAWN(lphc) &&
237 (lphc->wState & CBF_MEASUREITEM) )
239 MEASUREITEMSTRUCT measureItem;
240 RECT clientRect;
241 INT originalItemHeight = iTextItemHeight;
244 * We use the client rect for the width of the item.
246 GetClientRect(hwnd, &clientRect);
248 lphc->wState &= ~CBF_MEASUREITEM;
251 * Send a first one to measure the size of the text area
253 measureItem.CtlType = ODT_COMBOBOX;
254 measureItem.CtlID = lphc->self->wIDmenu;
255 measureItem.itemID = -1;
256 measureItem.itemWidth = clientRect.right;
257 measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
258 measureItem.itemData = 0;
259 SendMessageA(lphc->owner, WM_MEASUREITEM,
260 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
261 iTextItemHeight = 6 + measureItem.itemHeight;
264 * Send a second one in the case of a fixed ownerdraw list to calculate the
265 * size of the list items. (we basically do this on behalf of the listbox)
267 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
269 measureItem.CtlType = ODT_COMBOBOX;
270 measureItem.CtlID = lphc->self->wIDmenu;
271 measureItem.itemID = 0;
272 measureItem.itemWidth = clientRect.right;
273 measureItem.itemHeight = originalItemHeight;
274 measureItem.itemData = 0;
275 SendMessageA(lphc->owner, WM_MEASUREITEM,
276 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
277 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
281 * Keep the size for the next time
283 lphc->editHeight = iTextItemHeight;
286 return iTextItemHeight;
290 /***********************************************************************
291 * CBCalcPlacement
293 * Set up component coordinates given valid lphc->RectCombo.
295 static void CBCalcPlacement(
296 HWND hwnd,
297 LPHEADCOMBO lphc,
298 LPRECT lprEdit,
299 LPRECT lprButton,
300 LPRECT lprLB)
303 * Again, start with the client rectangle.
305 GetClientRect(hwnd, lprEdit);
308 * Remove the borders
310 InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
313 * Chop off the bottom part to fit with the height of the text area.
315 lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
318 * The button starts the same vertical position as the text area.
320 CopyRect(lprButton, lprEdit);
323 * If the combobox is "simple" there is no button.
325 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
326 lprButton->left = lprButton->right = lprButton->bottom = 0;
327 else
330 * Let's assume the combobox button is the same width as the
331 * scrollbar button.
332 * size the button horizontally and cut-off the text area.
334 lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
335 lprEdit->right = lprButton->left;
339 * In the case of a dropdown, there is an additional spacing between the
340 * text area and the button.
342 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
344 lprEdit->right -= COMBO_EDITBUTTONSPACE();
348 * If we have an edit control, we space it away from the borders slightly.
350 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
352 InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
356 * Adjust the size of the listbox popup.
358 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
361 * Use the client rectangle to initialize the listbox rectangle
363 GetClientRect(hwnd, lprLB);
366 * Then, chop-off the top part.
368 lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
370 else
373 * Make sure the dropped width is as large as the combobox itself.
375 if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
377 lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
380 * In the case of a dropdown, the popup listbox is offset to the right.
381 * so, we want to make sure it's flush with the right side of the
382 * combobox
384 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
385 lprLB->right -= COMBO_EDITBUTTONSPACE();
387 else
388 lprLB->right = lprLB->left + lphc->droppedWidth;
391 TRACE(combo,"\ttext\t= (%i,%i-%i,%i)\n",
392 lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
394 TRACE(combo,"\tbutton\t= (%i,%i-%i,%i)\n",
395 lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
397 TRACE(combo,"\tlbox\t= (%i,%i-%i,%i)\n",
398 lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
401 /***********************************************************************
402 * CBGetDroppedControlRect
404 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
406 CopyRect(lpRect, &lphc->droppedRect);
409 /***********************************************************************
410 * COMBO_WindowPosChanging
412 static LRESULT COMBO_WindowPosChanging(
413 HWND hwnd,
414 LPHEADCOMBO lphc,
415 WINDOWPOS* posChanging)
418 * We need to override the WM_WINDOWPOSCHANGING method to handle all
419 * the non-simple comboboxes. The problem is that those controls are
420 * always the same height. We have to make sure they are not resized
421 * to another value.
423 if ( ( CB_GETTYPE(lphc) != CBS_SIMPLE ) &&
424 ((posChanging->flags & SWP_NOSIZE) == 0) )
426 int newComboHeight;
428 newComboHeight = CBGetTextAreaHeight(hwnd,lphc) +
429 2*COMBO_YBORDERSIZE();
432 * Resizing a combobox has another side effect, it resizes the dropped
433 * rectangle as well. However, it does it only if the new height for the
434 * combobox is different than the height it should have. In other words,
435 * if the application resizing the combobox only had the intention to resize
436 * the actual control, for example, to do the layout of a dialog that is
437 * resized, the height of the dropdown is not changed.
439 if (posChanging->cy != newComboHeight)
441 lphc->droppedRect.bottom = lphc->droppedRect.top + posChanging->cy - newComboHeight;
443 posChanging->cy = newComboHeight;
447 return 0;
450 /***********************************************************************
451 * COMBO_Create
453 static LRESULT COMBO_Create( LPHEADCOMBO lphc, WND* wnd, LPARAM lParam)
455 static char clbName[] = "ComboLBox";
456 static char editName[] = "Edit";
458 LPCREATESTRUCTA lpcs = (CREATESTRUCTA*)lParam;
460 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
461 else if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
463 lphc->self = wnd;
464 lphc->owner = lpcs->hwndParent;
467 * The item height and dropped width are not set when the control
468 * is created.
470 lphc->droppedWidth = lphc->editHeight = 0;
473 * The first time we go through, we want to measure the ownerdraw item
475 lphc->wState |= CBF_MEASUREITEM;
477 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
479 if( lphc->owner || !(lpcs->style & WS_VISIBLE) )
481 UINT lbeStyle;
484 * Initialize the dropped rect to the size of the client area of the
485 * control and then, force all the areas of the combobox to be
486 * recalculated.
488 GetClientRect( wnd->hwndSelf, &lphc->droppedRect );
490 CBCalcPlacement(wnd->hwndSelf,
491 lphc,
492 &lphc->textRect,
493 &lphc->buttonRect,
494 &lphc->droppedRect );
497 * Adjust the position of the popup listbox if it's necessary
499 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
501 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
504 * If it's a dropdown, the listbox is offset
506 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
507 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
509 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect);
510 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect.right);
513 /* create listbox popup */
515 lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS) |
516 (lpcs->style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
518 if( lphc->dwStyle & CBS_SORT )
519 lbeStyle |= LBS_SORT;
520 if( lphc->dwStyle & CBS_HASSTRINGS )
521 lbeStyle |= LBS_HASSTRINGS;
522 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
523 lbeStyle |= LBS_NOINTEGRALHEIGHT;
524 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
525 lbeStyle |= LBS_DISABLENOSCROLL;
527 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
528 lbeStyle |= WS_CHILD | WS_VISIBLE;
529 else /* popup listbox */
530 lbeStyle |= WS_POPUP;
532 /* Dropdown ComboLBox is not a child window and we cannot pass
533 * ID_CB_LISTBOX directly because it will be treated as a menu handle.
535 lphc->hWndLBox = CreateWindowExA(0,
536 clbName,
537 NULL,
538 lbeStyle,
539 lphc->droppedRect.left,
540 lphc->droppedRect.top,
541 lphc->droppedRect.right - lphc->droppedRect.left,
542 lphc->droppedRect.bottom - lphc->droppedRect.top,
543 lphc->self->hwndSelf,
544 (lphc->dwStyle & CBS_DROPDOWN)? (HMENU)0 : (HMENU)ID_CB_LISTBOX,
545 lphc->self->hInstance,
546 (LPVOID)lphc );
548 if( lphc->hWndLBox )
550 BOOL bEdit = TRUE;
551 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT;
554 * In Win95 look, the border fo the edit control is
555 * provided by the combobox
557 if (TWEAK_WineLook == WIN31_LOOK)
558 lbeStyle |= WS_BORDER;
560 if( lphc->wState & CBF_EDIT )
562 if( lphc->dwStyle & CBS_OEMCONVERT )
563 lbeStyle |= ES_OEMCONVERT;
564 if( lphc->dwStyle & CBS_AUTOHSCROLL )
565 lbeStyle |= ES_AUTOHSCROLL;
566 if( lphc->dwStyle & CBS_LOWERCASE )
567 lbeStyle |= ES_LOWERCASE;
568 else if( lphc->dwStyle & CBS_UPPERCASE )
569 lbeStyle |= ES_UPPERCASE;
571 lphc->hWndEdit = CreateWindowExA(0,
572 editName,
573 NULL,
574 lbeStyle,
575 lphc->textRect.left, lphc->textRect.top,
576 lphc->textRect.right - lphc->textRect.left,
577 lphc->textRect.bottom - lphc->textRect.top,
578 lphc->self->hwndSelf,
579 (HMENU)ID_CB_EDIT,
580 lphc->self->hInstance,
581 NULL );
583 if( !lphc->hWndEdit )
584 bEdit = FALSE;
587 if( bEdit )
590 * If the combo is a dropdown, we must resize the control to fit only
591 * the text area and button. To do this, we send a dummy resize and the
592 * WM_WINDOWPOSCHANGING message will take care of setting the height for
593 * us.
595 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
597 CBForceDummyResize(lphc);
600 TRACE(combo,"init done\n");
601 return wnd->hwndSelf;
603 ERR(combo, "edit control failure.\n");
604 } else ERR(combo, "listbox failure.\n");
605 } else ERR(combo, "no owner for visible combo.\n");
607 /* CreateWindow() will send WM_NCDESTROY to cleanup */
609 return -1;
612 /***********************************************************************
613 * CBPaintButton
615 * Paint combo button (normal, pressed, and disabled states).
617 static void CBPaintButton(
618 LPHEADCOMBO lphc,
619 HDC hdc,
620 RECT rectButton)
622 UINT x, y;
623 BOOL bBool;
624 HDC hMemDC;
625 HBRUSH hPrevBrush;
626 COLORREF oldTextColor, oldBkColor;
628 if( lphc->wState & CBF_NOREDRAW )
629 return;
631 hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
634 * Draw the button background
636 PatBlt( hdc,
637 rectButton.left,
638 rectButton.top,
639 rectButton.right-rectButton.left,
640 rectButton.bottom-rectButton.top,
641 PATCOPY );
643 if( (bBool = lphc->wState & CBF_BUTTONDOWN) )
645 DrawEdge( hdc, &rectButton, EDGE_SUNKEN, BF_RECT );
647 else
649 DrawEdge( hdc, &rectButton, EDGE_RAISED, BF_RECT );
653 * Remove the edge of the button from the rectangle
654 * and calculate the position of the bitmap.
656 InflateRect( &rectButton, -2, -2);
658 x = (rectButton.left + rectButton.right - CBitWidth) >> 1;
659 y = (rectButton.top + rectButton.bottom - CBitHeight) >> 1;
662 hMemDC = CreateCompatibleDC( hdc );
663 SelectObject( hMemDC, hComboBmp );
664 oldTextColor = SetTextColor( hdc, GetSysColor(COLOR_BTNFACE) );
665 oldBkColor = SetBkColor( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) :
666 RGB(0,0,0) );
667 BitBlt( hdc, x, y, CBitWidth, CBitHeight, hMemDC, 0, 0, SRCCOPY );
668 SetBkColor( hdc, oldBkColor );
669 SetTextColor( hdc, oldTextColor );
670 DeleteDC( hMemDC );
671 SelectObject( hdc, hPrevBrush );
674 /***********************************************************************
675 * CBPaintText
677 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
679 static void CBPaintText(
680 LPHEADCOMBO lphc,
681 HDC hdc,
682 RECT rectEdit)
684 INT id, size = 0;
685 LPSTR pText = NULL;
687 if( lphc->wState & CBF_NOREDRAW ) return;
689 /* follow Windows combobox that sends a bunch of text
690 * inquiries to its listbox while processing WM_PAINT. */
692 if( (id = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
694 size = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
695 if( (pText = HeapAlloc( GetProcessHeap(), 0, size + 1)) )
697 SendMessageA( lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText );
698 pText[size] = '\0'; /* just in case */
699 } else return;
702 if( lphc->wState & CBF_EDIT )
704 if( CB_HASSTRINGS(lphc) ) SetWindowTextA( lphc->hWndEdit, pText ? pText : "" );
705 if( lphc->wState & CBF_FOCUSED )
706 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
708 else /* paint text field ourselves */
710 HBRUSH hPrevBrush = 0;
711 HDC hDC = hdc;
713 if( !hDC )
715 if ((hDC = GetDC(lphc->self->hwndSelf)))
717 HBRUSH hBrush = SendMessageA( lphc->owner,
718 WM_CTLCOLORLISTBOX,
719 hDC, lphc->self->hwndSelf );
720 hPrevBrush = SelectObject( hDC,
721 (hBrush) ? hBrush : GetStockObject(WHITE_BRUSH) );
724 if( hDC )
726 UINT itemState;
727 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hDC, lphc->hFont) : 0;
730 * Give ourselves some space.
732 InflateRect( &rectEdit, -1, -1 );
734 if ( (lphc->wState & CBF_FOCUSED) &&
735 !(lphc->wState & CBF_DROPPED) )
737 /* highlight */
739 FillRect( hDC, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
740 SetBkColor( hDC, GetSysColor( COLOR_HIGHLIGHT ) );
741 SetTextColor( hDC, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
742 itemState = ODS_SELECTED | ODS_FOCUS;
744 else
745 itemState = 0;
747 if( CB_OWNERDRAWN(lphc) )
749 DRAWITEMSTRUCT dis;
750 HRGN clipRegion;
753 * Save the current clip region.
754 * To retrieve the clip region, we need to create one "dummy"
755 * clip region.
757 clipRegion = CreateRectRgnIndirect(&rectEdit);
759 if (GetClipRgn(hDC, clipRegion)!=1)
761 DeleteObject(clipRegion);
762 clipRegion=(HRGN)NULL;
765 if ( lphc->self->dwStyle & WS_DISABLED )
766 itemState |= ODS_DISABLED;
768 dis.CtlType = ODT_COMBOBOX;
769 dis.CtlID = lphc->self->wIDmenu;
770 dis.hwndItem = lphc->self->hwndSelf;
771 dis.itemAction = ODA_DRAWENTIRE;
772 dis.itemID = id;
773 dis.itemState = itemState;
774 dis.hDC = hDC;
775 dis.rcItem = rectEdit;
776 dis.itemData = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA,
777 (WPARAM)id, 0 );
780 * Clip the DC and have the parent draw the item.
782 IntersectClipRect(hDC,
783 rectEdit.left, rectEdit.top,
784 rectEdit.right, rectEdit.bottom);
786 SendMessageA(lphc->owner, WM_DRAWITEM,
787 lphc->self->wIDmenu, (LPARAM)&dis );
790 * Reset the clipping region.
792 SelectClipRgn(hDC, clipRegion);
794 else
796 ExtTextOutA( hDC,
797 rectEdit.left + 1,
798 rectEdit.top + 1,
799 ETO_OPAQUE | ETO_CLIPPED,
800 &rectEdit,
801 pText ? pText : "" , size, NULL );
803 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
804 DrawFocusRect( hDC, &rectEdit );
807 if( hPrevFont )
808 SelectObject(hDC, hPrevFont );
810 if( !hdc )
812 if( hPrevBrush )
813 SelectObject( hDC, hPrevBrush );
815 ReleaseDC( lphc->self->hwndSelf, hDC );
819 if (pText)
820 HeapFree( GetProcessHeap(), 0, pText );
823 /***********************************************************************
824 * CBPaintBorder
826 static void CBPaintBorder(
827 HWND hwnd,
828 LPHEADCOMBO lphc,
829 HDC hdc)
831 RECT clientRect;
833 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
835 GetClientRect(hwnd, &clientRect);
837 else
839 CopyRect(&clientRect, &lphc->textRect);
841 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
842 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
845 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
848 /***********************************************************************
849 * COMBO_EraseBackground
851 static LRESULT COMBO_EraseBackground(
852 HWND hwnd,
853 LPHEADCOMBO lphc,
854 HDC hParamDC)
856 HBRUSH hBkgBrush;
857 RECT clientRect;
858 HDC hDC;
860 hDC = (hParamDC) ? hParamDC
861 : GetDC(hwnd);
864 * Calculate the area that we want to erase.
866 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
868 GetClientRect(hwnd, &clientRect);
870 else
872 CopyRect(&clientRect, &lphc->textRect);
874 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
877 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
878 hDC, hwnd);
880 if( !hBkgBrush )
881 hBkgBrush = GetStockObject(WHITE_BRUSH);
883 FillRect(hDC, &clientRect, hBkgBrush);
885 if (!hParamDC)
886 ReleaseDC(hwnd, hDC);
888 return TRUE;
891 /***********************************************************************
892 * COMBO_Paint
894 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
896 PAINTSTRUCT ps;
897 HDC hDC;
899 hDC = (hParamDC) ? hParamDC
900 : BeginPaint( lphc->self->hwndSelf, &ps);
903 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
905 HBRUSH hPrevBrush, hBkgBrush;
907 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
908 hDC, lphc->self->hwndSelf );
910 if( !hBkgBrush )
911 hBkgBrush = GetStockObject(WHITE_BRUSH);
913 hPrevBrush = SelectObject( hDC, hBkgBrush );
916 * In non 3.1 look, there is a sunken border on the combobox
918 if (TWEAK_WineLook != WIN31_LOOK)
920 CBPaintBorder(CB_HWND(lphc), lphc, hDC);
923 if( !IsRectEmpty(&lphc->buttonRect) )
925 CBPaintButton(lphc, hDC, lphc->buttonRect);
928 if( !(lphc->wState & CBF_EDIT) )
931 * The text area has a border only in Win 3.1 look.
933 if (TWEAK_WineLook == WIN31_LOOK)
935 HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
937 Rectangle( hDC,
938 lphc->textRect.left, lphc->textRect.top,
939 lphc->textRect.right - 1, lphc->textRect.bottom - 1);
941 SelectObject( hDC, hPrevPen );
944 CBPaintText( lphc, hDC, lphc->textRect);
947 if( hPrevBrush )
948 SelectObject( hDC, hPrevBrush );
951 if( !hParamDC )
952 EndPaint(lphc->self->hwndSelf, &ps);
954 return 0;
957 /***********************************************************************
958 * CBUpdateLBox
960 * Select listbox entry according to the contents of the edit control.
962 static INT CBUpdateLBox( LPHEADCOMBO lphc )
964 INT length, idx, ret;
965 LPSTR pText = NULL;
967 idx = ret = LB_ERR;
968 length = CB_GETEDITTEXTLENGTH( lphc );
970 if( length > 0 )
971 pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1);
973 TRACE(combo,"\t edit text length %i\n", length );
975 if( pText )
977 if( length ) GetWindowTextA( lphc->hWndEdit, pText, length + 1);
978 else pText[0] = '\0';
979 idx = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
980 (WPARAM)(-1), (LPARAM)pText );
981 if( idx == LB_ERR ) idx = 0; /* select first item */
982 else ret = idx;
983 HeapFree( GetProcessHeap(), 0, pText );
986 /* select entry */
988 SendMessageA( lphc->hWndLBox, LB_SETCURSEL, (WPARAM)idx, 0 );
990 if( idx >= 0 )
992 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)idx, 0 );
993 /* probably superfluous but Windows sends this too */
994 SendMessageA( lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)idx, 0 );
996 return ret;
999 /***********************************************************************
1000 * CBUpdateEdit
1002 * Copy a listbox entry to the edit control.
1004 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
1006 INT length;
1007 LPSTR pText = NULL;
1009 TRACE(combo,"\t %i\n", index );
1011 if( index == -1 )
1013 length = CB_GETEDITTEXTLENGTH( lphc );
1014 if( length )
1016 if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
1018 GetWindowTextA( lphc->hWndEdit, pText, length + 1 );
1019 index = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
1020 (WPARAM)(-1), (LPARAM)pText );
1021 HeapFree( GetProcessHeap(), 0, pText );
1026 if( index >= 0 ) /* got an entry */
1028 length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1029 if( length )
1031 if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
1033 SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1034 (WPARAM)index, (LPARAM)pText );
1035 SendMessageA( lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)pText );
1036 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1037 HeapFree( GetProcessHeap(), 0, pText );
1043 /***********************************************************************
1044 * CBDropDown
1046 * Show listbox popup.
1048 static void CBDropDown( LPHEADCOMBO lphc )
1050 INT index;
1051 RECT rect;
1053 TRACE(combo,"[%04x]: drop down\n", CB_HWND(lphc));
1055 CB_NOTIFY( lphc, CBN_DROPDOWN );
1057 /* set selection */
1059 lphc->wState |= CBF_DROPPED;
1060 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1062 index = CBUpdateLBox( lphc );
1063 if( !(lphc->wState & CBF_CAPTURE) ) CBUpdateEdit( lphc, index );
1065 else
1067 index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1068 if( index == LB_ERR ) index = 0;
1069 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)index, 0 );
1070 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1073 /* now set popup position */
1074 GetWindowRect( lphc->self->hwndSelf, &rect );
1078 * If it's a dropdown, the listbox is offset
1080 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1081 rect.left += COMBO_EDITBUTTONSPACE();
1083 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1084 lphc->droppedRect.right - lphc->droppedRect.left,
1085 lphc->droppedRect.bottom - lphc->droppedRect.top,
1086 SWP_NOACTIVATE | SWP_NOREDRAW);
1088 if( !(lphc->wState & CBF_NOREDRAW) )
1089 RedrawWindow( lphc->self->hwndSelf, NULL, 0, RDW_INVALIDATE |
1090 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1092 ShowWindow( lphc->hWndLBox, SW_SHOWNA );
1095 /***********************************************************************
1096 * CBRollUp
1098 * Hide listbox popup.
1100 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1102 HWND hWnd = lphc->self->hwndSelf;
1104 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1106 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1109 TRACE(combo,"[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
1111 /* always send WM_LBUTTONUP? */
1112 SendMessageA( lphc->hWndLBox, WM_LBUTTONUP, 0, (LPARAM)(-1) );
1114 if( lphc->wState & CBF_DROPPED )
1116 RECT rect;
1118 lphc->wState &= ~CBF_DROPPED;
1119 ShowWindow( lphc->hWndLBox, SW_HIDE );
1121 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1123 INT index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1124 CBUpdateEdit( lphc, index );
1125 rect = lphc->buttonRect;
1127 else
1129 if( bButton )
1131 UnionRect( &rect,
1132 &lphc->buttonRect,
1133 &lphc->textRect);
1135 else
1136 rect = lphc->textRect;
1138 bButton = TRUE;
1141 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1142 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1143 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1144 CB_NOTIFY( lphc, CBN_CLOSEUP );
1149 /***********************************************************************
1150 * COMBO_FlipListbox
1152 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1154 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL bRedrawButton )
1156 if( lphc->wState & CBF_DROPPED )
1158 CBRollUp( lphc, TRUE, bRedrawButton );
1159 return FALSE;
1162 CBDropDown( lphc );
1163 return TRUE;
1166 /***********************************************************************
1167 * COMBO_GetLBWindow
1169 * Edit control helper.
1171 HWND COMBO_GetLBWindow( WND* pWnd )
1173 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1174 if( lphc ) return lphc->hWndLBox;
1175 return 0;
1179 /***********************************************************************
1180 * CBRepaintButton
1182 static void CBRepaintButton( LPHEADCOMBO lphc )
1184 InvalidateRect(CB_HWND(lphc), &lphc->buttonRect, TRUE);
1185 UpdateWindow(CB_HWND(lphc));
1188 /***********************************************************************
1189 * COMBO_SetFocus
1191 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1193 if( !(lphc->wState & CBF_FOCUSED) )
1195 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1196 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1198 if( lphc->wState & CBF_EDIT )
1199 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1200 lphc->wState |= CBF_FOCUSED;
1201 if( !(lphc->wState & CBF_EDIT) )
1203 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1206 CB_NOTIFY( lphc, CBN_SETFOCUS );
1210 /***********************************************************************
1211 * COMBO_KillFocus
1213 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1215 HWND hWnd = lphc->self->hwndSelf;
1217 if( lphc->wState & CBF_FOCUSED )
1219 SendMessageA( hWnd, WM_LBUTTONUP, 0, (LPARAM)(-1) );
1221 CBRollUp( lphc, FALSE, TRUE );
1222 if( IsWindow( hWnd ) )
1224 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1225 SendMessageA( lphc->hWndLBox, LB_CARETOFF, 0, 0 );
1227 lphc->wState &= ~CBF_FOCUSED;
1229 /* redraw text */
1230 if( lphc->wState & CBF_EDIT )
1231 SendMessageA( lphc->hWndEdit, EM_SETSEL, (WPARAM)(-1), 0 );
1232 else
1234 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1237 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1242 /***********************************************************************
1243 * COMBO_Command
1245 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1247 if( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1249 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1251 switch( HIWORD(wParam) >> 8 )
1253 case (EN_SETFOCUS >> 8):
1255 TRACE(combo,"[%04x]: edit [%04x] got focus\n",
1256 CB_HWND(lphc), lphc->hWndEdit );
1258 if( !(lphc->wState & CBF_FOCUSED) ) COMBO_SetFocus( lphc );
1259 break;
1261 case (EN_KILLFOCUS >> 8):
1263 TRACE(combo,"[%04x]: edit [%04x] lost focus\n",
1264 CB_HWND(lphc), lphc->hWndEdit );
1266 /* NOTE: it seems that Windows' edit control sends an
1267 * undocumented message WM_USER + 0x1B instead of this
1268 * notification (only when it happens to be a part of
1269 * the combo). ?? - AK.
1272 COMBO_KillFocus( lphc );
1273 break;
1276 case (EN_CHANGE >> 8):
1277 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1278 CBUpdateLBox( lphc );
1279 break;
1281 case (EN_UPDATE >> 8):
1282 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1283 break;
1285 case (EN_ERRSPACE >> 8):
1286 CB_NOTIFY( lphc, CBN_ERRSPACE );
1289 else if( lphc->hWndLBox == hWnd )
1291 switch( HIWORD(wParam) )
1293 case LBN_ERRSPACE:
1294 CB_NOTIFY( lphc, CBN_ERRSPACE );
1295 break;
1297 case LBN_DBLCLK:
1298 CB_NOTIFY( lphc, CBN_DBLCLK );
1299 break;
1301 case LBN_SELCHANGE:
1302 case LBN_SELCANCEL:
1304 TRACE(combo,"[%04x]: lbox selection change [%04x]\n",
1305 CB_HWND(lphc), lphc->wState );
1307 /* do not roll up if selection is being tracked
1308 * by arrowkeys in the dropdown listbox */
1310 if( (lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP) )
1311 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1312 else lphc->wState &= ~CBF_NOROLLUP;
1314 CB_NOTIFY( lphc, CBN_SELCHANGE );
1315 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1316 /* fall through */
1318 case LBN_SETFOCUS:
1319 case LBN_KILLFOCUS:
1320 /* nothing to do here since ComboLBox always resets the focus to its
1321 * combo/edit counterpart */
1322 break;
1325 return 0;
1328 /***********************************************************************
1329 * COMBO_ItemOp
1331 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1333 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg,
1334 WPARAM wParam, LPARAM lParam )
1336 HWND hWnd = lphc->self->hwndSelf;
1338 TRACE(combo,"[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
1340 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
1342 /* two first items are the same in all 4 structs */
1343 lpIS->CtlType = ODT_COMBOBOX;
1344 lpIS->CtlID = lphc->self->wIDmenu;
1346 switch( msg ) /* patch window handle */
1348 case WM_DELETEITEM:
1349 lpIS->hwndItem = hWnd;
1350 #undef lpIS
1351 break;
1352 case WM_DRAWITEM:
1353 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
1354 lpIS->hwndItem = hWnd;
1355 #undef lpIS
1356 break;
1357 case WM_COMPAREITEM:
1358 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
1359 lpIS->hwndItem = hWnd;
1360 #undef lpIS
1361 break;
1364 return SendMessageA( lphc->owner, msg, lphc->self->wIDmenu, lParam );
1367 /***********************************************************************
1368 * COMBO_GetText
1370 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT N, LPSTR lpText)
1372 if( lphc->wState & CBF_EDIT )
1373 return SendMessageA( lphc->hWndEdit, WM_GETTEXT,
1374 (WPARAM)N, (LPARAM)lpText );
1376 /* get it from the listbox */
1378 if( lphc->hWndLBox )
1380 INT idx = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1381 if( idx != LB_ERR )
1383 LPSTR lpBuffer;
1384 INT length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN,
1385 (WPARAM)idx, 0 );
1387 /* 'length' is without the terminating character */
1388 if( length >= N )
1389 lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 );
1390 else
1391 lpBuffer = lpText;
1393 if( lpBuffer )
1395 INT n = SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1396 (WPARAM)idx, (LPARAM)lpBuffer );
1398 /* truncate if buffer is too short */
1400 if( length >= N )
1402 if (N && lpText) {
1403 if( n != LB_ERR ) memcpy( lpText, lpBuffer, (N>n) ? n+1 : N-1 );
1404 lpText[N - 1] = '\0';
1406 HeapFree( GetProcessHeap(), 0, lpBuffer );
1408 return (LRESULT)n;
1412 return 0;
1416 /***********************************************************************
1417 * CBResetPos
1419 * This function sets window positions according to the updated
1420 * component placement struct.
1422 static void CBResetPos(
1423 LPHEADCOMBO lphc,
1424 LPRECT rectEdit,
1425 LPRECT rectLB,
1426 BOOL bRedraw)
1428 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1430 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1431 * sizing messages */
1433 if( lphc->wState & CBF_EDIT )
1434 SetWindowPos( lphc->hWndEdit, 0,
1435 rectEdit->left, rectEdit->top,
1436 rectEdit->right - rectEdit->left,
1437 rectEdit->bottom - rectEdit->top,
1438 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1440 SetWindowPos( lphc->hWndLBox, 0,
1441 rectLB->left, rectLB->top,
1442 rectLB->right - rectLB->left,
1443 rectLB->bottom - rectLB->top,
1444 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1446 if( bDrop )
1448 if( lphc->wState & CBF_DROPPED )
1450 lphc->wState &= ~CBF_DROPPED;
1451 ShowWindow( lphc->hWndLBox, SW_HIDE );
1454 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1455 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
1456 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1461 /***********************************************************************
1462 * COMBO_Size
1464 static void COMBO_Size( LPHEADCOMBO lphc )
1466 CBCalcPlacement(lphc->self->hwndSelf,
1467 lphc,
1468 &lphc->textRect,
1469 &lphc->buttonRect,
1470 &lphc->droppedRect);
1472 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1476 /***********************************************************************
1477 * COMBO_Font
1479 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1482 * Set the font
1484 lphc->hFont = hFont;
1487 * Propagate to owned windows.
1489 if( lphc->wState & CBF_EDIT )
1490 SendMessageA( lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw );
1491 SendMessageA( lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw );
1494 * Redo the layout of the control.
1496 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1498 CBCalcPlacement(lphc->self->hwndSelf,
1499 lphc,
1500 &lphc->textRect,
1501 &lphc->buttonRect,
1502 &lphc->droppedRect);
1504 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1506 else
1508 CBForceDummyResize(lphc);
1513 /***********************************************************************
1514 * COMBO_SetItemHeight
1516 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1518 LRESULT lRet = CB_ERR;
1520 if( index == -1 ) /* set text field height */
1522 if( height < 32768 )
1524 lphc->editHeight = height;
1527 * Redo the layout of the control.
1529 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1531 CBCalcPlacement(lphc->self->hwndSelf,
1532 lphc,
1533 &lphc->textRect,
1534 &lphc->buttonRect,
1535 &lphc->droppedRect);
1537 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1539 else
1541 CBForceDummyResize(lphc);
1544 lRet = height;
1547 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1548 lRet = SendMessageA( lphc->hWndLBox, LB_SETITEMHEIGHT,
1549 (WPARAM)index, (LPARAM)height );
1550 return lRet;
1553 /***********************************************************************
1554 * COMBO_SelectString
1556 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPCSTR pText )
1558 INT index = SendMessageA( lphc->hWndLBox, LB_SELECTSTRING,
1559 (WPARAM)start, (LPARAM)pText );
1560 if( index >= 0 )
1562 if( lphc->wState & CBF_EDIT )
1563 CBUpdateEdit( lphc, index );
1564 else
1566 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1569 return (LRESULT)index;
1572 /***********************************************************************
1573 * COMBO_LButtonDown
1575 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1577 POINT pt;
1578 BOOL bButton;
1579 HWND hWnd = lphc->self->hwndSelf;
1581 pt.x = LOWORD(lParam);
1582 pt.y = HIWORD(lParam);
1583 bButton = PtInRect(&lphc->buttonRect, pt);
1585 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1586 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1588 lphc->wState |= CBF_BUTTONDOWN;
1589 if( lphc->wState & CBF_DROPPED )
1591 /* got a click to cancel selection */
1593 lphc->wState &= ~CBF_BUTTONDOWN;
1594 CBRollUp( lphc, TRUE, FALSE );
1595 if( !IsWindow( hWnd ) ) return;
1597 if( lphc->wState & CBF_CAPTURE )
1599 lphc->wState &= ~CBF_CAPTURE;
1600 ReleaseCapture();
1603 else
1605 /* drop down the listbox and start tracking */
1607 lphc->wState |= CBF_CAPTURE;
1608 CBDropDown( lphc );
1609 SetCapture( hWnd );
1611 if( bButton ) CBRepaintButton( lphc );
1615 /***********************************************************************
1616 * COMBO_LButtonUp
1618 * Release capture and stop tracking if needed.
1620 static void COMBO_LButtonUp( LPHEADCOMBO lphc, LPARAM lParam )
1622 if( lphc->wState & CBF_CAPTURE )
1624 lphc->wState &= ~CBF_CAPTURE;
1625 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1627 INT index = CBUpdateLBox( lphc );
1628 CBUpdateEdit( lphc, index );
1630 ReleaseCapture();
1633 if( lphc->wState & CBF_BUTTONDOWN )
1635 lphc->wState &= ~CBF_BUTTONDOWN;
1636 CBRepaintButton( lphc );
1640 /***********************************************************************
1641 * COMBO_MouseMove
1643 * Two things to do - track combo button and release capture when
1644 * pointer goes into the listbox.
1646 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1648 POINT pt;
1649 RECT lbRect;
1651 pt.x = LOWORD(lParam);
1652 pt.y = HIWORD(lParam);
1654 if( lphc->wState & CBF_BUTTONDOWN )
1656 BOOL bButton;
1658 bButton = PtInRect(&lphc->buttonRect, pt);
1660 if( !bButton )
1662 lphc->wState &= ~CBF_BUTTONDOWN;
1663 CBRepaintButton( lphc );
1667 GetClientRect( lphc->hWndLBox, &lbRect );
1668 MapWindowPoints( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 );
1669 if( PtInRect(&lbRect, pt) )
1671 lphc->wState &= ~CBF_CAPTURE;
1672 ReleaseCapture();
1673 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc );
1675 /* hand over pointer tracking */
1676 SendMessageA( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam );
1681 /***********************************************************************
1682 * ComboWndProc
1684 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1686 LRESULT WINAPI ComboWndProc( HWND hwnd, UINT message,
1687 WPARAM wParam, LPARAM lParam )
1689 LRESULT retvalue;
1690 WND* pWnd = WIN_FindWndPtr(hwnd);
1692 if( pWnd )
1694 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1696 TRACE(combo, "[%04x]: msg %s wp %08x lp %08lx\n",
1697 pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
1699 if( lphc || message == WM_NCCREATE )
1700 switch(message)
1703 /* System messages */
1705 case WM_NCCREATE:
1706 retvalue = COMBO_NCCreate(pWnd, lParam);
1707 goto END;
1708 case WM_NCDESTROY:
1709 COMBO_NCDestroy(lphc);
1710 break;
1712 case WM_CREATE:
1713 retvalue = COMBO_Create(lphc, pWnd, lParam);
1714 goto END;
1716 case WM_PRINTCLIENT:
1717 if (lParam & PRF_ERASEBKGND)
1718 COMBO_EraseBackground(hwnd, lphc, wParam);
1720 /* Fallthrough */
1721 case WM_PAINT:
1722 /* wParam may contain a valid HDC! */
1723 retvalue = COMBO_Paint(lphc, wParam);
1724 goto END;
1725 case WM_ERASEBKGND:
1726 retvalue = COMBO_EraseBackground(hwnd, lphc, wParam);
1727 goto END;
1728 case WM_GETDLGCODE:
1729 retvalue = (LRESULT)(DLGC_WANTARROWS | DLGC_WANTCHARS);
1730 goto END;
1731 case WM_WINDOWPOSCHANGING:
1732 retvalue = COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1733 return retvalue;
1734 case WM_SIZE:
1735 if( lphc->hWndLBox &&
1736 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1737 retvalue = TRUE;
1738 goto END;
1739 case WM_SETFONT:
1740 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1741 retvalue = TRUE;
1742 goto END;
1743 case WM_GETFONT:
1744 retvalue = (LRESULT)lphc->hFont;
1745 goto END;
1746 case WM_SETFOCUS:
1747 if( lphc->wState & CBF_EDIT )
1748 SetFocus( lphc->hWndEdit );
1749 else
1750 COMBO_SetFocus( lphc );
1751 retvalue = TRUE;
1752 goto END;
1753 case WM_KILLFOCUS:
1754 #define hwndFocus ((HWND16)wParam)
1755 if( !hwndFocus ||
1756 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1757 COMBO_KillFocus( lphc );
1758 #undef hwndFocus
1759 retvalue = TRUE;
1760 goto END;
1761 case WM_COMMAND:
1762 retvalue = COMBO_Command( lphc, wParam, (HWND)lParam );
1763 goto END;
1764 case WM_GETTEXT:
1765 retvalue = COMBO_GetText( lphc, (UINT)wParam, (LPSTR)lParam );
1766 goto END;
1767 case WM_SETTEXT:
1768 case WM_GETTEXTLENGTH:
1769 case WM_CLEAR:
1770 case WM_CUT:
1771 case WM_PASTE:
1772 case WM_COPY:
1773 if( lphc->wState & CBF_EDIT )
1775 retvalue = SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1776 goto END;
1778 retvalue = CB_ERR;
1779 goto END;
1780 case WM_DRAWITEM:
1781 case WM_DELETEITEM:
1782 case WM_COMPAREITEM:
1783 case WM_MEASUREITEM:
1784 retvalue = COMBO_ItemOp( lphc, message, wParam, lParam );
1785 goto END;
1786 case WM_ENABLE:
1787 if( lphc->wState & CBF_EDIT )
1788 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1789 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1790 retvalue = TRUE;
1791 goto END;
1792 case WM_SETREDRAW:
1793 if( wParam )
1794 lphc->wState &= ~CBF_NOREDRAW;
1795 else
1796 lphc->wState |= CBF_NOREDRAW;
1798 if( lphc->wState & CBF_EDIT )
1799 SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1800 SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1801 retvalue = 0;
1802 goto END;
1803 case WM_SYSKEYDOWN:
1804 if( KEYDATA_ALT & HIWORD(lParam) )
1805 if( wParam == VK_UP || wParam == VK_DOWN )
1806 COMBO_FlipListbox( lphc, TRUE );
1807 break;
1809 case WM_CHAR:
1810 case WM_KEYDOWN:
1811 if( lphc->wState & CBF_EDIT )
1812 retvalue = SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1813 else
1814 retvalue = SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1815 goto END;
1816 case WM_LBUTTONDOWN:
1817 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf );
1818 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1819 retvalue = TRUE;
1820 goto END;
1821 case WM_LBUTTONUP:
1822 COMBO_LButtonUp( lphc, lParam );
1823 retvalue = TRUE;
1824 goto END;
1825 case WM_MOUSEMOVE:
1826 if( lphc->wState & CBF_CAPTURE )
1827 COMBO_MouseMove( lphc, wParam, lParam );
1828 retvalue = TRUE;
1829 goto END;
1830 /* Combo messages */
1832 case CB_ADDSTRING16:
1833 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1834 case CB_ADDSTRING:
1835 retvalue = SendMessageA( lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1836 goto END;
1837 case CB_INSERTSTRING16:
1838 wParam = (INT)(INT16)wParam;
1839 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1840 case CB_INSERTSTRING:
1841 retvalue = SendMessageA( lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1842 goto END;
1843 case CB_DELETESTRING16:
1844 case CB_DELETESTRING:
1845 retvalue = SendMessageA( lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1846 goto END;
1847 case CB_SELECTSTRING16:
1848 wParam = (INT)(INT16)wParam;
1849 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1850 case CB_SELECTSTRING:
1851 retvalue = COMBO_SelectString( lphc, (INT)wParam, (LPSTR)lParam );
1852 goto END;
1853 case CB_FINDSTRING16:
1854 wParam = (INT)(INT16)wParam;
1855 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1856 case CB_FINDSTRING:
1857 retvalue = SendMessageA( lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1858 goto END;
1859 case CB_FINDSTRINGEXACT16:
1860 wParam = (INT)(INT16)wParam;
1861 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1862 case CB_FINDSTRINGEXACT:
1863 retvalue = SendMessageA( lphc->hWndLBox, LB_FINDSTRINGEXACT,
1864 wParam, lParam );
1865 goto END;
1866 case CB_SETITEMHEIGHT16:
1867 wParam = (INT)(INT16)wParam; /* signed integer */
1868 case CB_SETITEMHEIGHT:
1869 retvalue = COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1870 goto END;
1871 case CB_GETITEMHEIGHT16:
1872 wParam = (INT)(INT16)wParam;
1873 case CB_GETITEMHEIGHT:
1874 if( (INT)wParam >= 0 ) /* listbox item */
1876 retvalue = SendMessageA( lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1877 goto END;
1879 retvalue = CBGetTextAreaHeight(hwnd, lphc);
1880 goto END;
1881 case CB_RESETCONTENT16:
1882 case CB_RESETCONTENT:
1883 SendMessageA( lphc->hWndLBox, LB_RESETCONTENT, 0, 0 );
1884 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
1885 retvalue = TRUE;
1886 goto END;
1887 case CB_INITSTORAGE:
1888 retvalue = SendMessageA( lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
1889 goto END;
1890 case CB_GETHORIZONTALEXTENT:
1891 retvalue = SendMessageA( lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
1892 goto END;
1893 case CB_SETHORIZONTALEXTENT:
1894 retvalue = SendMessageA( lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
1895 goto END;
1896 case CB_GETTOPINDEX:
1897 retvalue = SendMessageA( lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
1898 goto END;
1899 case CB_GETLOCALE:
1900 retvalue = SendMessageA( lphc->hWndLBox, LB_GETLOCALE, 0, 0);
1901 goto END;
1902 case CB_SETLOCALE:
1903 retvalue = SendMessageA( lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
1904 goto END;
1905 case CB_GETDROPPEDWIDTH:
1906 if( lphc->droppedWidth )
1908 retvalue = lphc->droppedWidth;
1909 goto END;
1911 retvalue = lphc->droppedRect.right - lphc->droppedRect.left;
1912 goto END;
1913 case CB_SETDROPPEDWIDTH:
1914 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
1915 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
1916 retvalue = CB_ERR;
1917 goto END;
1918 case CB_GETDROPPEDCONTROLRECT16:
1919 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1920 if( lParam )
1922 RECT r;
1923 CBGetDroppedControlRect( lphc, &r );
1924 CONV_RECT32TO16( &r, (LPRECT16)lParam );
1926 retvalue = CB_OKAY;
1927 goto END;
1928 case CB_GETDROPPEDCONTROLRECT:
1929 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
1930 retvalue = CB_OKAY;
1931 goto END;
1932 case CB_GETDROPPEDSTATE16:
1933 case CB_GETDROPPEDSTATE:
1934 retvalue = (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
1935 goto END;
1936 case CB_DIR16:
1937 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1938 /* fall through */
1939 case CB_DIR:
1940 retvalue = COMBO_Directory( lphc, (UINT)wParam,
1941 (LPSTR)lParam, (message == CB_DIR));
1942 goto END;
1943 case CB_SHOWDROPDOWN16:
1944 case CB_SHOWDROPDOWN:
1945 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1947 if( wParam )
1949 if( !(lphc->wState & CBF_DROPPED) )
1950 CBDropDown( lphc );
1952 else
1953 if( lphc->wState & CBF_DROPPED )
1954 CBRollUp( lphc, FALSE, TRUE );
1956 retvalue = TRUE;
1957 goto END;
1958 case CB_GETCOUNT16:
1959 case CB_GETCOUNT:
1960 retvalue = SendMessageA( lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1961 goto END;
1962 case CB_GETCURSEL16:
1963 case CB_GETCURSEL:
1964 retvalue = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1965 goto END;
1966 case CB_SETCURSEL16:
1967 wParam = (INT)(INT16)wParam;
1968 case CB_SETCURSEL:
1969 lParam = SendMessageA( lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
1970 if( lphc->wState & CBF_SELCHANGE )
1972 /* no LBN_SELCHANGE in this case, update manually */
1973 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1974 lphc->wState &= ~CBF_SELCHANGE;
1976 retvalue = lParam;
1977 goto END;
1978 case CB_GETLBTEXT16:
1979 wParam = (INT)(INT16)wParam;
1980 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1981 case CB_GETLBTEXT:
1982 retvalue = SendMessageA( lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
1983 goto END;
1984 case CB_GETLBTEXTLEN16:
1985 wParam = (INT)(INT16)wParam;
1986 case CB_GETLBTEXTLEN:
1987 retvalue = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
1988 goto END;
1989 case CB_GETITEMDATA16:
1990 wParam = (INT)(INT16)wParam;
1991 case CB_GETITEMDATA:
1992 retvalue = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
1993 goto END;
1994 case CB_SETITEMDATA16:
1995 wParam = (INT)(INT16)wParam;
1996 case CB_SETITEMDATA:
1997 retvalue = SendMessageA( lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
1998 goto END;
1999 case CB_GETEDITSEL16:
2000 wParam = lParam = 0; /* just in case */
2001 case CB_GETEDITSEL:
2002 if( lphc->wState & CBF_EDIT )
2004 INT a, b;
2006 retvalue = SendMessageA( lphc->hWndEdit, EM_GETSEL,
2007 (wParam) ? wParam : (WPARAM)&a,
2008 (lParam) ? lParam : (LPARAM)&b );
2009 goto END;
2011 retvalue = CB_ERR;
2012 goto END;
2013 case CB_SETEDITSEL16:
2014 case CB_SETEDITSEL:
2015 if( lphc->wState & CBF_EDIT )
2017 retvalue = SendMessageA( lphc->hWndEdit, EM_SETSEL,
2018 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2019 goto END;
2021 retvalue = CB_ERR;
2022 goto END;
2023 case CB_SETEXTENDEDUI16:
2024 case CB_SETEXTENDEDUI:
2025 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2027 retvalue = CB_ERR;
2028 goto END;
2030 if( wParam )
2031 lphc->wState |= CBF_EUI;
2032 else lphc->wState &= ~CBF_EUI;
2033 retvalue = CB_OKAY;
2034 goto END;
2035 case CB_GETEXTENDEDUI16:
2036 case CB_GETEXTENDEDUI:
2037 retvalue = (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2038 goto END;
2039 case (WM_USER + 0x1B):
2040 WARN(combo, "[%04x]: undocumented msg!\n", hwnd );
2042 retvalue = DefWindowProcA(hwnd, message, wParam, lParam);
2043 goto END;
2045 retvalue = CB_ERR;
2046 END:
2047 WIN_ReleaseWndPtr(pWnd);
2048 return retvalue;