Fixed a few behaviors of the combobox that were broken.
[wine/testsucceed.git] / controls / combo.c
blob63f8692ed1c1cc17a6b8e64e7c9605e000d63cdc
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 "winbase.h"
12 #include "winuser.h"
13 #include "wingdi.h"
14 #include "wine/winuser16.h"
15 #include "win.h"
16 #include "spy.h"
17 #include "user.h"
18 #include "heap.h"
19 #include "combo.h"
20 #include "drive.h"
21 #include "debugtools.h"
22 #include "tweak.h"
24 DEFAULT_DEBUG_CHANNEL(combo)
26 /* bits in the dwKeyData */
27 #define KEYDATA_ALT 0x2000
28 #define KEYDATA_PREVSTATE 0x4000
31 * Additional combo box definitions
34 #define CB_GETPTR( wnd ) (*(LPHEADCOMBO*)((wnd)->wExtra))
35 #define CB_NOTIFY( lphc, code ) \
36 (SendMessageA( (lphc)->owner, WM_COMMAND, \
37 MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf))
38 #define CB_GETEDITTEXTLENGTH( lphc ) \
39 (SendMessageA( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
42 * Drawing globals
44 static HBITMAP hComboBmp = 0;
45 static UINT CBitHeight, CBitWidth;
48 * Look and feel dependant "constants"
50 #define COMBO_XBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
51 #define COMBO_YBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
52 #define COMBO_EDITBUTTONSPACE() ( (TWEAK_WineLook == WIN31_LOOK) ? 8 : 0 )
53 #define EDIT_CONTROL_PADDING() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 1 )
55 /***********************************************************************
56 * COMBO_Init
58 * Load combo button bitmap.
60 static BOOL COMBO_Init()
62 HDC hDC;
64 if( hComboBmp ) return TRUE;
65 if( (hDC = CreateCompatibleDC(0)) )
67 BOOL bRet = FALSE;
68 if( (hComboBmp = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_COMBO))) )
70 BITMAP bm;
71 HBITMAP hPrevB;
72 RECT r;
74 GetObjectA( hComboBmp, sizeof(bm), &bm );
75 CBitHeight = bm.bmHeight;
76 CBitWidth = bm.bmWidth;
78 TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
80 hPrevB = SelectObject16( hDC, hComboBmp);
81 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
82 InvertRect( hDC, &r );
83 SelectObject( hDC, hPrevB );
84 bRet = TRUE;
86 DeleteDC( hDC );
87 return bRet;
89 return FALSE;
92 /***********************************************************************
93 * COMBO_NCCreate
95 static LRESULT COMBO_NCCreate(WND* wnd, LPARAM lParam)
97 LPHEADCOMBO lphc;
99 if ( wnd && COMBO_Init() &&
100 (lphc = HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO))) )
102 LPCREATESTRUCTA lpcs = (CREATESTRUCTA*)lParam;
104 memset( lphc, 0, sizeof(HEADCOMBO) );
105 *(LPHEADCOMBO*)wnd->wExtra = lphc;
107 /* some braindead apps do try to use scrollbar/border flags */
109 lphc->dwStyle = (lpcs->style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL));
110 wnd->dwStyle &= ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
113 * We also have to remove the client edge style to make sure
114 * we don't end-up with a non client area.
116 wnd->dwExStyle &= ~(WS_EX_CLIENTEDGE);
118 if( !(lpcs->style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
119 lphc->dwStyle |= CBS_HASSTRINGS;
120 if( !(wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) )
121 lphc->wState |= CBF_NOTIFY;
123 TRACE("[0x%08x], style = %08x\n",
124 (UINT)lphc, lphc->dwStyle );
126 return (LRESULT)(UINT)wnd->hwndSelf;
128 return (LRESULT)FALSE;
131 /***********************************************************************
132 * COMBO_NCDestroy
134 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
137 if( lphc )
139 WND* wnd = lphc->self;
141 TRACE("[%04x]: freeing storage\n", CB_HWND(lphc));
143 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
144 DestroyWindow( lphc->hWndLBox );
146 HeapFree( GetProcessHeap(), 0, lphc );
147 wnd->wExtra[0] = 0;
149 return 0;
152 /***********************************************************************
153 * CBForceDummyResize
155 * The dummy resize is used for listboxes that have a popup to trigger
156 * a re-arranging of the contents of the combobox and the recalculation
157 * of the size of the "real" control window.
159 static void CBForceDummyResize(
160 LPHEADCOMBO lphc)
162 RECT windowRect;
164 GetWindowRect(CB_HWND(lphc), &windowRect);
167 * We have to be careful, resizing a combobox also has the meaning that the
168 * dropped rect will be resized. In this case, we want to trigger a resize
169 * to recalculate layout but we don't want to change the dropped rectangle
170 * So, we add the size of the dropped rectangle to the size of the control.
171 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
172 * message.
174 SetWindowPos( CB_HWND(lphc),
175 (HWND)NULL,
176 0, 0,
177 windowRect.right - windowRect.left,
178 windowRect.bottom - windowRect.top +
179 lphc->droppedRect.bottom - lphc->droppedRect.top,
180 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
183 /***********************************************************************
184 * CBGetTextAreaHeight
186 * This method will calculate the height of the text area of the
187 * combobox.
188 * The height of the text area is set in two ways.
189 * It can be set explicitely through a combobox message of through a
190 * WM_MEASUREITEM callback.
191 * If this is not the case, the height is set to 13 dialog units.
192 * This height was determined through experimentation.
194 static INT CBGetTextAreaHeight(
195 HWND hwnd,
196 LPHEADCOMBO lphc)
198 INT iTextItemHeight;
200 if( lphc->editHeight ) /* explicitly set height */
202 iTextItemHeight = lphc->editHeight;
204 else
206 TEXTMETRICA tm;
207 HDC hDC = GetDC(hwnd);
208 HFONT hPrevFont = 0;
209 INT baseUnitY;
211 if (lphc->hFont)
212 hPrevFont = SelectObject( hDC, lphc->hFont );
214 GetTextMetricsA(hDC, &tm);
216 baseUnitY = tm.tmHeight;
218 if( hPrevFont )
219 SelectObject( hDC, hPrevFont );
221 ReleaseDC(hwnd, hDC);
223 iTextItemHeight = ((13 * baseUnitY) / 8);
226 * This "formula" calculates the height of the complete control.
227 * To calculate the height of the text area, we have to remove the
228 * borders.
230 iTextItemHeight -= 2*COMBO_YBORDERSIZE();
234 * Check the ownerdraw case if we haven't asked the parent the size
235 * of the item yet.
237 if ( CB_OWNERDRAWN(lphc) &&
238 (lphc->wState & CBF_MEASUREITEM) )
240 MEASUREITEMSTRUCT measureItem;
241 RECT clientRect;
242 INT originalItemHeight = iTextItemHeight;
245 * We use the client rect for the width of the item.
247 GetClientRect(hwnd, &clientRect);
249 lphc->wState &= ~CBF_MEASUREITEM;
252 * Send a first one to measure the size of the text area
254 measureItem.CtlType = ODT_COMBOBOX;
255 measureItem.CtlID = lphc->self->wIDmenu;
256 measureItem.itemID = -1;
257 measureItem.itemWidth = clientRect.right;
258 measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
259 measureItem.itemData = 0;
260 SendMessageA(lphc->owner, WM_MEASUREITEM,
261 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
262 iTextItemHeight = 6 + measureItem.itemHeight;
265 * Send a second one in the case of a fixed ownerdraw list to calculate the
266 * size of the list items. (we basically do this on behalf of the listbox)
268 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
270 measureItem.CtlType = ODT_COMBOBOX;
271 measureItem.CtlID = lphc->self->wIDmenu;
272 measureItem.itemID = 0;
273 measureItem.itemWidth = clientRect.right;
274 measureItem.itemHeight = originalItemHeight;
275 measureItem.itemData = 0;
276 SendMessageA(lphc->owner, WM_MEASUREITEM,
277 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
278 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
282 * Keep the size for the next time
284 lphc->editHeight = iTextItemHeight;
287 return iTextItemHeight;
291 /***********************************************************************
292 * CBCalcPlacement
294 * Set up component coordinates given valid lphc->RectCombo.
296 static void CBCalcPlacement(
297 HWND hwnd,
298 LPHEADCOMBO lphc,
299 LPRECT lprEdit,
300 LPRECT lprButton,
301 LPRECT lprLB)
304 * Again, start with the client rectangle.
306 GetClientRect(hwnd, lprEdit);
309 * Remove the borders
311 InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
314 * Chop off the bottom part to fit with the height of the text area.
316 lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
319 * The button starts the same vertical position as the text area.
321 CopyRect(lprButton, lprEdit);
324 * If the combobox is "simple" there is no button.
326 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
327 lprButton->left = lprButton->right = lprButton->bottom = 0;
328 else
331 * Let's assume the combobox button is the same width as the
332 * scrollbar button.
333 * size the button horizontally and cut-off the text area.
335 lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
336 lprEdit->right = lprButton->left;
340 * In the case of a dropdown, there is an additional spacing between the
341 * text area and the button.
343 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
345 lprEdit->right -= COMBO_EDITBUTTONSPACE();
349 * If we have an edit control, we space it away from the borders slightly.
351 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
353 InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
357 * Adjust the size of the listbox popup.
359 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
362 * Use the client rectangle to initialize the listbox rectangle
364 GetClientRect(hwnd, lprLB);
367 * Then, chop-off the top part.
369 lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
371 else
374 * Make sure the dropped width is as large as the combobox itself.
376 if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
378 lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
381 * In the case of a dropdown, the popup listbox is offset to the right.
382 * so, we want to make sure it's flush with the right side of the
383 * combobox
385 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
386 lprLB->right -= COMBO_EDITBUTTONSPACE();
388 else
389 lprLB->right = lprLB->left + lphc->droppedWidth;
392 TRACE("\ttext\t= (%i,%i-%i,%i)\n",
393 lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
395 TRACE("\tbutton\t= (%i,%i-%i,%i)\n",
396 lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
398 TRACE("\tlbox\t= (%i,%i-%i,%i)\n",
399 lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
402 /***********************************************************************
403 * CBGetDroppedControlRect
405 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
407 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
408 of the combo box and the lower right corner of the listbox */
410 GetWindowRect(lphc->self->hwndSelf, lpRect);
412 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
413 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
417 /***********************************************************************
418 * COMBO_WindowPosChanging
420 static LRESULT COMBO_WindowPosChanging(
421 HWND hwnd,
422 LPHEADCOMBO lphc,
423 WINDOWPOS* posChanging)
426 * We need to override the WM_WINDOWPOSCHANGING method to handle all
427 * the non-simple comboboxes. The problem is that those controls are
428 * always the same height. We have to make sure they are not resized
429 * to another value.
431 if ( ( CB_GETTYPE(lphc) != CBS_SIMPLE ) &&
432 ((posChanging->flags & SWP_NOSIZE) == 0) )
434 int newComboHeight;
436 newComboHeight = CBGetTextAreaHeight(hwnd,lphc) +
437 2*COMBO_YBORDERSIZE();
440 * Resizing a combobox has another side effect, it resizes the dropped
441 * rectangle as well. However, it does it only if the new height for the
442 * combobox is different than the height it should have. In other words,
443 * if the application resizing the combobox only had the intention to resize
444 * the actual control, for example, to do the layout of a dialog that is
445 * resized, the height of the dropdown is not changed.
447 if (posChanging->cy != newComboHeight)
449 lphc->droppedRect.bottom = lphc->droppedRect.top + posChanging->cy - newComboHeight;
451 posChanging->cy = newComboHeight;
455 return 0;
458 /***********************************************************************
459 * COMBO_Create
461 static LRESULT COMBO_Create( LPHEADCOMBO lphc, WND* wnd, LPARAM lParam)
463 static char clbName[] = "ComboLBox";
464 static char editName[] = "Edit";
466 LPCREATESTRUCTA lpcs = (CREATESTRUCTA*)lParam;
468 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
469 else if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
471 lphc->self = wnd;
472 lphc->owner = lpcs->hwndParent;
475 * The item height and dropped width are not set when the control
476 * is created.
478 lphc->droppedWidth = lphc->editHeight = 0;
481 * The first time we go through, we want to measure the ownerdraw item
483 lphc->wState |= CBF_MEASUREITEM;
485 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
487 if( lphc->owner || !(lpcs->style & WS_VISIBLE) )
489 UINT lbeStyle;
492 * Initialize the dropped rect to the size of the client area of the
493 * control and then, force all the areas of the combobox to be
494 * recalculated.
496 GetClientRect( wnd->hwndSelf, &lphc->droppedRect );
498 CBCalcPlacement(wnd->hwndSelf,
499 lphc,
500 &lphc->textRect,
501 &lphc->buttonRect,
502 &lphc->droppedRect );
505 * Adjust the position of the popup listbox if it's necessary
507 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
509 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
512 * If it's a dropdown, the listbox is offset
514 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
515 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
517 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect);
518 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect.right);
521 /* create listbox popup */
523 lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS) |
524 (lpcs->style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
526 if( lphc->dwStyle & CBS_SORT )
527 lbeStyle |= LBS_SORT;
528 if( lphc->dwStyle & CBS_HASSTRINGS )
529 lbeStyle |= LBS_HASSTRINGS;
530 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
531 lbeStyle |= LBS_NOINTEGRALHEIGHT;
532 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
533 lbeStyle |= LBS_DISABLENOSCROLL;
535 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
536 lbeStyle |= WS_CHILD | WS_VISIBLE;
537 else /* popup listbox */
538 lbeStyle |= WS_POPUP;
540 /* Dropdown ComboLBox is not a child window and we cannot pass
541 * ID_CB_LISTBOX directly because it will be treated as a menu handle.
543 lphc->hWndLBox = CreateWindowExA(0,
544 clbName,
545 NULL,
546 lbeStyle,
547 lphc->droppedRect.left,
548 lphc->droppedRect.top,
549 lphc->droppedRect.right - lphc->droppedRect.left,
550 lphc->droppedRect.bottom - lphc->droppedRect.top,
551 lphc->self->hwndSelf,
552 (lphc->dwStyle & CBS_DROPDOWN)? (HMENU)0 : (HMENU)ID_CB_LISTBOX,
553 lphc->self->hInstance,
554 (LPVOID)lphc );
557 * The ComboLBox is a strange little beast (when it's not a CBS_SIMPLE)...
558 * It's a popup window but, when you get the window style, you get WS_CHILD.
559 * When created, it's parent is the combobox but, when you ask for it's parent
560 * after that, you're supposed to get the desktop. (see MFC code function
561 * AfxCancelModes)
562 * To achieve this in Wine, we have to create it as a popup and change
563 * it's style to child after the creation.
565 if ( (lphc->hWndLBox!= 0) &&
566 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
568 SetWindowLongA(lphc->hWndLBox,
569 GWL_STYLE,
570 (GetWindowLongA(lphc->hWndLBox, GWL_STYLE) | WS_CHILD) & ~WS_POPUP);
573 if( lphc->hWndLBox )
575 BOOL bEdit = TRUE;
576 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT;
579 * In Win95 look, the border fo the edit control is
580 * provided by the combobox
582 if (TWEAK_WineLook == WIN31_LOOK)
583 lbeStyle |= WS_BORDER;
585 if( lphc->wState & CBF_EDIT )
587 if( lphc->dwStyle & CBS_OEMCONVERT )
588 lbeStyle |= ES_OEMCONVERT;
589 if( lphc->dwStyle & CBS_AUTOHSCROLL )
590 lbeStyle |= ES_AUTOHSCROLL;
591 if( lphc->dwStyle & CBS_LOWERCASE )
592 lbeStyle |= ES_LOWERCASE;
593 else if( lphc->dwStyle & CBS_UPPERCASE )
594 lbeStyle |= ES_UPPERCASE;
596 lphc->hWndEdit = CreateWindowExA(0,
597 editName,
598 NULL,
599 lbeStyle,
600 lphc->textRect.left, lphc->textRect.top,
601 lphc->textRect.right - lphc->textRect.left,
602 lphc->textRect.bottom - lphc->textRect.top,
603 lphc->self->hwndSelf,
604 (HMENU)ID_CB_EDIT,
605 lphc->self->hInstance,
606 NULL );
608 if( !lphc->hWndEdit )
609 bEdit = FALSE;
612 if( bEdit )
615 * If the combo is a dropdown, we must resize the control to fit only
616 * the text area and button. To do this, we send a dummy resize and the
617 * WM_WINDOWPOSCHANGING message will take care of setting the height for
618 * us.
620 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
622 CBForceDummyResize(lphc);
625 TRACE("init done\n");
626 return wnd->hwndSelf;
628 ERR("edit control failure.\n");
629 } else ERR("listbox failure.\n");
630 } else ERR("no owner for visible combo.\n");
632 /* CreateWindow() will send WM_NCDESTROY to cleanup */
634 return -1;
637 /***********************************************************************
638 * CBPaintButton
640 * Paint combo button (normal, pressed, and disabled states).
642 static void CBPaintButton(
643 LPHEADCOMBO lphc,
644 HDC hdc,
645 RECT rectButton)
647 UINT x, y;
648 BOOL bBool;
649 HDC hMemDC;
650 HBRUSH hPrevBrush;
651 COLORREF oldTextColor, oldBkColor;
653 if( lphc->wState & CBF_NOREDRAW )
654 return;
656 hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
659 * Draw the button background
661 PatBlt( hdc,
662 rectButton.left,
663 rectButton.top,
664 rectButton.right-rectButton.left,
665 rectButton.bottom-rectButton.top,
666 PATCOPY );
668 if( (bBool = lphc->wState & CBF_BUTTONDOWN) )
670 DrawEdge( hdc, &rectButton, EDGE_SUNKEN, BF_RECT );
672 else
674 DrawEdge( hdc, &rectButton, EDGE_RAISED, BF_RECT );
678 * Remove the edge of the button from the rectangle
679 * and calculate the position of the bitmap.
681 InflateRect( &rectButton, -2, -2);
683 x = (rectButton.left + rectButton.right - CBitWidth) >> 1;
684 y = (rectButton.top + rectButton.bottom - CBitHeight) >> 1;
687 hMemDC = CreateCompatibleDC( hdc );
688 SelectObject( hMemDC, hComboBmp );
689 oldTextColor = SetTextColor( hdc, GetSysColor(COLOR_BTNFACE) );
690 oldBkColor = SetBkColor( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) :
691 RGB(0,0,0) );
692 BitBlt( hdc, x, y, CBitWidth, CBitHeight, hMemDC, 0, 0, SRCCOPY );
693 SetBkColor( hdc, oldBkColor );
694 SetTextColor( hdc, oldTextColor );
695 DeleteDC( hMemDC );
696 SelectObject( hdc, hPrevBrush );
699 /***********************************************************************
700 * CBPaintText
702 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
704 static void CBPaintText(
705 LPHEADCOMBO lphc,
706 HDC hdc,
707 RECT rectEdit)
709 INT id, size = 0;
710 LPSTR pText = NULL;
712 if( lphc->wState & CBF_NOREDRAW ) return;
714 /* follow Windows combobox that sends a bunch of text
715 * inquiries to its listbox while processing WM_PAINT. */
717 if( (id = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
719 size = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
720 if( (pText = HeapAlloc( GetProcessHeap(), 0, size + 1)) )
722 SendMessageA( lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText );
723 pText[size] = '\0'; /* just in case */
724 } else return;
727 if( lphc->wState & CBF_EDIT )
729 if( CB_HASSTRINGS(lphc) ) SetWindowTextA( lphc->hWndEdit, pText ? pText : "" );
730 if( lphc->wState & CBF_FOCUSED )
731 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
733 else /* paint text field ourselves */
735 HBRUSH hPrevBrush = 0;
736 HDC hDC = hdc;
738 if( !hDC )
740 if ((hDC = GetDC(lphc->self->hwndSelf)))
742 HBRUSH hBrush = SendMessageA( lphc->owner,
743 WM_CTLCOLORLISTBOX,
744 hDC, lphc->self->hwndSelf );
745 hPrevBrush = SelectObject( hDC,
746 (hBrush) ? hBrush : GetStockObject(WHITE_BRUSH) );
749 if( hDC )
751 UINT itemState;
752 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hDC, lphc->hFont) : 0;
755 * Give ourselves some space.
757 InflateRect( &rectEdit, -1, -1 );
759 if ( (lphc->wState & CBF_FOCUSED) &&
760 !(lphc->wState & CBF_DROPPED) )
762 /* highlight */
764 FillRect( hDC, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
765 SetBkColor( hDC, GetSysColor( COLOR_HIGHLIGHT ) );
766 SetTextColor( hDC, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
767 itemState = ODS_SELECTED | ODS_FOCUS;
769 else
770 itemState = 0;
772 if( CB_OWNERDRAWN(lphc) )
774 DRAWITEMSTRUCT dis;
775 HRGN clipRegion;
778 * Save the current clip region.
779 * To retrieve the clip region, we need to create one "dummy"
780 * clip region.
782 clipRegion = CreateRectRgnIndirect(&rectEdit);
784 if (GetClipRgn(hDC, clipRegion)!=1)
786 DeleteObject(clipRegion);
787 clipRegion=(HRGN)NULL;
790 if ( lphc->self->dwStyle & WS_DISABLED )
791 itemState |= ODS_DISABLED;
793 dis.CtlType = ODT_COMBOBOX;
794 dis.CtlID = lphc->self->wIDmenu;
795 dis.hwndItem = lphc->self->hwndSelf;
796 dis.itemAction = ODA_DRAWENTIRE;
797 dis.itemID = id;
798 dis.itemState = itemState;
799 dis.hDC = hDC;
800 dis.rcItem = rectEdit;
801 dis.itemData = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA,
802 (WPARAM)id, 0 );
805 * Clip the DC and have the parent draw the item.
807 IntersectClipRect(hDC,
808 rectEdit.left, rectEdit.top,
809 rectEdit.right, rectEdit.bottom);
811 SendMessageA(lphc->owner, WM_DRAWITEM,
812 lphc->self->wIDmenu, (LPARAM)&dis );
815 * Reset the clipping region.
817 SelectClipRgn(hDC, clipRegion);
819 else
821 ExtTextOutA( hDC,
822 rectEdit.left + 1,
823 rectEdit.top + 1,
824 ETO_OPAQUE | ETO_CLIPPED,
825 &rectEdit,
826 pText ? pText : "" , size, NULL );
828 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
829 DrawFocusRect( hDC, &rectEdit );
832 if( hPrevFont )
833 SelectObject(hDC, hPrevFont );
835 if( !hdc )
837 if( hPrevBrush )
838 SelectObject( hDC, hPrevBrush );
840 ReleaseDC( lphc->self->hwndSelf, hDC );
844 if (pText)
845 HeapFree( GetProcessHeap(), 0, pText );
848 /***********************************************************************
849 * CBPaintBorder
851 static void CBPaintBorder(
852 HWND hwnd,
853 LPHEADCOMBO lphc,
854 HDC hdc)
856 RECT clientRect;
858 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
860 GetClientRect(hwnd, &clientRect);
862 else
864 CopyRect(&clientRect, &lphc->textRect);
866 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
867 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
870 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
873 /***********************************************************************
874 * COMBO_EraseBackground
876 static LRESULT COMBO_EraseBackground(
877 HWND hwnd,
878 LPHEADCOMBO lphc,
879 HDC hParamDC)
881 HBRUSH hBkgBrush;
882 RECT clientRect;
883 HDC hDC;
885 hDC = (hParamDC) ? hParamDC
886 : GetDC(hwnd);
889 * Calculate the area that we want to erase.
891 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
893 GetClientRect(hwnd, &clientRect);
895 else
897 CopyRect(&clientRect, &lphc->textRect);
899 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
902 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
903 hDC, hwnd);
905 if( !hBkgBrush )
906 hBkgBrush = GetStockObject(WHITE_BRUSH);
908 FillRect(hDC, &clientRect, hBkgBrush);
910 if (!hParamDC)
911 ReleaseDC(hwnd, hDC);
913 return TRUE;
916 /***********************************************************************
917 * COMBO_Paint
919 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
921 PAINTSTRUCT ps;
922 HDC hDC;
924 hDC = (hParamDC) ? hParamDC
925 : BeginPaint( lphc->self->hwndSelf, &ps);
928 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
930 HBRUSH hPrevBrush, hBkgBrush;
932 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
933 hDC, lphc->self->hwndSelf );
935 if( !hBkgBrush )
936 hBkgBrush = GetStockObject(WHITE_BRUSH);
938 hPrevBrush = SelectObject( hDC, hBkgBrush );
941 * In non 3.1 look, there is a sunken border on the combobox
943 if (TWEAK_WineLook != WIN31_LOOK)
945 CBPaintBorder(CB_HWND(lphc), lphc, hDC);
948 if( !IsRectEmpty(&lphc->buttonRect) )
950 CBPaintButton(lphc, hDC, lphc->buttonRect);
953 if( !(lphc->wState & CBF_EDIT) )
956 * The text area has a border only in Win 3.1 look.
958 if (TWEAK_WineLook == WIN31_LOOK)
960 HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
962 Rectangle( hDC,
963 lphc->textRect.left, lphc->textRect.top,
964 lphc->textRect.right - 1, lphc->textRect.bottom - 1);
966 SelectObject( hDC, hPrevPen );
969 CBPaintText( lphc, hDC, lphc->textRect);
972 if( hPrevBrush )
973 SelectObject( hDC, hPrevBrush );
976 if( !hParamDC )
977 EndPaint(lphc->self->hwndSelf, &ps);
979 return 0;
982 /***********************************************************************
983 * CBUpdateLBox
985 * Select listbox entry according to the contents of the edit control.
987 static INT CBUpdateLBox( LPHEADCOMBO lphc )
989 INT length, idx, ret;
990 LPSTR pText = NULL;
992 idx = ret = LB_ERR;
993 length = CB_GETEDITTEXTLENGTH( lphc );
995 if( length > 0 )
996 pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1);
998 TRACE("\t edit text length %i\n", length );
1000 if( pText )
1002 if( length ) GetWindowTextA( lphc->hWndEdit, pText, length + 1);
1003 else pText[0] = '\0';
1004 idx = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
1005 (WPARAM)(-1), (LPARAM)pText );
1006 if( idx == LB_ERR ) idx = 0; /* select first item */
1007 else ret = idx;
1008 HeapFree( GetProcessHeap(), 0, pText );
1011 /* select entry */
1013 SendMessageA( lphc->hWndLBox, LB_SETCURSEL, (WPARAM)idx, 0 );
1015 if( idx >= 0 )
1017 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)idx, 0 );
1018 /* probably superfluous but Windows sends this too */
1019 SendMessageA( lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)idx, 0 );
1021 return ret;
1024 /***********************************************************************
1025 * CBUpdateEdit
1027 * Copy a listbox entry to the edit control.
1029 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
1031 INT length;
1032 LPSTR pText = NULL;
1034 TRACE("\t %i\n", index );
1036 if( index == -1 )
1038 length = CB_GETEDITTEXTLENGTH( lphc );
1039 if( length )
1041 if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
1043 GetWindowTextA( lphc->hWndEdit, pText, length + 1 );
1044 index = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
1045 (WPARAM)(-1), (LPARAM)pText );
1046 HeapFree( GetProcessHeap(), 0, pText );
1051 if( index >= 0 ) /* got an entry */
1053 length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1054 if( length )
1056 if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
1058 SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1059 (WPARAM)index, (LPARAM)pText );
1061 lphc->wState |= CBF_NOEDITNOTIFY;
1063 SendMessageA( lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)pText );
1064 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1065 HeapFree( GetProcessHeap(), 0, pText );
1071 /***********************************************************************
1072 * CBDropDown
1074 * Show listbox popup.
1076 static void CBDropDown( LPHEADCOMBO lphc )
1078 RECT rect;
1080 TRACE("[%04x]: drop down\n", CB_HWND(lphc));
1082 CB_NOTIFY( lphc, CBN_DROPDOWN );
1084 /* set selection */
1086 lphc->wState |= CBF_DROPPED;
1087 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1089 lphc->droppedIndex = CBUpdateLBox( lphc );
1091 if( !(lphc->wState & CBF_CAPTURE) )
1092 CBUpdateEdit( lphc, lphc->droppedIndex );
1094 else
1096 lphc->droppedIndex = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1098 if( lphc->droppedIndex == LB_ERR )
1099 lphc->droppedIndex = 0;
1101 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)lphc->droppedIndex, 0 );
1102 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1105 /* now set popup position */
1106 GetWindowRect( lphc->self->hwndSelf, &rect );
1110 * If it's a dropdown, the listbox is offset
1112 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1113 rect.left += COMBO_EDITBUTTONSPACE();
1115 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1116 lphc->droppedRect.right - lphc->droppedRect.left,
1117 lphc->droppedRect.bottom - lphc->droppedRect.top,
1118 SWP_NOACTIVATE | SWP_NOREDRAW);
1120 if( !(lphc->wState & CBF_NOREDRAW) )
1121 RedrawWindow( lphc->self->hwndSelf, NULL, 0, RDW_INVALIDATE |
1122 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1124 ShowWindow( lphc->hWndLBox, SW_SHOWNA );
1127 /***********************************************************************
1128 * CBRollUp
1130 * Hide listbox popup.
1132 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1134 HWND hWnd = lphc->self->hwndSelf;
1136 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1138 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1141 TRACE("[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
1144 * It seems useful to send the WM_LBUTTONUP with (-1,-1) when cancelling
1145 * and with (0,0) (anywhere in the listbox) when Oking.
1147 SendMessageA( lphc->hWndLBox, WM_LBUTTONUP, 0, ok ? (LPARAM)0 : (LPARAM)(-1) );
1149 if( lphc->wState & CBF_DROPPED )
1151 RECT rect;
1153 lphc->wState &= ~CBF_DROPPED;
1154 ShowWindow( lphc->hWndLBox, SW_HIDE );
1156 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1158 INT index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1159 CBUpdateEdit( lphc, index );
1160 rect = lphc->buttonRect;
1162 else
1164 if( bButton )
1166 UnionRect( &rect,
1167 &lphc->buttonRect,
1168 &lphc->textRect);
1170 else
1171 rect = lphc->textRect;
1173 bButton = TRUE;
1176 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1177 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1178 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1179 CB_NOTIFY( lphc, CBN_CLOSEUP );
1184 /***********************************************************************
1185 * COMBO_FlipListbox
1187 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1189 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL bRedrawButton )
1191 if( lphc->wState & CBF_DROPPED )
1193 CBRollUp( lphc, TRUE, bRedrawButton );
1194 return FALSE;
1197 CBDropDown( lphc );
1198 return TRUE;
1201 /***********************************************************************
1202 * COMBO_GetLBWindow
1204 * Edit control helper.
1206 HWND COMBO_GetLBWindow( WND* pWnd )
1208 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1209 if( lphc ) return lphc->hWndLBox;
1210 return 0;
1214 /***********************************************************************
1215 * CBRepaintButton
1217 static void CBRepaintButton( LPHEADCOMBO lphc )
1219 InvalidateRect(CB_HWND(lphc), &lphc->buttonRect, TRUE);
1220 UpdateWindow(CB_HWND(lphc));
1223 /***********************************************************************
1224 * COMBO_SetFocus
1226 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1228 if( !(lphc->wState & CBF_FOCUSED) )
1230 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1231 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1233 if( lphc->wState & CBF_EDIT )
1234 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1235 lphc->wState |= CBF_FOCUSED;
1236 if( !(lphc->wState & CBF_EDIT) )
1238 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1241 CB_NOTIFY( lphc, CBN_SETFOCUS );
1245 /***********************************************************************
1246 * COMBO_KillFocus
1248 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1250 HWND hWnd = lphc->self->hwndSelf;
1252 if( lphc->wState & CBF_FOCUSED )
1254 SendMessageA( hWnd, WM_LBUTTONUP, 0, (LPARAM)(-1) );
1256 CBRollUp( lphc, FALSE, TRUE );
1257 if( IsWindow( hWnd ) )
1259 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1260 SendMessageA( lphc->hWndLBox, LB_CARETOFF, 0, 0 );
1262 lphc->wState &= ~CBF_FOCUSED;
1264 /* redraw text */
1265 if( lphc->wState & CBF_EDIT )
1266 SendMessageA( lphc->hWndEdit, EM_SETSEL, (WPARAM)(-1), 0 );
1267 else
1269 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1272 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1277 /***********************************************************************
1278 * COMBO_Command
1280 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1282 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1284 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1286 switch( HIWORD(wParam) >> 8 )
1288 case (EN_SETFOCUS >> 8):
1290 TRACE("[%04x]: edit [%04x] got focus\n",
1291 CB_HWND(lphc), lphc->hWndEdit );
1293 if( !(lphc->wState & CBF_FOCUSED) ) COMBO_SetFocus( lphc );
1294 break;
1296 case (EN_KILLFOCUS >> 8):
1298 TRACE("[%04x]: edit [%04x] lost focus\n",
1299 CB_HWND(lphc), lphc->hWndEdit );
1301 /* NOTE: it seems that Windows' edit control sends an
1302 * undocumented message WM_USER + 0x1B instead of this
1303 * notification (only when it happens to be a part of
1304 * the combo). ?? - AK.
1307 COMBO_KillFocus( lphc );
1308 break;
1311 case (EN_CHANGE >> 8):
1313 * In some circumstances (when the selection of the combobox
1314 * is changed for example) we don't wans the EN_CHANGE notification
1315 * to be forwarded to the parent of the combobox. This code
1316 * checks a flag that is set in these occasions and ignores the
1317 * notification.
1319 if (lphc->wState & CBF_NOEDITNOTIFY)
1321 lphc->wState &= ~CBF_NOEDITNOTIFY;
1323 else
1325 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1328 CBUpdateLBox( lphc );
1329 break;
1331 case (EN_UPDATE >> 8):
1332 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1333 break;
1335 case (EN_ERRSPACE >> 8):
1336 CB_NOTIFY( lphc, CBN_ERRSPACE );
1339 else if( lphc->hWndLBox == hWnd )
1341 switch( HIWORD(wParam) )
1343 case LBN_ERRSPACE:
1344 CB_NOTIFY( lphc, CBN_ERRSPACE );
1345 break;
1347 case LBN_DBLCLK:
1348 CB_NOTIFY( lphc, CBN_DBLCLK );
1349 break;
1351 case LBN_SELCHANGE:
1352 case LBN_SELCANCEL:
1354 TRACE("[%04x]: lbox selection change [%04x]\n",
1355 CB_HWND(lphc), lphc->wState );
1357 /* do not roll up if selection is being tracked
1358 * by arrowkeys in the dropdown listbox */
1360 if( (lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP) )
1361 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1362 else lphc->wState &= ~CBF_NOROLLUP;
1364 CB_NOTIFY( lphc, CBN_SELCHANGE );
1365 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1366 /* fall through */
1368 case LBN_SETFOCUS:
1369 case LBN_KILLFOCUS:
1370 /* nothing to do here since ComboLBox always resets the focus to its
1371 * combo/edit counterpart */
1372 break;
1375 return 0;
1378 /***********************************************************************
1379 * COMBO_ItemOp
1381 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1383 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg,
1384 WPARAM wParam, LPARAM lParam )
1386 HWND hWnd = lphc->self->hwndSelf;
1388 TRACE("[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
1390 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
1392 /* two first items are the same in all 4 structs */
1393 lpIS->CtlType = ODT_COMBOBOX;
1394 lpIS->CtlID = lphc->self->wIDmenu;
1396 switch( msg ) /* patch window handle */
1398 case WM_DELETEITEM:
1399 lpIS->hwndItem = hWnd;
1400 #undef lpIS
1401 break;
1402 case WM_DRAWITEM:
1403 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
1404 lpIS->hwndItem = hWnd;
1405 #undef lpIS
1406 break;
1407 case WM_COMPAREITEM:
1408 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
1409 lpIS->hwndItem = hWnd;
1410 #undef lpIS
1411 break;
1414 return SendMessageA( lphc->owner, msg, lphc->self->wIDmenu, lParam );
1417 /***********************************************************************
1418 * COMBO_GetText
1420 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT N, LPSTR lpText)
1422 if( lphc->wState & CBF_EDIT )
1423 return SendMessageA( lphc->hWndEdit, WM_GETTEXT,
1424 (WPARAM)N, (LPARAM)lpText );
1426 /* get it from the listbox */
1428 if( lphc->hWndLBox )
1430 INT idx = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1431 if( idx != LB_ERR )
1433 LPSTR lpBuffer;
1434 INT length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN,
1435 (WPARAM)idx, 0 );
1437 /* 'length' is without the terminating character */
1438 if( length >= N )
1439 lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 );
1440 else
1441 lpBuffer = lpText;
1443 if( lpBuffer )
1445 INT n = SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1446 (WPARAM)idx, (LPARAM)lpBuffer );
1448 /* truncate if buffer is too short */
1450 if( length >= N )
1452 if (N && lpText) {
1453 if( n != LB_ERR ) memcpy( lpText, lpBuffer, (N>n) ? n+1 : N-1 );
1454 lpText[N - 1] = '\0';
1456 HeapFree( GetProcessHeap(), 0, lpBuffer );
1458 return (LRESULT)n;
1462 return 0;
1466 /***********************************************************************
1467 * CBResetPos
1469 * This function sets window positions according to the updated
1470 * component placement struct.
1472 static void CBResetPos(
1473 LPHEADCOMBO lphc,
1474 LPRECT rectEdit,
1475 LPRECT rectLB,
1476 BOOL bRedraw)
1478 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1480 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1481 * sizing messages */
1483 if( lphc->wState & CBF_EDIT )
1484 SetWindowPos( lphc->hWndEdit, 0,
1485 rectEdit->left, rectEdit->top,
1486 rectEdit->right - rectEdit->left,
1487 rectEdit->bottom - rectEdit->top,
1488 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1490 SetWindowPos( lphc->hWndLBox, 0,
1491 rectLB->left, rectLB->top,
1492 rectLB->right - rectLB->left,
1493 rectLB->bottom - rectLB->top,
1494 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1496 if( bDrop )
1498 if( lphc->wState & CBF_DROPPED )
1500 lphc->wState &= ~CBF_DROPPED;
1501 ShowWindow( lphc->hWndLBox, SW_HIDE );
1504 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1505 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
1506 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1511 /***********************************************************************
1512 * COMBO_Size
1514 static void COMBO_Size( LPHEADCOMBO lphc )
1516 CBCalcPlacement(lphc->self->hwndSelf,
1517 lphc,
1518 &lphc->textRect,
1519 &lphc->buttonRect,
1520 &lphc->droppedRect);
1522 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1526 /***********************************************************************
1527 * COMBO_Font
1529 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1532 * Set the font
1534 lphc->hFont = hFont;
1537 * Propagate to owned windows.
1539 if( lphc->wState & CBF_EDIT )
1540 SendMessageA( lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw );
1541 SendMessageA( lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw );
1544 * Redo the layout of the control.
1546 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1548 CBCalcPlacement(lphc->self->hwndSelf,
1549 lphc,
1550 &lphc->textRect,
1551 &lphc->buttonRect,
1552 &lphc->droppedRect);
1554 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1556 else
1558 CBForceDummyResize(lphc);
1563 /***********************************************************************
1564 * COMBO_SetItemHeight
1566 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1568 LRESULT lRet = CB_ERR;
1570 if( index == -1 ) /* set text field height */
1572 if( height < 32768 )
1574 lphc->editHeight = height;
1577 * Redo the layout of the control.
1579 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1581 CBCalcPlacement(lphc->self->hwndSelf,
1582 lphc,
1583 &lphc->textRect,
1584 &lphc->buttonRect,
1585 &lphc->droppedRect);
1587 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1589 else
1591 CBForceDummyResize(lphc);
1594 lRet = height;
1597 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1598 lRet = SendMessageA( lphc->hWndLBox, LB_SETITEMHEIGHT,
1599 (WPARAM)index, (LPARAM)height );
1600 return lRet;
1603 /***********************************************************************
1604 * COMBO_SelectString
1606 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPCSTR pText )
1608 INT index = SendMessageA( lphc->hWndLBox, LB_SELECTSTRING,
1609 (WPARAM)start, (LPARAM)pText );
1610 if( index >= 0 )
1612 if( lphc->wState & CBF_EDIT )
1613 CBUpdateEdit( lphc, index );
1614 else
1616 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1619 return (LRESULT)index;
1622 /***********************************************************************
1623 * COMBO_LButtonDown
1625 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1627 POINT pt;
1628 BOOL bButton;
1629 HWND hWnd = lphc->self->hwndSelf;
1631 pt.x = LOWORD(lParam);
1632 pt.y = HIWORD(lParam);
1633 bButton = PtInRect(&lphc->buttonRect, pt);
1635 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1636 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1638 lphc->wState |= CBF_BUTTONDOWN;
1639 if( lphc->wState & CBF_DROPPED )
1641 /* got a click to cancel selection */
1643 lphc->wState &= ~CBF_BUTTONDOWN;
1644 CBRollUp( lphc, TRUE, FALSE );
1645 if( !IsWindow( hWnd ) ) return;
1647 if( lphc->wState & CBF_CAPTURE )
1649 lphc->wState &= ~CBF_CAPTURE;
1650 ReleaseCapture();
1653 else
1655 /* drop down the listbox and start tracking */
1657 lphc->wState |= CBF_CAPTURE;
1658 CBDropDown( lphc );
1659 SetCapture( hWnd );
1661 if( bButton ) CBRepaintButton( lphc );
1665 /***********************************************************************
1666 * COMBO_LButtonUp
1668 * Release capture and stop tracking if needed.
1670 static void COMBO_LButtonUp( LPHEADCOMBO lphc, LPARAM lParam )
1672 if( lphc->wState & CBF_CAPTURE )
1674 lphc->wState &= ~CBF_CAPTURE;
1675 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1677 INT index = CBUpdateLBox( lphc );
1678 CBUpdateEdit( lphc, index );
1680 ReleaseCapture();
1683 if( lphc->wState & CBF_BUTTONDOWN )
1685 lphc->wState &= ~CBF_BUTTONDOWN;
1686 CBRepaintButton( lphc );
1690 /***********************************************************************
1691 * COMBO_MouseMove
1693 * Two things to do - track combo button and release capture when
1694 * pointer goes into the listbox.
1696 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1698 POINT pt;
1699 RECT lbRect;
1701 pt.x = LOWORD(lParam);
1702 pt.y = HIWORD(lParam);
1704 if( lphc->wState & CBF_BUTTONDOWN )
1706 BOOL bButton;
1708 bButton = PtInRect(&lphc->buttonRect, pt);
1710 if( !bButton )
1712 lphc->wState &= ~CBF_BUTTONDOWN;
1713 CBRepaintButton( lphc );
1717 GetClientRect( lphc->hWndLBox, &lbRect );
1718 MapWindowPoints( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 );
1719 if( PtInRect(&lbRect, pt) )
1721 lphc->wState &= ~CBF_CAPTURE;
1722 ReleaseCapture();
1723 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc );
1725 /* hand over pointer tracking */
1726 SendMessageA( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam );
1731 /***********************************************************************
1732 * ComboWndProc_locked
1734 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1736 static inline LRESULT WINAPI ComboWndProc_locked( WND* pWnd, UINT message,
1737 WPARAM wParam, LPARAM lParam )
1739 if( pWnd ) {
1740 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1741 HWND hwnd = pWnd->hwndSelf;
1743 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1744 pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
1746 if( lphc || message == WM_NCCREATE )
1747 switch(message)
1750 /* System messages */
1752 case WM_NCCREATE:
1753 return COMBO_NCCreate(pWnd, lParam);
1754 case WM_NCDESTROY:
1755 COMBO_NCDestroy(lphc);
1756 break;/* -> DefWindowProc */
1758 case WM_CREATE:
1759 return COMBO_Create(lphc, pWnd, lParam);
1761 case WM_PRINTCLIENT:
1762 if (lParam & PRF_ERASEBKGND)
1763 COMBO_EraseBackground(hwnd, lphc, wParam);
1765 /* Fallthrough */
1766 case WM_PAINT:
1767 /* wParam may contain a valid HDC! */
1768 return COMBO_Paint(lphc, wParam);
1769 case WM_ERASEBKGND:
1770 return COMBO_EraseBackground(hwnd, lphc, wParam);
1771 case WM_GETDLGCODE:
1772 return (LRESULT)(DLGC_WANTARROWS | DLGC_WANTCHARS);
1773 case WM_WINDOWPOSCHANGING:
1774 return COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1775 case WM_SIZE:
1776 if( lphc->hWndLBox &&
1777 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1778 return TRUE;
1779 case WM_SETFONT:
1780 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1781 return TRUE;
1782 case WM_GETFONT:
1783 return (LRESULT)lphc->hFont;
1784 case WM_SETFOCUS:
1785 if( lphc->wState & CBF_EDIT )
1786 SetFocus( lphc->hWndEdit );
1787 else
1788 COMBO_SetFocus( lphc );
1789 return TRUE;
1790 case WM_KILLFOCUS:
1791 #define hwndFocus ((HWND16)wParam)
1792 if( !hwndFocus ||
1793 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1794 COMBO_KillFocus( lphc );
1795 #undef hwndFocus
1796 return TRUE;
1797 case WM_COMMAND:
1798 return COMBO_Command( lphc, wParam, (HWND)lParam );
1799 case WM_GETTEXT:
1800 return COMBO_GetText( lphc, (UINT)wParam, (LPSTR)lParam );
1801 case WM_SETTEXT:
1802 case WM_GETTEXTLENGTH:
1803 case WM_CLEAR:
1804 case WM_CUT:
1805 case WM_PASTE:
1806 case WM_COPY:
1807 if( lphc->wState & CBF_EDIT )
1809 lphc->wState |= CBF_NOEDITNOTIFY;
1811 return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1813 return CB_ERR;
1814 case WM_DRAWITEM:
1815 case WM_DELETEITEM:
1816 case WM_COMPAREITEM:
1817 case WM_MEASUREITEM:
1818 return COMBO_ItemOp( lphc, message, wParam, lParam );
1819 case WM_ENABLE:
1820 if( lphc->wState & CBF_EDIT )
1821 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1822 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1823 return TRUE;
1824 case WM_SETREDRAW:
1825 if( wParam )
1826 lphc->wState &= ~CBF_NOREDRAW;
1827 else
1828 lphc->wState |= CBF_NOREDRAW;
1830 if( lphc->wState & CBF_EDIT )
1831 SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1832 SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1833 return 0;
1834 case WM_SYSKEYDOWN:
1835 if( KEYDATA_ALT & HIWORD(lParam) )
1836 if( wParam == VK_UP || wParam == VK_DOWN )
1837 COMBO_FlipListbox( lphc, TRUE );
1838 break;/* -> DefWindowProc */
1840 case WM_CHAR:
1841 case WM_KEYDOWN:
1842 if( lphc->wState & CBF_EDIT )
1843 return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1844 else
1845 return SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1846 case WM_LBUTTONDOWN:
1847 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf );
1848 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1849 return TRUE;
1850 case WM_LBUTTONUP:
1851 COMBO_LButtonUp( lphc, lParam );
1852 return TRUE;
1853 case WM_MOUSEMOVE:
1854 if( lphc->wState & CBF_CAPTURE )
1855 COMBO_MouseMove( lphc, wParam, lParam );
1856 return TRUE;
1857 /* Combo messages */
1859 case CB_ADDSTRING16:
1860 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1861 case CB_ADDSTRING:
1862 return SendMessageA( lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1863 case CB_INSERTSTRING16:
1864 wParam = (INT)(INT16)wParam;
1865 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1866 case CB_INSERTSTRING:
1867 return SendMessageA( lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1868 case CB_DELETESTRING16:
1869 case CB_DELETESTRING:
1870 return SendMessageA( lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1871 case CB_SELECTSTRING16:
1872 wParam = (INT)(INT16)wParam;
1873 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1874 case CB_SELECTSTRING:
1875 return COMBO_SelectString( lphc, (INT)wParam, (LPSTR)lParam );
1876 case CB_FINDSTRING16:
1877 wParam = (INT)(INT16)wParam;
1878 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1879 case CB_FINDSTRING:
1880 return SendMessageA( lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1881 case CB_FINDSTRINGEXACT16:
1882 wParam = (INT)(INT16)wParam;
1883 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1884 case CB_FINDSTRINGEXACT:
1885 return SendMessageA( lphc->hWndLBox, LB_FINDSTRINGEXACT,
1886 wParam, lParam );
1887 case CB_SETITEMHEIGHT16:
1888 wParam = (INT)(INT16)wParam; /* signed integer */
1889 case CB_SETITEMHEIGHT:
1890 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1891 case CB_GETITEMHEIGHT16:
1892 wParam = (INT)(INT16)wParam;
1893 case CB_GETITEMHEIGHT:
1894 if( (INT)wParam >= 0 ) /* listbox item */
1895 return SendMessageA( lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1896 return CBGetTextAreaHeight(hwnd, lphc);
1897 case CB_RESETCONTENT16:
1898 case CB_RESETCONTENT:
1899 SendMessageA( lphc->hWndLBox, LB_RESETCONTENT, 0, 0 );
1900 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
1901 return TRUE;
1902 case CB_INITSTORAGE:
1903 return SendMessageA( lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
1904 case CB_GETHORIZONTALEXTENT:
1905 return SendMessageA( lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
1906 case CB_SETHORIZONTALEXTENT:
1907 return SendMessageA( lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
1908 case CB_GETTOPINDEX:
1909 return SendMessageA( lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
1910 case CB_GETLOCALE:
1911 return SendMessageA( lphc->hWndLBox, LB_GETLOCALE, 0, 0);
1912 case CB_SETLOCALE:
1913 return SendMessageA( lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
1914 case CB_GETDROPPEDWIDTH:
1915 if( lphc->droppedWidth )
1916 return lphc->droppedWidth;
1917 return lphc->droppedRect.right - lphc->droppedRect.left;
1918 case CB_SETDROPPEDWIDTH:
1919 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
1920 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
1921 return CB_ERR;
1922 case CB_GETDROPPEDCONTROLRECT16:
1923 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1924 if( lParam )
1926 RECT r;
1927 CBGetDroppedControlRect( lphc, &r );
1928 CONV_RECT32TO16( &r, (LPRECT16)lParam );
1930 return CB_OKAY;
1931 case CB_GETDROPPEDCONTROLRECT:
1932 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
1933 return CB_OKAY;
1934 case CB_GETDROPPEDSTATE16:
1935 case CB_GETDROPPEDSTATE:
1936 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
1937 case CB_DIR16:
1938 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1939 /* fall through */
1940 case CB_DIR:
1941 return COMBO_Directory( lphc, (UINT)wParam,
1942 (LPSTR)lParam, (message == CB_DIR));
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 return TRUE;
1957 case CB_GETCOUNT16:
1958 case CB_GETCOUNT:
1959 return SendMessageA( lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1960 case CB_GETCURSEL16:
1961 case CB_GETCURSEL:
1962 return SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1963 case CB_SETCURSEL16:
1964 wParam = (INT)(INT16)wParam;
1965 case CB_SETCURSEL:
1966 lParam = SendMessageA( lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
1967 if( lphc->wState & CBF_SELCHANGE )
1969 /* no LBN_SELCHANGE in this case, update manually */
1970 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1971 lphc->wState &= ~CBF_SELCHANGE;
1973 return lParam;
1974 case CB_GETLBTEXT16:
1975 wParam = (INT)(INT16)wParam;
1976 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1977 case CB_GETLBTEXT:
1978 return SendMessageA( lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
1979 case CB_GETLBTEXTLEN16:
1980 wParam = (INT)(INT16)wParam;
1981 case CB_GETLBTEXTLEN:
1982 return SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
1983 case CB_GETITEMDATA16:
1984 wParam = (INT)(INT16)wParam;
1985 case CB_GETITEMDATA:
1986 return SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
1987 case CB_SETITEMDATA16:
1988 wParam = (INT)(INT16)wParam;
1989 case CB_SETITEMDATA:
1990 return SendMessageA( lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
1991 case CB_GETEDITSEL16:
1992 wParam = lParam = 0; /* just in case */
1993 case CB_GETEDITSEL:
1994 if( lphc->wState & CBF_EDIT )
1996 INT a, b;
1998 return SendMessageA( lphc->hWndEdit, EM_GETSEL,
1999 (wParam) ? wParam : (WPARAM)&a,
2000 (lParam) ? lParam : (LPARAM)&b );
2002 return CB_ERR;
2003 case CB_SETEDITSEL16:
2004 case CB_SETEDITSEL:
2005 if( lphc->wState & CBF_EDIT )
2006 return SendMessageA( lphc->hWndEdit, EM_SETSEL,
2007 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2008 return CB_ERR;
2009 case CB_SETEXTENDEDUI16:
2010 case CB_SETEXTENDEDUI:
2011 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2012 return CB_ERR;
2013 if( wParam )
2014 lphc->wState |= CBF_EUI;
2015 else lphc->wState &= ~CBF_EUI;
2016 return CB_OKAY;
2017 case CB_GETEXTENDEDUI16:
2018 case CB_GETEXTENDEDUI:
2019 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2020 case (WM_USER + 0x1B):
2021 WARN("[%04x]: undocumented msg!\n", hwnd );
2023 return DefWindowProcA(hwnd, message, wParam, lParam);
2025 return CB_ERR;
2028 /***********************************************************************
2029 * ComboWndProc
2031 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2032 * window structs.
2034 LRESULT WINAPI ComboWndProc( HWND hwnd, UINT message,
2035 WPARAM wParam, LPARAM lParam )
2037 WND* pWnd = WIN_FindWndPtr(hwnd);
2038 LRESULT retvalue = ComboWndProc_locked(pWnd,message,wParam,lParam);
2041 WIN_ReleaseWndPtr(pWnd);
2042 return retvalue;