applied fixes to make a user32.dll
[reactos.git] / reactos / lib / user32 / controls / combo.c
1 /*
2 * Combo controls
3 *
4 * Copyright 1997 Alex Korobka
5 *
6 * FIXME: roll up in Netscape 3.01.
7 */
8
9 #include <string.h>
10
11 #include <windows.h>
12 #include <user32/sysmetr.h>
13 #include <user32/win.h>
14
15 #include <user32/heapdup.h>
16 #include <user32/combo.h>
17 #include <user32/debug.h>
18
19 /* bits in the dwKeyData */
20 #define KEYDATA_ALT 0x2000
21 #define KEYDATA_PREVSTATE 0x4000
22
23 /*
24 * Additional combo box definitions
25 */
26
27 #define CB_GETPTR( wnd ) (*(LPHEADCOMBO*)((wnd)->wExtra))
28 #define CB_NOTIFY( lphc, code ) \
29 (SendMessageA( (lphc)->owner, WM_COMMAND, \
30 MAKEWPARAM((lphc)->self->wIDmenu, (code)), (LPARAM)(lphc)->self->hwndSelf))
31 #define CB_GETEDITTEXTLENGTH( lphc ) \
32 (SendMessageA( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
33
34 static HBITMAP hComboBmp = 0;
35 static UINT CBitHeight, CBitWidth;
36 static UINT CBitOffset = 8;
37
38 /***********************************************************************
39 * COMBO_Init
40 *
41 * Load combo button bitmap.
42 */
43 WINBOOL COMBO_Init(void)
44 {
45 HDC hDC;
46
47 if( hComboBmp ) return TRUE;
48 if( (hDC = CreateCompatibleDC(0)) )
49 {
50 WINBOOL bRet = FALSE;
51 if( (hComboBmp = LoadBitmap(0, MAKEINTRESOURCE(OBM_COMBO))) )
52 {
53 BITMAP bm;
54 HBITMAP hPrevB;
55 RECT r;
56
57 GetObjectA( hComboBmp, sizeof(bm), &bm );
58 CBitHeight = bm.bmHeight;
59 CBitWidth = bm.bmWidth;
60
61 DPRINT( "combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
62
63 hPrevB = SelectObject( hDC, hComboBmp);
64 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
65 InvertRect( hDC, &r );
66 SelectObject( hDC, hPrevB );
67 bRet = TRUE;
68 }
69 DeleteDC( hDC );
70 return bRet;
71 }
72 return FALSE;
73 }
74
75 /***********************************************************************
76 * COMBO_NCCreate
77 */
78 static LRESULT COMBO_NCCreate(WND* wnd, LPARAM lParam)
79 {
80 LPHEADCOMBO lphc;
81
82 if ( wnd && COMBO_Init() &&
83 (lphc = HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO))) )
84 {
85 LPCREATESTRUCT lpcs = (CREATESTRUCT*)lParam;
86
87 memset( lphc, 0, sizeof(HEADCOMBO) );
88 *(LPHEADCOMBO*)wnd->wExtra = lphc;
89
90 /* some braindead apps do try to use scrollbar/border flags */
91
92 lphc->dwStyle = (lpcs->style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL));
93 wnd->dwStyle &= ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
94
95 if( !(lpcs->style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
96 lphc->dwStyle |= CBS_HASSTRINGS;
97 if( !(wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) )
98 lphc->wState |= CBF_NOTIFY;
99
100 DPRINT( "[0x%08x], style = %08x\n",
101 (UINT)lphc, lphc->dwStyle );
102
103 return (LRESULT)(UINT)wnd->hwndSelf;
104 }
105 return (LRESULT)FALSE;
106 }
107
108 /***********************************************************************
109 * COMBO_NCDestroy
110 */
111 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
112 {
113
114 if( lphc )
115 {
116 WND* wnd = lphc->self;
117
118 DPRINT("[%04x]: freeing storage\n", CB_HWND(lphc));
119
120 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
121 DestroyWindow( lphc->hWndLBox );
122
123 HeapFree( GetProcessHeap(), 0, lphc );
124 wnd->wExtra[0] = 0;
125 }
126 return 0;
127 }
128
129 /***********************************************************************
130 * CBGetDefaultTextHeight
131 */
132 static void CBGetDefaultTextHeight( LPHEADCOMBO lphc, LPSIZE lpSize )
133 {
134 if( lphc->editHeight ) /* explicitly set height */
135 lpSize->cy = lphc->editHeight;
136 else
137 {
138 HDC hDC = GetDC( lphc->self->hwndSelf );
139 HFONT hPrevFont = 0;
140
141 if( lphc->hFont ) hPrevFont = SelectObject( hDC, lphc->hFont );
142
143 GetTextExtentPointA( hDC, "0", 1, lpSize);
144
145 lpSize->cy += lpSize->cy / 4 + 4 * SYSMETRICS_CYBORDER;
146
147 if( hPrevFont ) SelectObject( hDC, hPrevFont );
148 ReleaseDC( lphc->self->hwndSelf, hDC );
149 }
150 lpSize->cx = lphc->RectCombo.right - lphc->RectCombo.left;
151 }
152
153
154 /***********************************************************************
155 * CBCalcPlacement
156 *
157 * Set up component coordinates given valid lphc->RectCombo.
158 */
159 static void CBCalcPlacement( LPHEADCOMBO lphc, LPRECT lprEdit,
160 LPRECT lprButton, LPRECT lprLB )
161 {
162 RECT rect = lphc->RectCombo;
163 SIZE size;
164
165 /* get combo height and width */
166
167 if( CB_OWNERDRAWN(lphc) )
168 {
169 UINT u = lphc->RectEdit.bottom - lphc->RectEdit.top;
170
171 if( lphc->wState & CBF_MEASUREITEM ) /* first initialization */
172 {
173 MEASUREITEMSTRUCT mi;
174
175 /* calculate defaults before sending WM_MEASUREITEM */
176
177 CBGetDefaultTextHeight( lphc, &size );
178
179 lphc->wState &= ~CBF_MEASUREITEM;
180
181 mi.CtlType = ODT_COMBOBOX;
182 mi.CtlID = lphc->self->wIDmenu;
183 mi.itemID = -1;
184 mi.itemWidth = size.cx;
185 mi.itemHeight = size.cy - 6; /* ownerdrawn cb is taller */
186 mi.itemData = 0;
187 SendMessageA(lphc->owner, WM_MEASUREITEM,
188 (WPARAM)mi.CtlID, (LPARAM)&mi);
189 u = 6 + (UINT)mi.itemHeight;
190 }
191 else
192 size.cx = rect.right - rect.left;
193 size.cy = u;
194 }
195 else
196 CBGetDefaultTextHeight( lphc, &size );
197
198 /* calculate text and button placement */
199
200 lprEdit->left = lprEdit->top = lprButton->top = 0;
201 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* no button */
202 lprButton->left = lprButton->right = lprButton->bottom = 0;
203 else
204 {
205 INT i = size.cx - CBitWidth - 10; /* seems ok */
206
207 lprButton->right = size.cx;
208 lprButton->left = (INT)i;
209 lprButton->bottom = lprButton->top + size.cy;
210
211 if( i < 0 ) size.cx = 0;
212 else size.cx = i;
213 }
214
215 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
216 {
217 size.cx -= CBitOffset;
218 if( size.cx < 0 ) size.cx = 0;
219 }
220
221 lprEdit->right = size.cx; lprEdit->bottom = size.cy;
222
223 /* listbox placement */
224
225 lprLB->left = ( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) ? 0 : CBitOffset;
226 lprLB->top = lprEdit->bottom - SYSMETRICS_CYBORDER;
227 lprLB->right = rect.right - rect.left;
228 lprLB->bottom = rect.bottom - rect.top;
229
230 if( lphc->droppedWidth > (lprLB->right - lprLB->left) )
231 lprLB->right = lprLB->left + lphc->droppedWidth;
232
233 DPRINT("[%04x]: (%i,%i-%i,%i) placement\n",
234 CB_HWND(lphc), lphc->RectCombo.left, lphc->RectCombo.top,
235 lphc->RectCombo.right, lphc->RectCombo.bottom);
236
237 DPRINT("\ttext\t= (%i,%i-%i,%i)\n",
238 lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
239
240 DPRINT("\tbutton\t= (%i,%i-%i,%i)\n",
241 lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
242
243 DPRINT("\tlbox\t= (%i,%i-%i,%i)\n",
244 lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
245 }
246
247 /***********************************************************************
248 * CBGetDroppedControlRect
249 */
250 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
251 {
252 lpRect->left = lphc->RectCombo.left +
253 (lphc->wState & CBF_EDIT) ? CBitOffset : 0;
254 lpRect->top = lphc->RectCombo.top + lphc->RectEdit.bottom -
255 SYSMETRICS_CYBORDER;
256 lpRect->right = lphc->RectCombo.right;
257 lpRect->bottom = lphc->RectCombo.bottom - SYSMETRICS_CYBORDER;
258 }
259
260 /***********************************************************************
261 * COMBO_Create
262 */
263 LRESULT COMBO_Create( LPHEADCOMBO lphc, WND* wnd, LPARAM lParam)
264 {
265 static char clbName[] = "ComboLBox";
266 static char editName[] = "Edit";
267
268 LPCREATESTRUCT lpcs = (CREATESTRUCT*)lParam;
269
270 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
271 else if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
272
273 lphc->self = wnd;
274 lphc->owner = lpcs->hwndParent;
275
276 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
277
278 if( lphc->owner || !(lpcs->style & WS_VISIBLE) )
279 {
280 UINT lbeStyle;
281 RECT editRect, btnRect, lbRect;
282
283 GetWindowRect( wnd->hwndSelf, &lphc->RectCombo );
284
285 lphc->wState |= CBF_MEASUREITEM;
286 CBCalcPlacement( lphc, &editRect, &btnRect, &lbRect );
287 lphc->RectButton = btnRect;
288 lphc->droppedWidth = lphc->editHeight = 0;
289
290 /* create listbox popup */
291
292 lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS) |
293 (lpcs->style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
294
295 if( lphc->dwStyle & CBS_SORT )
296 lbeStyle |= LBS_SORT;
297 if( lphc->dwStyle & CBS_HASSTRINGS )
298 lbeStyle |= LBS_HASSTRINGS;
299 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
300 lbeStyle |= LBS_NOINTEGRALHEIGHT;
301 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
302 lbeStyle |= LBS_DISABLENOSCROLL;
303
304 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
305 lbeStyle |= WS_CHILD | WS_VISIBLE;
306 else /* popup listbox */
307 {
308 lbeStyle |= WS_POPUP;
309 OffsetRect( &lbRect, lphc->RectCombo.left, lphc->RectCombo.top );
310 }
311
312 /* Dropdown ComboLBox is not a child window and we cannot pass
313 * ID_CB_LISTBOX directly because it will be treated as a menu handle.
314 */
315
316 lphc->hWndLBox = CreateWindowExA( 0, clbName, NULL, lbeStyle,
317 lbRect.left + SYSMETRICS_CXBORDER,
318 lbRect.top + SYSMETRICS_CYBORDER,
319 lbRect.right - lbRect.left - 2 * SYSMETRICS_CXBORDER,
320 lbRect.bottom - lbRect.top - 2 * SYSMETRICS_CYBORDER,
321 lphc->self->hwndSelf,
322 (lphc->dwStyle & CBS_DROPDOWN)? (HMENU)0 : (HMENU)ID_CB_LISTBOX,
323 lphc->self->hInstance, (LPVOID)lphc );
324 if( lphc->hWndLBox )
325 {
326 WINBOOL bEdit = TRUE;
327 lbeStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | ES_NOHIDESEL | ES_LEFT;
328 if( lphc->wState & CBF_EDIT )
329 {
330 if( lphc->dwStyle & CBS_OEMCONVERT )
331 lbeStyle |= ES_OEMCONVERT;
332 if( lphc->dwStyle & CBS_AUTOHSCROLL )
333 lbeStyle |= ES_AUTOHSCROLL;
334 if( lphc->dwStyle & CBS_LOWERCASE )
335 lbeStyle |= ES_LOWERCASE;
336 else if( lphc->dwStyle & CBS_UPPERCASE )
337 lbeStyle |= ES_UPPERCASE;
338 lphc->hWndEdit = CreateWindowExA( 0, editName, NULL, lbeStyle,
339 editRect.left, editRect.top, editRect.right - editRect.left,
340 editRect.bottom - editRect.top, lphc->self->hwndSelf,
341 (HMENU)ID_CB_EDIT, lphc->self->hInstance, NULL );
342 if( !lphc->hWndEdit ) bEdit = FALSE;
343 }
344
345 if( bEdit )
346 {
347 lphc->RectEdit = editRect;
348 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
349 {
350 lphc->wState |= CBF_NORESIZE;
351 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
352 lphc->RectCombo.right - lphc->RectCombo.left,
353 lphc->RectEdit.bottom - lphc->RectEdit.top,
354 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
355 lphc->wState &= ~CBF_NORESIZE;
356 }
357 DPRINT("init done\n");
358 return (LRESULT)wnd->hwndSelf;
359 }
360 DPRINT("edit control failure.\n");
361 } else DPRINT("listbox failure.\n");
362 } else DPRINT("no owner for visible combo.\n");
363
364 /* CreateWindow() will send WM_NCDESTROY to cleanup */
365
366 return -1;
367 }
368
369 /***********************************************************************
370 * CBPaintButton
371 *
372 * Paint combo button (normal, pressed, and disabled states).
373 */
374 static void CBPaintButton(LPHEADCOMBO lphc, HDC hdc)
375 {
376 RECT r;
377 UINT x, y;
378 WINBOOL bWINBOOL;
379 HDC hMemDC;
380 HBRUSH hPrevBrush;
381 COLORREF oldTextColor, oldBkColor;
382
383 if( lphc->wState & CBF_NOREDRAW ) return;
384
385 hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
386 r = lphc->RectButton;
387
388 Rectangle(hdc, r.left, r.top, r.right, r.bottom );
389 if( (bWINBOOL = lphc->wState & CBF_BUTTONDOWN) )
390 {
391 DrawEdge( hdc, &r, EDGE_SUNKEN, BF_RECT );
392 OffsetRect( &r, 1, 1 );
393 } else {
394 r.top++, r.left++;
395 DrawEdge( hdc, &r, EDGE_RAISED, BF_RECT );
396 r.top--, r.left--;
397 }
398
399 InflateRect( &r, -1, -1 );
400
401 x = (r.left + r.right - CBitWidth) >> 1;
402 y = (r.top + r.bottom - CBitHeight) >> 1;
403
404 InflateRect( &r, -3, -3 );
405
406 hMemDC = CreateCompatibleDC( hdc );
407 SelectObject( hMemDC, hComboBmp );
408 oldTextColor = SetTextColor( hdc, GetSysColor(COLOR_BTNFACE) );
409 oldBkColor = SetBkColor( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) :
410 RGB(0,0,0) );
411 BitBlt( hdc, x, y, 8, 8, hMemDC, 0, 0, SRCCOPY );
412 SetBkColor( hdc, oldBkColor );
413 SetTextColor( hdc, oldTextColor );
414 DeleteDC( hMemDC );
415 SelectObject( hdc, hPrevBrush );
416 }
417
418 /***********************************************************************
419 * CBPaintText
420 *
421 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
422 */
423 static void CBPaintText(LPHEADCOMBO lphc, HDC hdc)
424 {
425 INT id, size = 0;
426 LPSTR pText = NULL;
427
428 if( lphc->wState & CBF_NOREDRAW ) return;
429
430 /* follow Windows combobox that sends a bunch of text
431 * inquiries to its listbox while processing WM_PAINT. */
432
433 if( (id = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
434 {
435 size = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
436 if( (pText = HeapAlloc( GetProcessHeap(), 0, size + 1)) )
437 {
438 SendMessageA( lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText );
439 pText[size] = '\0'; /* just in case */
440 } else return;
441 }
442
443 if( lphc->wState & CBF_EDIT )
444 {
445 if( CB_HASSTRINGS(lphc) ) SetWindowTextA( lphc->hWndEdit, pText ? pText : "" );
446 if( lphc->wState & CBF_FOCUSED )
447 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
448 }
449 else /* paint text field ourselves */
450 {
451 HBRUSH hPrevBrush = 0;
452 HDC hDC = hdc;
453
454 if( !hDC )
455 {
456 if ((hDC = GetDC(lphc->self->hwndSelf)))
457 {
458 HBRUSH hBrush = (HBRUSH)SendMessageA( lphc->owner,
459 WM_CTLCOLORLISTBOX,
460 (WPARAM)hDC, (LPARAM)lphc->self->hwndSelf );
461 hPrevBrush = SelectObject( hDC,
462 (hBrush) ? hBrush : GetStockObject(WHITE_BRUSH) );
463 }
464 }
465 if( hDC )
466 {
467 RECT rect;
468 UINT itemState;
469 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hDC, lphc->hFont) : 0;
470
471 PatBlt( hDC, (rect.left = lphc->RectEdit.left + SYSMETRICS_CXBORDER),
472 (rect.top = lphc->RectEdit.top + SYSMETRICS_CYBORDER),
473 (rect.right = lphc->RectEdit.right - SYSMETRICS_CXBORDER),
474 (rect.bottom = lphc->RectEdit.bottom - SYSMETRICS_CYBORDER) - 1, PATCOPY );
475 InflateRect( &rect, -1, -1 );
476
477 if( lphc->wState & CBF_FOCUSED &&
478 !(lphc->wState & CBF_DROPPED) )
479 {
480 /* highlight */
481
482 FillRect( hDC, &rect, GetSysColorBrush(COLOR_HIGHLIGHT) );
483 SetBkColor( hDC, GetSysColor( COLOR_HIGHLIGHT ) );
484 SetTextColor( hDC, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
485 itemState = ODS_SELECTED | ODS_FOCUS;
486 } else itemState = 0;
487
488 if( CB_OWNERDRAWN(lphc) )
489 {
490 DRAWITEMSTRUCT dis;
491
492 if( lphc->self->dwStyle & WS_DISABLED ) itemState |= ODS_DISABLED;
493
494 dis.CtlType = ODT_COMBOBOX;
495 dis.CtlID = lphc->self->wIDmenu;
496 dis.hwndItem = lphc->self->hwndSelf;
497 dis.itemAction = ODA_DRAWENTIRE;
498 dis.itemID = id;
499 dis.itemState = itemState;
500 dis.hDC = hDC;
501 dis.rcItem = rect;
502 dis.itemData = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA,
503 (WPARAM)id, 0 );
504 SendMessageA( lphc->owner, WM_DRAWITEM,
505 lphc->self->wIDmenu, (LPARAM)&dis );
506 }
507 else
508 {
509 ExtTextOutA( hDC, rect.left + 1, rect.top + 1,
510 ETO_OPAQUE | ETO_CLIPPED, &rect,
511 pText ? pText : "" , size, NULL );
512 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
513 DrawFocusRect( hDC, &rect );
514 }
515
516 if( hPrevFont ) SelectObject(hDC, hPrevFont );
517 if( !hdc )
518 {
519 if( hPrevBrush ) SelectObject( hDC, hPrevBrush );
520 ReleaseDC( lphc->self->hwndSelf, hDC );
521 }
522 }
523 }
524 if (pText)
525 HeapFree( GetProcessHeap(), 0, pText );
526 }
527
528 /***********************************************************************
529 * COMBO_Paint
530 */
531 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
532 {
533 PAINTSTRUCT ps;
534 HDC hDC;
535
536 hDC = (hParamDC) ? hParamDC
537 : BeginPaint( lphc->self->hwndSelf, &ps);
538 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
539 {
540 HBRUSH hPrevBrush, hBkgBrush;
541
542 hBkgBrush = (HBRUSH)SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
543 (WPARAM)hDC, (LPARAM)lphc->self->hwndSelf );
544 if( !hBkgBrush ) hBkgBrush = GetStockObject(WHITE_BRUSH);
545
546 hPrevBrush = SelectObject( hDC, hBkgBrush );
547 if( !IsRectEmpty(&lphc->RectButton) )
548 {
549 /* paint everything to the right of the text field */
550
551 PatBlt( hDC, lphc->RectEdit.right, lphc->RectEdit.top,
552 lphc->RectButton.right - lphc->RectEdit.right,
553 lphc->RectEdit.bottom - lphc->RectEdit.top, PATCOPY );
554 CBPaintButton( lphc, hDC );
555 }
556
557 if( !(lphc->wState & CBF_EDIT) )
558 {
559 /* paint text field */
560
561 HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(
562 COLOR_WINDOWFRAME) );
563
564 Rectangle( hDC, lphc->RectEdit.left, lphc->RectEdit.top,
565 lphc->RectEdit.right, lphc->RectButton.bottom );
566 SelectObject( hDC, hPrevPen );
567 CBPaintText( lphc, hDC );
568 }
569 if( hPrevBrush ) SelectObject( hDC, hPrevBrush );
570 }
571 if( !hParamDC ) EndPaint(lphc->self->hwndSelf, &ps);
572 return 0;
573 }
574
575 /***********************************************************************
576 * CBUpdateLBox
577 *
578 * Select listbox entry according to the contents of the edit control.
579 */
580 static INT CBUpdateLBox( LPHEADCOMBO lphc )
581 {
582 INT length, idx, ret;
583 LPSTR pText = NULL;
584
585 idx = ret = LB_ERR;
586 length = CB_GETEDITTEXTLENGTH( lphc );
587
588 if( length > 0 )
589 pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1);
590
591 DPRINT("\t edit text length %i\n", length );
592
593 if( pText )
594 {
595 if( length ) GetWindowTextA( lphc->hWndEdit, pText, length + 1);
596 else pText[0] = '\0';
597 idx = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
598 (WPARAM)(-1), (LPARAM)pText );
599 if( idx == LB_ERR ) idx = 0; /* select first item */
600 else ret = idx;
601 HeapFree( GetProcessHeap(), 0, pText );
602 }
603
604 /* select entry */
605
606 SendMessageA( lphc->hWndLBox, LB_SETCURSEL, (WPARAM)idx, 0 );
607
608 if( idx >= 0 )
609 {
610 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)idx, 0 );
611 /* probably superfluous but Windows sends this too */
612 SendMessageA( lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)idx, 0 );
613 }
614 return ret;
615 }
616
617 /***********************************************************************
618 * CBUpdateEdit
619 *
620 * Copy a listbox entry to the edit control.
621 */
622 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
623 {
624 INT length;
625 LPSTR pText = NULL;
626
627 DPRINT("\t %i\n", index );
628
629 if( index == -1 )
630 {
631 length = CB_GETEDITTEXTLENGTH( lphc );
632 if( length )
633 {
634 if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
635 {
636 GetWindowTextA( lphc->hWndEdit, pText, length + 1 );
637 index = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
638 (WPARAM)(-1), (LPARAM)pText );
639 HeapFree( GetProcessHeap(), 0, pText );
640 }
641 }
642 }
643
644 if( index >= 0 ) /* got an entry */
645 {
646 length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
647 if( length )
648 {
649 if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
650 {
651 SendMessageA( lphc->hWndLBox, LB_GETTEXT,
652 (WPARAM)index, (LPARAM)pText );
653 SendMessageA( lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)pText );
654 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
655 HeapFree( GetProcessHeap(), 0, pText );
656 }
657 }
658 }
659 }
660
661 /***********************************************************************
662 * CBDropDown
663 *
664 * Show listbox popup.
665 */
666 static void CBDropDown( LPHEADCOMBO lphc )
667 {
668 INT index;
669 RECT rect;
670 LPRECT pRect = NULL;
671
672 DPRINT("[%04x]: drop down\n", CB_HWND(lphc));
673
674 CB_NOTIFY( lphc, CBN_DROPDOWN );
675
676 /* set selection */
677
678 lphc->wState |= CBF_DROPPED;
679 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
680 {
681 index = CBUpdateLBox( lphc );
682 if( !(lphc->wState & CBF_CAPTURE) ) CBUpdateEdit( lphc, index );
683 }
684 else
685 {
686 index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
687 if( index == LB_ERR ) index = 0;
688 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)index, 0 );
689 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
690 pRect = &lphc->RectEdit;
691 }
692
693 /* now set popup position */
694
695 GetWindowRect( lphc->self->hwndSelf, &rect );
696
697 rect.top += lphc->RectEdit.bottom - lphc->RectEdit.top - SYSMETRICS_CYBORDER;
698 rect.bottom = rect.top + lphc->RectCombo.bottom -
699 lphc->RectCombo.top - SYSMETRICS_CYBORDER;
700 rect.right = rect.left + lphc->RectCombo.right - lphc->RectCombo.left;
701 rect.left += ( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) ? 0 : CBitOffset;
702
703 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.top,
704 rect.right - rect.left, rect.bottom - rect.top,
705 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOREDRAW);
706
707 if( !(lphc->wState & CBF_NOREDRAW) )
708 if( pRect )
709 RedrawWindow( lphc->self->hwndSelf, pRect, 0, RDW_INVALIDATE |
710 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
711 ShowWindow( lphc->hWndLBox, SW_SHOWNA );
712 }
713
714 /***********************************************************************
715 * CBRollUp
716 *
717 * Hide listbox popup.
718 */
719 static void CBRollUp( LPHEADCOMBO lphc, WINBOOL ok, WINBOOL bButton )
720 {
721 HWND hWnd = lphc->self->hwndSelf;
722
723 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
724
725 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
726 {
727
728 DPRINT("[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
729
730 /* always send WM_LBUTTONUP? */
731 SendMessageA( lphc->hWndLBox, WM_LBUTTONUP, 0, (LPARAM)(-1) );
732
733 if( lphc->wState & CBF_DROPPED )
734 {
735 RECT rect;
736
737 lphc->wState &= ~CBF_DROPPED;
738 ShowWindow( lphc->hWndLBox, SW_HIDE );
739
740 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
741 {
742 INT index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
743 CBUpdateEdit( lphc, index );
744 rect = lphc->RectButton;
745 }
746 else
747 {
748 if( bButton )
749 UnionRect( &rect, &lphc->RectButton,
750 &lphc->RectEdit );
751 else
752 rect = lphc->RectEdit;
753 bButton = TRUE;
754 }
755
756 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
757 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
758 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
759 CB_NOTIFY( lphc, CBN_CLOSEUP );
760 }
761 }
762 }
763
764 /***********************************************************************
765 * COMBO_FlipListbox
766 *
767 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
768 */
769 WINBOOL COMBO_FlipListbox( LPHEADCOMBO lphc, WINBOOL bRedrawButton )
770 {
771 if( lphc->wState & CBF_DROPPED )
772 {
773 CBRollUp( lphc, TRUE, bRedrawButton );
774 return FALSE;
775 }
776
777 CBDropDown( lphc );
778 return TRUE;
779 }
780
781 /***********************************************************************
782 * COMBO_GetLBWindow
783 *
784 * Edit control helper.
785 */
786 HWND COMBO_GetLBWindow( WND* pWnd )
787 {
788 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
789 if( lphc ) return lphc->hWndLBox;
790 return 0;
791 }
792
793
794 /***********************************************************************
795 * CBRepaintButton
796 */
797 static void CBRepaintButton( LPHEADCOMBO lphc )
798 {
799 HDC hDC = GetDC( lphc->self->hwndSelf );
800
801 if( hDC )
802 {
803 CBPaintButton( lphc, hDC );
804 ReleaseDC( lphc->self->hwndSelf, hDC );
805 }
806 }
807
808 /***********************************************************************
809 * COMBO_SetFocus
810 */
811 static void COMBO_SetFocus( LPHEADCOMBO lphc )
812 {
813 if( !(lphc->wState & CBF_FOCUSED) )
814 {
815 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
816 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
817
818 if( lphc->wState & CBF_EDIT )
819 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
820 lphc->wState |= CBF_FOCUSED;
821 if( !(lphc->wState & CBF_EDIT) ) CBPaintText( lphc, 0 );
822
823 CB_NOTIFY( lphc, CBN_SETFOCUS );
824 }
825 }
826
827 /***********************************************************************
828 * COMBO_KillFocus
829 */
830 static void COMBO_KillFocus( LPHEADCOMBO lphc )
831 {
832 HWND hWnd = lphc->self->hwndSelf;
833
834 if( lphc->wState & CBF_FOCUSED )
835 {
836 SendMessageA( hWnd, WM_LBUTTONUP, 0, (LPARAM)(-1) );
837
838 CBRollUp( lphc, FALSE, TRUE );
839 if( IsWindow( hWnd ) )
840 {
841 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
842 SendMessageA( lphc->hWndLBox, LB_CARETOFF, 0, 0 );
843
844 lphc->wState &= ~CBF_FOCUSED;
845
846 /* redraw text */
847 if( lphc->wState & CBF_EDIT )
848 SendMessageA( lphc->hWndEdit, EM_SETSEL, (WPARAM)(-1), 0 );
849 else CBPaintText( lphc, 0 );
850
851 CB_NOTIFY( lphc, CBN_KILLFOCUS );
852 }
853 }
854 }
855
856 /***********************************************************************
857 * COMBO_Command
858 */
859 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
860 {
861 if( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
862 {
863 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
864
865 switch( HIWORD(wParam) >> 8 )
866 {
867 case (EN_SETFOCUS >> 8):
868
869 DPRINT("[%04x]: edit [%04x] got focus\n",
870 CB_HWND(lphc), lphc->hWndEdit );
871
872 if( !(lphc->wState & CBF_FOCUSED) ) COMBO_SetFocus( lphc );
873 break;
874
875 case (EN_KILLFOCUS >> 8):
876
877 DPRINT("[%04x]: edit [%04x] lost focus\n",
878 CB_HWND(lphc), lphc->hWndEdit );
879
880 /* NOTE: it seems that Windows' edit control sends an
881 * undocumented message WM_USER + 0x1B instead of this
882 * notification (only when it happens to be a part of
883 * the combo). ?? - AK.
884 */
885
886 COMBO_KillFocus( lphc );
887 break;
888
889
890 case (EN_CHANGE >> 8):
891 CB_NOTIFY( lphc, CBN_EDITCHANGE );
892 CBUpdateLBox( lphc );
893 break;
894
895 case (EN_UPDATE >> 8):
896 CB_NOTIFY( lphc, CBN_EDITUPDATE );
897 break;
898
899 case (EN_ERRSPACE >> 8):
900 CB_NOTIFY( lphc, CBN_ERRSPACE );
901 }
902 }
903 else if( lphc->hWndLBox == hWnd )
904 {
905 switch( HIWORD(wParam) )
906 {
907 case LBN_ERRSPACE:
908 CB_NOTIFY( lphc, CBN_ERRSPACE );
909 break;
910
911 case LBN_DBLCLK:
912 CB_NOTIFY( lphc, CBN_DBLCLK );
913 break;
914
915 case LBN_SELCHANGE:
916 case LBN_SELCANCEL:
917
918 DPRINT("[%04x]: lbox selection change [%04x]\n",
919 CB_HWND(lphc), lphc->wState );
920
921 /* do not roll up if selection is being tracked
922 * by arrowkeys in the dropdown listbox */
923
924 if( (lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP) )
925 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
926 else lphc->wState &= ~CBF_NOROLLUP;
927
928 CB_NOTIFY( lphc, CBN_SELCHANGE );
929 CBPaintText( lphc, 0 );
930 /* fall through */
931
932 case LBN_SETFOCUS:
933 case LBN_KILLFOCUS:
934 /* nothing to do here since ComboLBox always resets the focus to its
935 * combo/edit counterpart */
936 break;
937 }
938 }
939 return 0;
940 }
941
942 /***********************************************************************
943 * COMBO_ItemOp
944 *
945 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
946 */
947 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg,
948 WPARAM wParam, LPARAM lParam )
949 {
950 HWND hWnd = lphc->self->hwndSelf;
951
952 DPRINT("[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
953
954 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
955
956 /* two first items are the same in all 4 structs */
957 lpIS->CtlType = ODT_COMBOBOX;
958 lpIS->CtlID = lphc->self->wIDmenu;
959
960 switch( msg ) /* patch window handle */
961 {
962 case WM_DELETEITEM:
963 lpIS->hwndItem = hWnd;
964 #undef lpIS
965 break;
966 case WM_DRAWITEM:
967 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
968 lpIS->hwndItem = hWnd;
969 #undef lpIS
970 break;
971 case WM_COMPAREITEM:
972 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
973 lpIS->hwndItem = hWnd;
974 #undef lpIS
975 break;
976 }
977
978 return SendMessageA( lphc->owner, msg, lphc->self->wIDmenu, lParam );
979 }
980
981 /***********************************************************************
982 * COMBO_GetText
983 */
984 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT N, LPSTR lpText)
985 {
986 if( lphc->wState & CBF_EDIT )
987 return SendMessageA( lphc->hWndEdit, WM_GETTEXT,
988 (WPARAM)N, (LPARAM)lpText );
989
990 /* get it from the listbox */
991
992 if( lphc->hWndLBox )
993 {
994 INT idx = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
995 if( idx != LB_ERR )
996 {
997 LPSTR lpBuffer;
998 INT length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN,
999 (WPARAM)idx, 0 );
1000
1001 /* 'length' is without the terminating character */
1002 if( length >= N )
1003 lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 );
1004 else
1005 lpBuffer = lpText;
1006
1007 if( lpBuffer )
1008 {
1009 INT n = SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1010 (WPARAM)idx, (LPARAM)lpBuffer );
1011
1012 /* truncate if buffer is too short */
1013
1014 if( length >= N )
1015 {
1016 if (N && lpText) {
1017 if( n != LB_ERR ) memcpy( lpText, lpBuffer, (N>n) ? n+1 : N-1 );
1018 lpText[N - 1] = '\0';
1019 }
1020 HeapFree( GetProcessHeap(), 0, lpBuffer );
1021 }
1022 return (LRESULT)n;
1023 }
1024 }
1025 }
1026 return 0;
1027 }
1028
1029
1030 /***********************************************************************
1031 * CBResetPos
1032 *
1033 * This function sets window positions according to the updated
1034 * component placement struct.
1035 */
1036 static void CBResetPos( LPHEADCOMBO lphc, LPRECT lbRect, WINBOOL bRedraw )
1037 {
1038 WINBOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1039
1040 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1041 * sizing messages */
1042
1043 if( lphc->wState & CBF_EDIT )
1044 SetWindowPos( lphc->hWndEdit, 0, lphc->RectEdit.left, lphc->RectEdit.top,
1045 lphc->RectEdit.right - lphc->RectEdit.left,
1046 lphc->RectEdit.bottom - lphc->RectEdit.top,
1047 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1048
1049 if( bDrop )
1050 OffsetRect( lbRect, lphc->RectCombo.left, lphc->RectCombo.top );
1051
1052 lbRect->right -= lbRect->left; /* convert to width */
1053 lbRect->bottom -= lbRect->top;
1054 SetWindowPos( lphc->hWndLBox, 0, lbRect->left, lbRect->top,
1055 lbRect->right, lbRect->bottom,
1056 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1057
1058 if( bDrop )
1059 {
1060 if( lphc->wState & CBF_DROPPED )
1061 {
1062 lphc->wState &= ~CBF_DROPPED;
1063 ShowWindow( lphc->hWndLBox, SW_HIDE );
1064 }
1065
1066 lphc->wState |= CBF_NORESIZE;
1067 SetWindowPos( lphc->self->hwndSelf, 0, 0, 0,
1068 lphc->RectCombo.right - lphc->RectCombo.left,
1069 lphc->RectEdit.bottom - lphc->RectEdit.top,
1070 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW );
1071 lphc->wState &= ~CBF_NORESIZE;
1072
1073 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1074 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
1075 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1076 }
1077 }
1078
1079
1080 /***********************************************************************
1081 * COMBO_Size
1082 */
1083 static void COMBO_Size( LPHEADCOMBO lphc )
1084 {
1085 RECT rect;
1086 INT w, h;
1087
1088 GetWindowRect( lphc->self->hwndSelf, &rect );
1089 w = rect.right - rect.left; h = rect.bottom - rect.top;
1090
1091 DPRINT("w = %i, h = %i\n", w, h );
1092
1093 /* CreateWindow() may send a bogus WM_SIZE, ignore it */
1094
1095 if( w == (lphc->RectCombo.right - lphc->RectCombo.left) )
1096 {
1097 if( (CB_GETTYPE(lphc) == CBS_SIMPLE) &&
1098 (h == (lphc->RectCombo.bottom - lphc->RectCombo.top)) )
1099 return;
1100 else if( (lphc->dwStyle & CBS_DROPDOWN) &&
1101 (h == (lphc->RectEdit.bottom - lphc->RectEdit.top)) )
1102 return;
1103 }
1104 lphc->RectCombo = rect;
1105 CBCalcPlacement( lphc, &lphc->RectEdit, &lphc->RectButton, &rect );
1106 CBResetPos( lphc, &rect, TRUE );
1107 }
1108
1109
1110 /***********************************************************************
1111 * COMBO_Font
1112 */
1113 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, WINBOOL bRedraw )
1114 {
1115 RECT rect;
1116
1117 lphc->hFont = hFont;
1118
1119 if( lphc->wState & CBF_EDIT )
1120 SendMessageA( lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw );
1121 SendMessageA( lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw );
1122
1123 GetWindowRect( lphc->self->hwndSelf, &rect );
1124 OffsetRect( &lphc->RectCombo, rect.left - lphc->RectCombo.left,
1125 rect.top - lphc->RectCombo.top );
1126 CBCalcPlacement( lphc, &lphc->RectEdit,
1127 &lphc->RectButton, &rect );
1128 CBResetPos( lphc, &rect, bRedraw );
1129 }
1130
1131
1132 /***********************************************************************
1133 * COMBO_SetItemHeight
1134 */
1135 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1136 {
1137 LRESULT lRet = CB_ERR;
1138
1139 if( index == -1 ) /* set text field height */
1140 {
1141 if( height < 768 )
1142 {
1143 RECT rect;
1144
1145 lphc->editHeight = height;
1146 GetWindowRect( lphc->self->hwndSelf, &rect );
1147 OffsetRect( &lphc->RectCombo, rect.left - lphc->RectCombo.left,
1148 rect.top - lphc->RectCombo.top );
1149 CBCalcPlacement( lphc, &lphc->RectEdit,
1150 &lphc->RectButton, &rect );
1151 CBResetPos( lphc, &rect, TRUE );
1152 lRet = height;
1153 }
1154 }
1155 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1156 lRet = SendMessageA( lphc->hWndLBox, LB_SETITEMHEIGHT,
1157 (WPARAM)index, (LPARAM)height );
1158 return lRet;
1159 }
1160
1161 /***********************************************************************
1162 * COMBO_SelectString
1163 */
1164 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPCSTR pText )
1165 {
1166 INT index = SendMessageA( lphc->hWndLBox, LB_SELECTSTRING,
1167 (WPARAM)start, (LPARAM)pText );
1168 if( index >= 0 )
1169 {
1170 if( lphc->wState & CBF_EDIT )
1171 CBUpdateEdit( lphc, index );
1172 else
1173 CBPaintText( lphc, 0 );
1174 }
1175 return (LRESULT)index;
1176 }
1177
1178 /***********************************************************************
1179 * COMBO_LButtonDown
1180 */
1181 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1182 {
1183 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
1184 WINBOOL bButton = PtInRect(&lphc->RectButton, pt);
1185 HWND hWnd = lphc->self->hwndSelf;
1186
1187 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1188 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1189 {
1190 lphc->wState |= CBF_BUTTONDOWN;
1191 if( lphc->wState & CBF_DROPPED )
1192 {
1193 /* got a click to cancel selection */
1194
1195 CBRollUp( lphc, TRUE, FALSE );
1196 if( !IsWindow( hWnd ) ) return;
1197
1198 if( lphc->wState & CBF_CAPTURE )
1199 {
1200 lphc->wState &= ~CBF_CAPTURE;
1201 ReleaseCapture();
1202 }
1203 lphc->wState &= ~CBF_BUTTONDOWN;
1204 }
1205 else
1206 {
1207 /* drop down the listbox and start tracking */
1208
1209 lphc->wState |= CBF_CAPTURE;
1210 CBDropDown( lphc );
1211 SetCapture( hWnd );
1212 }
1213 if( bButton ) CBRepaintButton( lphc );
1214 }
1215 }
1216
1217 /***********************************************************************
1218 * COMBO_LButtonUp
1219 *
1220 * Release capture and stop tracking if needed.
1221 */
1222 static void COMBO_LButtonUp( LPHEADCOMBO lphc, LPARAM lParam )
1223 {
1224 if( lphc->wState & CBF_CAPTURE )
1225 {
1226 lphc->wState &= ~CBF_CAPTURE;
1227 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1228 {
1229 INT index = CBUpdateLBox( lphc );
1230 CBUpdateEdit( lphc, index );
1231 }
1232 ReleaseCapture();
1233 }
1234
1235 if( lphc->wState & CBF_BUTTONDOWN )
1236 {
1237 lphc->wState &= ~CBF_BUTTONDOWN;
1238 CBRepaintButton( lphc );
1239 }
1240 }
1241
1242 /***********************************************************************
1243 * COMBO_MouseMove
1244 *
1245 * Two things to do - track combo button and release capture when
1246 * pointer goes into the listbox.
1247 */
1248 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1249 {
1250 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
1251 RECT lbRect;
1252
1253 if( lphc->wState & CBF_BUTTONDOWN )
1254 {
1255 WINBOOL bButton = PtInRect(&lphc->RectButton, pt);
1256
1257 if( !bButton )
1258 {
1259 lphc->wState &= ~CBF_BUTTONDOWN;
1260 CBRepaintButton( lphc );
1261 }
1262 }
1263
1264 GetClientRect( lphc->hWndLBox, &lbRect );
1265 MapWindowPoints( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 );
1266 if( PtInRect(&lbRect, pt) )
1267 {
1268 lphc->wState &= ~CBF_CAPTURE;
1269 ReleaseCapture();
1270 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc );
1271
1272 /* hand over pointer tracking */
1273 SendMessageA( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam );
1274 }
1275 }
1276
1277
1278 /***********************************************************************
1279 * ComboWndProc
1280 *
1281 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win/ctrl/src/combobox_15.htm
1282 */
1283 LRESULT WINAPI ComboWndProc( HWND hwnd, UINT message,
1284 WPARAM wParam, LPARAM lParam )
1285 {
1286 WND* pWnd = WIN_FindWndPtr(hwnd);
1287
1288 if( pWnd )
1289 {
1290 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1291
1292 DPRINT( "[%04x]: msg %s wp %08x lp %08lx\n",
1293 pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
1294
1295 if( lphc || message == WM_NCCREATE )
1296 switch(message)
1297 {
1298
1299 /* System messages */
1300
1301 case WM_NCCREATE:
1302 return COMBO_NCCreate(pWnd, lParam);
1303
1304 case WM_NCDESTROY:
1305 COMBO_NCDestroy(lphc);
1306 break;
1307
1308 case WM_CREATE:
1309 return COMBO_Create(lphc, pWnd, lParam);
1310
1311 case WM_PAINT:
1312 /* wParam may contain a valid HDC! */
1313 return COMBO_Paint(lphc, wParam);
1314
1315 case WM_ERASEBKGND:
1316 return TRUE;
1317
1318 case WM_GETDLGCODE:
1319 return (LRESULT)(DLGC_WANTARROWS | DLGC_WANTCHARS);
1320
1321 case WM_SIZE:
1322 if( lphc->hWndLBox &&
1323 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1324 return TRUE;
1325
1326 case WM_SETFONT:
1327 COMBO_Font( lphc, (HFONT)wParam, (WINBOOL)lParam );
1328 return TRUE;
1329
1330 case WM_GETFONT:
1331 return (LRESULT)lphc->hFont;
1332
1333 case WM_SETFOCUS:
1334 if( lphc->wState & CBF_EDIT )
1335 SetFocus( lphc->hWndEdit );
1336 else
1337 COMBO_SetFocus( lphc );
1338 return TRUE;
1339
1340 case WM_KILLFOCUS:
1341 #define hwndFocus ((HWND)wParam)
1342 if( !hwndFocus ||
1343 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1344 COMBO_KillFocus( lphc );
1345 #undef hwndFocus
1346 return TRUE;
1347
1348 case WM_COMMAND:
1349 return COMBO_Command( lphc, wParam, (HWND)lParam );
1350
1351 case WM_GETTEXT:
1352 return COMBO_GetText( lphc, (UINT)wParam, (LPSTR)lParam );
1353
1354 case WM_SETTEXT:
1355 case WM_GETTEXTLENGTH:
1356 case WM_CLEAR:
1357 case WM_CUT:
1358 case WM_PASTE:
1359 case WM_COPY:
1360 if( lphc->wState & CBF_EDIT )
1361 return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1362 return CB_ERR;
1363
1364 case WM_DRAWITEM:
1365 case WM_DELETEITEM:
1366 case WM_COMPAREITEM:
1367 case WM_MEASUREITEM:
1368 return COMBO_ItemOp( lphc, message, wParam, lParam );
1369
1370 case WM_ENABLE:
1371 if( lphc->wState & CBF_EDIT )
1372 EnableWindow( lphc->hWndEdit, (WINBOOL)wParam );
1373 EnableWindow( lphc->hWndLBox, (WINBOOL)wParam );
1374 return TRUE;
1375
1376 case WM_SETREDRAW:
1377 if( wParam )
1378 lphc->wState &= ~CBF_NOREDRAW;
1379 else
1380 lphc->wState |= CBF_NOREDRAW;
1381
1382 if( lphc->wState & CBF_EDIT )
1383 SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1384 SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1385 return 0;
1386
1387 case WM_SYSKEYDOWN:
1388 if( KEYDATA_ALT & HIWORD(lParam) )
1389 if( wParam == VK_UP || wParam == VK_DOWN )
1390 COMBO_FlipListbox( lphc, TRUE );
1391 break;
1392
1393 case WM_CHAR:
1394 case WM_KEYDOWN:
1395 if( lphc->wState & CBF_EDIT )
1396 return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1397 else
1398 return SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1399
1400 case WM_LBUTTONDOWN:
1401 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf );
1402 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1403 return TRUE;
1404
1405 case WM_LBUTTONUP:
1406 COMBO_LButtonUp( lphc, lParam );
1407 return TRUE;
1408
1409 case WM_MOUSEMOVE:
1410 if( lphc->wState & CBF_CAPTURE )
1411 COMBO_MouseMove( lphc, wParam, lParam );
1412 return TRUE;
1413
1414 /* Combo messages */
1415
1416 case CB_ADDSTRING:
1417 return SendMessageA( lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1418
1419 case CB_INSERTSTRING:
1420 return SendMessageA( lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1421
1422 case CB_DELETESTRING:
1423 return SendMessageA( lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1424
1425 case CB_SELECTSTRING:
1426 return COMBO_SelectString( lphc, (INT)wParam, (LPSTR)lParam );
1427
1428 case CB_FINDSTRING:
1429 return SendMessageA( lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1430
1431 case CB_FINDSTRINGEXACT:
1432 return SendMessageA( lphc->hWndLBox, LB_FINDSTRINGEXACT,
1433 wParam, lParam );
1434
1435 case CB_SETITEMHEIGHT:
1436 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1437
1438 case CB_GETITEMHEIGHT:
1439 if( (INT)wParam >= 0 ) /* listbox item */
1440 return SendMessageA( lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1441 return (lphc->RectEdit.bottom - lphc->RectEdit.top);
1442
1443 case CB_RESETCONTENT:
1444 SendMessageA( lphc->hWndLBox, LB_RESETCONTENT, 0, 0 );
1445 CBPaintText( lphc, 0 );
1446 return TRUE;
1447
1448 case CB_INITSTORAGE:
1449 return SendMessageA( lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
1450
1451 case CB_GETHORIZONTALEXTENT:
1452 return SendMessageA( lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
1453
1454 case CB_SETHORIZONTALEXTENT:
1455 return SendMessageA( lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
1456
1457 case CB_GETTOPINDEX:
1458 return SendMessageA( lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
1459
1460 case CB_GETLOCALE:
1461 return SendMessageA( lphc->hWndLBox, LB_GETLOCALE, 0, 0);
1462
1463 case CB_SETLOCALE:
1464 return SendMessageA( lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
1465
1466 case CB_GETDROPPEDWIDTH:
1467 if( lphc->droppedWidth )
1468 return lphc->droppedWidth;
1469 return lphc->RectCombo.right - lphc->RectCombo.left -
1470 (lphc->wState & CBF_EDIT) ? CBitOffset : 0;
1471
1472 case CB_SETDROPPEDWIDTH:
1473 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
1474 (INT)wParam < 768 ) lphc->droppedWidth = (INT)wParam;
1475 return CB_ERR;
1476
1477 case CB_GETDROPPEDCONTROLRECT:
1478 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
1479 return CB_OKAY;
1480
1481 case CB_GETDROPPEDSTATE:
1482 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
1483
1484
1485 case CB_DIR:
1486 return COMBO_Directory( lphc, (UINT)wParam,
1487 (LPSTR)lParam, (message == CB_DIR));
1488 case CB_SHOWDROPDOWN:
1489 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1490 {
1491 if( wParam )
1492 {
1493 if( !(lphc->wState & CBF_DROPPED) )
1494 CBDropDown( lphc );
1495 }
1496 else
1497 if( lphc->wState & CBF_DROPPED )
1498 CBRollUp( lphc, FALSE, TRUE );
1499 }
1500 return TRUE;
1501
1502 case CB_GETCOUNT:
1503 return SendMessageA( lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1504
1505 case CB_GETCURSEL:
1506 return SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1507
1508 case CB_SETCURSEL:
1509 lParam = SendMessageA( lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
1510 if( lphc->wState & CBF_SELCHANGE )
1511 {
1512 /* no LBN_SELCHANGE in this case, update manually */
1513
1514 CBPaintText( lphc, 0 );
1515 lphc->wState &= ~CBF_SELCHANGE;
1516 }
1517 return lParam;
1518
1519
1520 case CB_GETLBTEXT:
1521 return SendMessageA( lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
1522
1523 case CB_GETLBTEXTLEN:
1524 return SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
1525
1526 case CB_GETITEMDATA:
1527 return SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
1528
1529 case CB_SETITEMDATA:
1530 return SendMessageA( lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
1531
1532 case CB_GETEDITSEL:
1533 if( lphc->wState & CBF_EDIT )
1534 {
1535 INT a, b;
1536
1537 return SendMessageA( lphc->hWndEdit, EM_GETSEL,
1538 (wParam) ? wParam : (WPARAM)&a,
1539 (lParam) ? lParam : (LPARAM)&b );
1540 }
1541 return CB_ERR;
1542
1543
1544 case CB_SETEDITSEL:
1545 if( lphc->wState & CBF_EDIT )
1546 return SendMessageA( lphc->hWndEdit, EM_SETSEL,
1547 (INT)(INT)LOWORD(lParam), (INT)(INT)HIWORD(lParam) );
1548 return CB_ERR;
1549
1550
1551 case CB_SETEXTENDEDUI:
1552 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) return CB_ERR;
1553
1554 if( wParam )
1555 lphc->wState |= CBF_EUI;
1556 else lphc->wState &= ~CBF_EUI;
1557 return CB_OKAY;
1558
1559
1560 case CB_GETEXTENDEDUI:
1561 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
1562
1563 case (WM_USER + 0x1B):
1564 DPRINT( "[%04x]: undocumented msg!\n", hwnd );
1565 }
1566 return DefWindowProcA(hwnd, message, wParam, lParam);
1567 }
1568 return CB_ERR;
1569 }
1570