changed registering builtin classes to unicode
[reactos.git] / reactos / lib / user32 / controls / combo.c
1 /*
2 * Combo controls
3 *
4 * Copyright 1997 Alex Korobka
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 * FIXME: roll up in Netscape 3.01.
21 */
22 /*
23 * COPYRIGHT: See COPYING in the top level directory
24 * PROJECT: ReactOS User32
25 * PURPOSE: combobox control
26 * FILE: lib/user32/controls/combo.c
27 * PROGRAMER: Steven Edwards
28 * REVISION HISTORY: 2003/06/25 SAE Created
29 * NOTES: Adapted from Wine
30 */
31
32 #include <stdio.h>
33 #include <string.h>
34
35 #include <windows.h>
36 #include <user32.h>
37 #include <debug.h>
38
39 #include "user32/regcontrol.h"
40 #include "controls.h"
41
42 /* bits in the dwKeyData */
43 #define KEYDATA_ALT 0x2000
44 #define KEYDATA_PREVSTATE 0x4000
45
46 /*
47 * Additional combo box definitions
48 */
49
50 #define CB_NOTIFY( lphc, code ) \
51 (SendMessageW((lphc)->owner, WM_COMMAND, \
52 MAKEWPARAM(GetWindowLongA((lphc)->self,GWL_ID), (code)), (LPARAM)(lphc)->self))
53
54 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
55 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
56 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
57 #define CB_HWND( lphc ) ((lphc)->self)
58
59 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
60
61 /*
62 * Drawing globals
63 */
64 static HBITMAP hComboBmp = 0;
65 static UINT CBitHeight, CBitWidth;
66
67 /*
68 * Look and feel dependant "constants"
69 */
70
71 #define COMBO_YBORDERGAP 5
72 #define COMBO_XBORDERSIZE() ( 2 )
73 #define COMBO_YBORDERSIZE() ( 2 )
74 #define COMBO_EDITBUTTONSPACE() ( 0 )
75 #define EDIT_CONTROL_PADDING() ( 1 )
76
77 // static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
78 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
79
80 /*********************************************************************
81 * combo class descriptor
82 */
83 const struct builtin_class_descr COMBO_builtin_class =
84 {
85 L"ComboBox", /* name */
86 CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS, /* style */
87 (WNDPROC) ComboWndProcW, /* procW */
88 sizeof(HEADCOMBO *), /* extra */
89 (LPCWSTR) IDC_ARROW, /* cursor */
90 0 /* brush */
91 };
92
93
94 /***********************************************************************
95 * COMBO_Init
96 *
97 * Load combo button bitmap.
98 */
99 static BOOL COMBO_Init()
100 {
101 HDC hDC;
102
103 if( hComboBmp ) return TRUE;
104 if( (hDC = CreateCompatibleDC(0)) )
105 {
106 BOOL bRet = FALSE;
107 if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
108 {
109 BITMAP bm;
110 HBITMAP hPrevB;
111 RECT r;
112
113 GetObjectW( hComboBmp, sizeof(bm), &bm );
114 CBitHeight = bm.bmHeight;
115 CBitWidth = bm.bmWidth;
116
117 DbgPrint("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
118
119 hPrevB = SelectObject( hDC, hComboBmp);
120 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
121 InvertRect( hDC, &r );
122 SelectObject( hDC, hPrevB );
123 bRet = TRUE;
124 }
125 DeleteDC( hDC );
126 return bRet;
127 }
128 return FALSE;
129 }
130
131 /***********************************************************************
132 * COMBO_NCCreate
133 */
134 static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
135 {
136 LPHEADCOMBO lphc;
137
138 if (COMBO_Init() && (lphc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEADCOMBO))) )
139 {
140 lphc->self = hwnd;
141 SetWindowLongA( hwnd, 0, (LONG)lphc );
142
143 /* some braindead apps do try to use scrollbar/border flags */
144
145 lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
146 SetWindowLongA( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) );
147
148 /*
149 * We also have to remove the client edge style to make sure
150 * we don't end-up with a non client area.
151 */
152 SetWindowLongA( hwnd, GWL_EXSTYLE,
153 GetWindowLongA( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
154
155 if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
156 lphc->dwStyle |= CBS_HASSTRINGS;
157 if( !(GetWindowLongA( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
158 lphc->wState |= CBF_NOTIFY;
159
160 DbgPrint("[%p], style = %08x\n", lphc, lphc->dwStyle );
161 return TRUE;
162 }
163 return FALSE;
164 }
165
166 /***********************************************************************
167 * COMBO_NCDestroy
168 */
169 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
170 {
171
172 if( lphc )
173 {
174 DbgPrint("[%p]: freeing storage\n", lphc->self);
175
176 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
177 DestroyWindow( lphc->hWndLBox );
178
179 SetWindowLongA( lphc->self, 0, 0 );
180 HeapFree( GetProcessHeap(), 0, lphc );
181 }
182 return 0;
183 }
184
185 /***********************************************************************
186 * CBGetTextAreaHeight
187 *
188 * This method will calculate the height of the text area of the
189 * combobox.
190 * The height of the text area is set in two ways.
191 * It can be set explicitly through a combobox message or through a
192 * WM_MEASUREITEM callback.
193 * If this is not the case, the height is set to 13 dialog units.
194 * This height was determined through experimentation.
195 */
196 static INT CBGetTextAreaHeight(
197 HWND hwnd,
198 LPHEADCOMBO lphc)
199 {
200 INT iTextItemHeight;
201
202 if( lphc->editHeight ) /* explicitly set height */
203 {
204 iTextItemHeight = lphc->editHeight;
205 }
206 else
207 {
208 TEXTMETRICW tm;
209 HDC hDC = GetDC(hwnd);
210 HFONT hPrevFont = 0;
211 INT baseUnitY;
212
213 if (lphc->hFont)
214 hPrevFont = SelectObject( hDC, lphc->hFont );
215
216 GetTextMetricsW(hDC, &tm);
217
218 baseUnitY = tm.tmHeight;
219
220 if( hPrevFont )
221 SelectObject( hDC, hPrevFont );
222
223 ReleaseDC(hwnd, hDC);
224
225 #if 0
226 iTextItemHeight = ((13 * baseUnitY) / 8);
227
228 /*
229 * This "formula" calculates the height of the complete control.
230 * To calculate the height of the text area, we have to remove the
231 * borders.
232 */
233 iTextItemHeight -= 2*COMBO_YBORDERSIZE();
234 #endif
235
236 /* Joakim: This seems to work better, the old formula caused the combo box
237 to be waaay to big with big font sizes */
238 iTextItemHeight = baseUnitY+2*COMBO_YBORDERSIZE();
239 }
240
241 /*
242 * Check the ownerdraw case if we haven't asked the parent the size
243 * of the item yet.
244 */
245 if ( CB_OWNERDRAWN(lphc) &&
246 (lphc->wState & CBF_MEASUREITEM) )
247 {
248 MEASUREITEMSTRUCT measureItem;
249 RECT clientRect;
250 INT originalItemHeight = iTextItemHeight;
251 UINT id = GetWindowLongA( lphc->self, GWL_ID );
252
253 /*
254 * We use the client rect for the width of the item.
255 */
256 GetClientRect(hwnd, &clientRect);
257
258 lphc->wState &= ~CBF_MEASUREITEM;
259
260 /*
261 * Send a first one to measure the size of the text area
262 */
263 measureItem.CtlType = ODT_COMBOBOX;
264 measureItem.CtlID = id;
265 measureItem.itemID = -1;
266 measureItem.itemWidth = clientRect.right;
267 measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
268 measureItem.itemData = 0;
269 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
270 iTextItemHeight = 6 + measureItem.itemHeight;
271
272 /*
273 * Send a second one in the case of a fixed ownerdraw list to calculate the
274 * size of the list items. (we basically do this on behalf of the listbox)
275 */
276 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
277 {
278 measureItem.CtlType = ODT_COMBOBOX;
279 measureItem.CtlID = id;
280 measureItem.itemID = 0;
281 measureItem.itemWidth = clientRect.right;
282 measureItem.itemHeight = originalItemHeight;
283 measureItem.itemData = 0;
284 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
285 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
286 }
287
288 /*
289 * Keep the size for the next time
290 */
291 lphc->editHeight = iTextItemHeight;
292 }
293
294 return iTextItemHeight;
295 }
296
297 /***********************************************************************
298 * CBForceDummyResize
299 *
300 * The dummy resize is used for listboxes that have a popup to trigger
301 * a re-arranging of the contents of the combobox and the recalculation
302 * of the size of the "real" control window.
303 */
304 static void CBForceDummyResize(
305 LPHEADCOMBO lphc)
306 {
307 RECT windowRect;
308 int newComboHeight;
309
310 newComboHeight = CBGetTextAreaHeight(lphc->self,lphc) + 2*COMBO_YBORDERSIZE();
311
312 GetWindowRect(lphc->self, &windowRect);
313
314 /*
315 * We have to be careful, resizing a combobox also has the meaning that the
316 * dropped rect will be resized. In this case, we want to trigger a resize
317 * to recalculate layout but we don't want to change the dropped rectangle
318 * So, we pass the height of text area of control as the height.
319 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
320 * message.
321 */
322 SetWindowPos( lphc->self,
323 NULL,
324 0, 0,
325 windowRect.right - windowRect.left,
326 newComboHeight,
327 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
328 }
329
330 /***********************************************************************
331 * CBCalcPlacement
332 *
333 * Set up component coordinates given valid lphc->RectCombo.
334 */
335 static void CBCalcPlacement(
336 HWND hwnd,
337 LPHEADCOMBO lphc,
338 LPRECT lprEdit,
339 LPRECT lprButton,
340 LPRECT lprLB)
341 {
342 /*
343 * Again, start with the client rectangle.
344 */
345 GetClientRect(hwnd, lprEdit);
346
347 /*
348 * Remove the borders
349 */
350 InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
351
352 /*
353 * Chop off the bottom part to fit with the height of the text area.
354 */
355 lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
356
357 /*
358 * The button starts the same vertical position as the text area.
359 */
360 CopyRect(lprButton, lprEdit);
361
362 /*
363 * If the combobox is "simple" there is no button.
364 */
365 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
366 lprButton->left = lprButton->right = lprButton->bottom = 0;
367 else
368 {
369 /*
370 * Let's assume the combobox button is the same width as the
371 * scrollbar button.
372 * size the button horizontally and cut-off the text area.
373 */
374 lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
375 lprEdit->right = lprButton->left;
376 }
377
378 /*
379 * In the case of a dropdown, there is an additional spacing between the
380 * text area and the button.
381 */
382 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
383 {
384 lprEdit->right -= COMBO_EDITBUTTONSPACE();
385 }
386
387 /*
388 * If we have an edit control, we space it away from the borders slightly.
389 */
390 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
391 {
392 InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
393 }
394
395 /*
396 * Adjust the size of the listbox popup.
397 */
398 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
399 {
400 /*
401 * Use the client rectangle to initialize the listbox rectangle
402 */
403 GetClientRect(hwnd, lprLB);
404
405 /*
406 * Then, chop-off the top part.
407 */
408 lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
409 }
410 else
411 {
412 /*
413 * Make sure the dropped width is as large as the combobox itself.
414 */
415 if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
416 {
417 lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
418
419 /*
420 * In the case of a dropdown, the popup listbox is offset to the right.
421 * so, we want to make sure it's flush with the right side of the
422 * combobox
423 */
424 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
425 lprLB->right -= COMBO_EDITBUTTONSPACE();
426 }
427 else
428 lprLB->right = lprLB->left + lphc->droppedWidth;
429 }
430
431 DbgPrint("\ttext\t= (%ld,%ld-%ld,%ld)\n",
432 lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
433
434 DbgPrint("\tbutton\t= (%ld,%ld-%ld,%ld)\n",
435 lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
436
437 DbgPrint("\tlbox\t= (%ld,%ld-%ld,%ld)\n",
438 lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
439 }
440
441 /***********************************************************************
442 * CBGetDroppedControlRect
443 */
444 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
445 {
446 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
447 of the combo box and the lower right corner of the listbox */
448
449 GetWindowRect(lphc->self, lpRect);
450
451 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
452 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
453
454 }
455
456 /***********************************************************************
457 * COMBO_WindowPosChanging
458 */
459 static LRESULT COMBO_WindowPosChanging(
460 HWND hwnd,
461 LPHEADCOMBO lphc,
462 WINDOWPOS* posChanging)
463 {
464 /*
465 * We need to override the WM_WINDOWPOSCHANGING method to handle all
466 * the non-simple comboboxes. The problem is that those controls are
467 * always the same height. We have to make sure they are not resized
468 * to another value.
469 */
470 if ( ( CB_GETTYPE(lphc) != CBS_SIMPLE ) &&
471 ((posChanging->flags & SWP_NOSIZE) == 0) )
472 {
473 int newComboHeight;
474
475 newComboHeight = CBGetTextAreaHeight(hwnd,lphc) +
476 2*COMBO_YBORDERSIZE();
477
478 /*
479 * Resizing a combobox has another side effect, it resizes the dropped
480 * rectangle as well. However, it does it only if the new height for the
481 * combobox is different from the height it should have. In other words,
482 * if the application resizing the combobox only had the intention to resize
483 * the actual control, for example, to do the layout of a dialog that is
484 * resized, the height of the dropdown is not changed.
485 */
486 if (posChanging->cy != newComboHeight)
487 {
488 DbgPrint("posChanging->cy=%d, newComboHeight=%d, oldbot=%ld, oldtop=%ld\n",
489 posChanging->cy, newComboHeight, lphc->droppedRect.bottom,
490 lphc->droppedRect.top);
491 lphc->droppedRect.bottom = lphc->droppedRect.top + posChanging->cy - newComboHeight;
492
493 posChanging->cy = newComboHeight;
494 }
495 }
496
497 return 0;
498 }
499
500 /***********************************************************************
501 * COMBO_Create
502 */
503 static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style,
504 BOOL unicode )
505 {
506 static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
507 static const WCHAR editName[] = {'E','d','i','t',0};
508
509 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
510 if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
511
512 lphc->owner = hwndParent;
513
514 /*
515 * The item height and dropped width are not set when the control
516 * is created.
517 */
518 lphc->droppedWidth = lphc->editHeight = 0;
519
520 /*
521 * The first time we go through, we want to measure the ownerdraw item
522 */
523 lphc->wState |= CBF_MEASUREITEM;
524
525 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
526
527 if( lphc->owner || !(style & WS_VISIBLE) )
528 {
529 UINT lbeStyle = 0;
530 UINT lbeExStyle = 0;
531
532 /*
533 * Initialize the dropped rect to the size of the client area of the
534 * control and then, force all the areas of the combobox to be
535 * recalculated.
536 */
537 GetClientRect( hwnd, &lphc->droppedRect );
538 CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
539
540 /*
541 * Adjust the position of the popup listbox if it's necessary
542 */
543 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
544 {
545 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
546
547 /*
548 * If it's a dropdown, the listbox is offset
549 */
550 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
551 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
552
553 ClientToScreen(hwnd, (LPPOINT)&lphc->droppedRect);
554 ClientToScreen(hwnd, (LPPOINT)&lphc->droppedRect.right);
555 }
556
557 /* create listbox popup */
558
559 lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
560 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
561
562 if( lphc->dwStyle & CBS_SORT )
563 lbeStyle |= LBS_SORT;
564 if( lphc->dwStyle & CBS_HASSTRINGS )
565 lbeStyle |= LBS_HASSTRINGS;
566 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
567 lbeStyle |= LBS_NOINTEGRALHEIGHT;
568 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
569 lbeStyle |= LBS_DISABLENOSCROLL;
570
571 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
572 {
573 lbeStyle |= WS_VISIBLE;
574
575 /*
576 * In win 95 look n feel, the listbox in the simple combobox has
577 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
578 */
579 //if (TWEAK_WineLook > WIN31_LOOK)
580 //{
581 lbeStyle &= ~WS_BORDER;
582 lbeExStyle |= WS_EX_CLIENTEDGE;
583 //}
584 }
585
586 if (unicode)
587 lphc->hWndLBox = CreateWindowExW(lbeExStyle, clbName, NULL, lbeStyle,
588 lphc->droppedRect.left,
589 lphc->droppedRect.top,
590 lphc->droppedRect.right - lphc->droppedRect.left,
591 lphc->droppedRect.bottom - lphc->droppedRect.top,
592 hwnd, (HMENU)ID_CB_LISTBOX,
593 (HINSTANCE)GetWindowLongA( hwnd, GWL_HINSTANCE ), lphc );
594 else
595 lphc->hWndLBox = CreateWindowExA(lbeExStyle, "ComboLBox", NULL, lbeStyle,
596 lphc->droppedRect.left,
597 lphc->droppedRect.top,
598 lphc->droppedRect.right - lphc->droppedRect.left,
599 lphc->droppedRect.bottom - lphc->droppedRect.top,
600 hwnd, (HMENU)ID_CB_LISTBOX,
601 (HINSTANCE)GetWindowLongA( hwnd, GWL_HINSTANCE ), lphc );
602
603 if( lphc->hWndLBox )
604 {
605 BOOL bEdit = TRUE;
606 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
607
608 /*
609 * In Win95 look, the border fo the edit control is
610 * provided by the combobox
611 */
612 //if (TWEAK_WineLook == WIN31_LOOK)
613 // lbeStyle |= WS_BORDER;
614
615 if( lphc->wState & CBF_EDIT )
616 {
617 if( lphc->dwStyle & CBS_OEMCONVERT )
618 lbeStyle |= ES_OEMCONVERT;
619 if( lphc->dwStyle & CBS_AUTOHSCROLL )
620 lbeStyle |= ES_AUTOHSCROLL;
621 if( lphc->dwStyle & CBS_LOWERCASE )
622 lbeStyle |= ES_LOWERCASE;
623 else if( lphc->dwStyle & CBS_UPPERCASE )
624 lbeStyle |= ES_UPPERCASE;
625
626 if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
627
628 if (unicode)
629 lphc->hWndEdit = CreateWindowExW(0, editName, NULL, lbeStyle,
630 lphc->textRect.left, lphc->textRect.top,
631 lphc->textRect.right - lphc->textRect.left,
632 lphc->textRect.bottom - lphc->textRect.top,
633 hwnd, (HMENU)ID_CB_EDIT,
634 (HINSTANCE)GetWindowLongA( hwnd, GWL_HINSTANCE ), NULL );
635 else
636 lphc->hWndEdit = CreateWindowExA(0, "Edit", NULL, lbeStyle,
637 lphc->textRect.left, lphc->textRect.top,
638 lphc->textRect.right - lphc->textRect.left,
639 lphc->textRect.bottom - lphc->textRect.top,
640 hwnd, (HMENU)ID_CB_EDIT,
641 (HINSTANCE)GetWindowLongA( hwnd, GWL_HINSTANCE ), NULL );
642
643 if( !lphc->hWndEdit )
644 bEdit = FALSE;
645 }
646
647 if( bEdit )
648 {
649 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
650 {
651 /* Now do the trick with parent */
652 SetParent(lphc->hWndLBox, HWND_DESKTOP);
653 /*
654 * If the combo is a dropdown, we must resize the control
655 * to fit only the text area and button. To do this,
656 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
657 * will take care of setting the height for us.
658 */
659 CBForceDummyResize(lphc);
660 }
661
662 OutputDebugStringA("init done\n");
663 return 0;
664 }
665 OutputDebugStringA("edit control failure.\n");
666 } else OutputDebugStringA("listbox failure.\n");
667 } else OutputDebugStringA("no owner for visible combo.\n");
668
669 /* CreateWindow() will send WM_NCDESTROY to cleanup */
670
671 return -1;
672 }
673
674 /***********************************************************************
675 * CBPaintButton
676 *
677 * Paint combo button (normal, pressed, and disabled states).
678 */
679 static void CBPaintButton(
680 LPHEADCOMBO lphc,
681 HDC hdc,
682 RECT rectButton)
683 {
684 UINT buttonState;
685
686 if( lphc->wState & CBF_NOREDRAW )
687 return;
688
689 buttonState = DFCS_SCROLLCOMBOBOX;
690
691 if (lphc->wState & CBF_BUTTONDOWN)
692 {
693 buttonState |= DFCS_PUSHED;
694 }
695
696 if (CB_DISABLED(lphc))
697 {
698 buttonState |= DFCS_INACTIVE;
699 }
700
701 DrawFrameControl(hdc,
702 &rectButton,
703 DFC_SCROLL,
704 buttonState);
705 }
706
707 /***********************************************************************
708 * CBPaintText
709 *
710 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
711 */
712 static void CBPaintText(
713 LPHEADCOMBO lphc,
714 HDC hdc,
715 RECT rectEdit)
716 {
717 INT id, size = 0;
718 LPWSTR pText = NULL;
719
720 if( lphc->wState & CBF_NOREDRAW ) return;
721
722 OutputDebugStringA("\n");
723
724 /* follow Windows combobox that sends a bunch of text
725 * inquiries to its listbox while processing WM_PAINT. */
726
727 if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
728 {
729 size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
730 if (size == LB_ERR)
731 FIXME("LB_ERR probably not handled yet\n");
732 if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
733 {
734 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
735 size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText);
736 pText[size] = '\0'; /* just in case */
737 } else return;
738 }
739 else
740 if( !CB_OWNERDRAWN(lphc) )
741 return;
742
743 if( lphc->wState & CBF_EDIT )
744 {
745 static const WCHAR empty_stringW[] = { 0 };
746 if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
747 if( lphc->wState & CBF_FOCUSED )
748 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
749 }
750 else /* paint text field ourselves */
751 {
752 UINT itemState = ODS_COMBOBOXEDIT;
753 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
754
755 /*
756 * Give ourselves some space.
757 */
758 InflateRect( &rectEdit, -1, -1 );
759
760 if( CB_OWNERDRAWN(lphc) )
761 {
762 DRAWITEMSTRUCT dis;
763 HRGN clipRegion;
764 UINT ctlid = GetWindowLongA( lphc->self, GWL_ID );
765
766 /* setup state for DRAWITEM message. Owner will highlight */
767 if ( (lphc->wState & CBF_FOCUSED) &&
768 !(lphc->wState & CBF_DROPPED) )
769 itemState |= ODS_SELECTED | ODS_FOCUS;
770
771 /*
772 * Save the current clip region.
773 * To retrieve the clip region, we need to create one "dummy"
774 * clip region.
775 */
776 clipRegion = CreateRectRgnIndirect(&rectEdit);
777
778 if (GetClipRgn(hdc, clipRegion)!=1)
779 {
780 DeleteObject(clipRegion);
781 clipRegion=NULL;
782 }
783
784 if (!IsWindowEnabled(lphc->self) & WS_DISABLED) itemState |= ODS_DISABLED;
785
786 dis.CtlType = ODT_COMBOBOX;
787 dis.CtlID = ctlid;
788 dis.hwndItem = lphc->self;
789 dis.itemAction = ODA_DRAWENTIRE;
790 dis.itemID = id;
791 dis.itemState = itemState;
792 dis.hDC = hdc;
793 dis.rcItem = rectEdit;
794 dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA,
795 (WPARAM)id, 0 );
796
797 /*
798 * Clip the DC and have the parent draw the item.
799 */
800 IntersectClipRect(hdc,
801 rectEdit.left, rectEdit.top,
802 rectEdit.right, rectEdit.bottom);
803
804 SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
805
806 /*
807 * Reset the clipping region.
808 */
809 SelectClipRgn(hdc, clipRegion);
810 }
811 else
812 {
813 static const WCHAR empty_stringW[] = { 0 };
814
815 if ( (lphc->wState & CBF_FOCUSED) &&
816 !(lphc->wState & CBF_DROPPED) ) {
817
818 /* highlight */
819 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
820 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
821 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
822 }
823
824 ExtTextOutW( hdc,
825 rectEdit.left + 1,
826 rectEdit.top + 1,
827 ETO_OPAQUE | ETO_CLIPPED,
828 &rectEdit,
829 pText ? pText : empty_stringW , size, NULL );
830
831 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
832 DrawFocusRect( hdc, &rectEdit );
833 }
834
835 if( hPrevFont )
836 SelectObject(hdc, hPrevFont );
837 }
838 if (pText)
839 HeapFree( GetProcessHeap(), 0, pText );
840 }
841
842 /***********************************************************************
843 * CBPaintBorder
844 */
845 static void CBPaintBorder(
846 HWND hwnd,
847 LPHEADCOMBO lphc,
848 HDC hdc)
849 {
850 RECT clientRect;
851
852 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
853 {
854 GetClientRect(hwnd, &clientRect);
855 }
856 else
857 {
858 CopyRect(&clientRect, &lphc->textRect);
859
860 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
861 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
862 }
863
864 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
865 }
866
867 /***********************************************************************
868 * COMBO_PrepareColors
869 *
870 * This method will sent the appropriate WM_CTLCOLOR message to
871 * prepare and setup the colors for the combo's DC.
872 *
873 * It also returns the brush to use for the background.
874 */
875 static HBRUSH COMBO_PrepareColors(
876 LPHEADCOMBO lphc,
877 HDC hDC)
878 {
879 HBRUSH hBkgBrush;
880
881 /*
882 * Get the background brush for this control.
883 */
884 if (CB_DISABLED(lphc))
885 {
886 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
887 (WPARAM)hDC, (LPARAM)lphc->self );
888
889 /*
890 * We have to change the text color since WM_CTLCOLORSTATIC will
891 * set it to the "enabled" color. This is the same behavior as the
892 * edit control
893 */
894 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
895 }
896 else
897 {
898 if (lphc->wState & CBF_EDIT)
899 {
900 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
901 (WPARAM)hDC, (LPARAM)lphc->self );
902 }
903 else
904 {
905 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORLISTBOX,
906 (WPARAM)hDC, (LPARAM)lphc->self );
907 }
908 }
909
910 /*
911 * Catch errors.
912 */
913 if( !hBkgBrush )
914 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
915
916 return hBkgBrush;
917 }
918
919 /***********************************************************************
920 * COMBO_EraseBackground
921 */
922 static LRESULT COMBO_EraseBackground(
923 HWND hwnd,
924 LPHEADCOMBO lphc,
925 HDC hParamDC)
926 {
927 HBRUSH hBkgBrush;
928 HDC hDC;
929
930 if(lphc->wState & CBF_EDIT)
931 return TRUE;
932
933 hDC = (hParamDC) ? hParamDC
934 : GetDC(hwnd);
935 /*
936 * Retrieve the background brush
937 */
938 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
939
940 FillRect(hDC, &lphc->textRect, hBkgBrush);
941
942 if (!hParamDC)
943 ReleaseDC(hwnd, hDC);
944
945 return TRUE;
946 }
947
948 /***********************************************************************
949 * COMBO_Paint
950 */
951 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
952 {
953 PAINTSTRUCT ps;
954 HDC hDC;
955
956 hDC = (hParamDC) ? hParamDC
957 : BeginPaint( lphc->self, &ps);
958
959 DbgPrint("hdc=%p\n", hDC);
960
961 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
962 {
963 HBRUSH hPrevBrush, hBkgBrush;
964
965 /*
966 * Retrieve the background brush and select it in the
967 * DC.
968 */
969 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
970
971 hPrevBrush = SelectObject( hDC, hBkgBrush );
972
973 /*
974 * In non 3.1 look, there is a sunken border on the combobox
975 */
976 //if (TWEAK_WineLook != WIN31_LOOK)
977 //{
978 CBPaintBorder(lphc->self, lphc, hDC);
979 //}
980
981 if( !IsRectEmpty(&lphc->buttonRect) )
982 {
983 CBPaintButton(lphc, hDC, lphc->buttonRect);
984 }
985
986 /* paint the edit control padding area */
987 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
988 {
989 RECT rPadEdit = lphc->textRect;
990
991 InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
992
993 FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
994 }
995
996 if( !(lphc->wState & CBF_EDIT) )
997 {
998 CBPaintText( lphc, hDC, lphc->textRect);
999 }
1000
1001 if( hPrevBrush )
1002 SelectObject( hDC, hPrevBrush );
1003 }
1004
1005 if( !hParamDC )
1006 EndPaint(lphc->self, &ps);
1007
1008 return 0;
1009 }
1010
1011 /***********************************************************************
1012 * CBUpdateLBox
1013 *
1014 * Select listbox entry according to the contents of the edit control.
1015 */
1016 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
1017 {
1018 INT length, idx;
1019 LPWSTR pText = NULL;
1020
1021 idx = LB_ERR;
1022 length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
1023
1024 if( length > 0 )
1025 pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1026
1027 DbgPrint("\t edit text length %i\n", length );
1028
1029 if( pText )
1030 {
1031 if( length ) GetWindowTextW( lphc->hWndEdit, pText, length + 1);
1032 else pText[0] = '\0';
1033 idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING,
1034 (WPARAM)(-1), (LPARAM)pText );
1035 HeapFree( GetProcessHeap(), 0, pText );
1036 }
1037
1038 SendMessageW(lphc->hWndLBox, LB_SETCURSEL, (WPARAM)(bSelect ? idx : -1), 0);
1039
1040 /* probably superfluous but Windows sends this too */
1041 SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
1042 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
1043
1044 return idx;
1045 }
1046
1047 /***********************************************************************
1048 * CBUpdateEdit
1049 *
1050 * Copy a listbox entry to the edit control.
1051 */
1052 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
1053 {
1054 INT length;
1055 LPWSTR pText = NULL;
1056 static const WCHAR empty_stringW[] = { 0 };
1057
1058 DbgPrint("\t %i\n", index );
1059
1060 if( index >= 0 ) /* got an entry */
1061 {
1062 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1063 if( length != LB_ERR)
1064 {
1065 if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
1066 {
1067 SendMessageW(lphc->hWndLBox, LB_GETTEXT,
1068 (WPARAM)index, (LPARAM)pText );
1069 }
1070 }
1071 }
1072
1073 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1074 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
1075 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1076
1077 if( lphc->wState & CBF_FOCUSED )
1078 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1079
1080 if( pText )
1081 HeapFree( GetProcessHeap(), 0, pText );
1082 }
1083
1084 /***********************************************************************
1085 * CBDropDown
1086 *
1087 * Show listbox popup.
1088 */
1089 static void CBDropDown( LPHEADCOMBO lphc )
1090 {
1091 RECT rect,r;
1092 int nItems = 0;
1093 int nDroppedHeight;
1094
1095 DbgPrint("[%p]: drop down\n", lphc->self);
1096
1097 CB_NOTIFY( lphc, CBN_DROPDOWN );
1098
1099 /* set selection */
1100
1101 lphc->wState |= CBF_DROPPED;
1102 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1103 {
1104 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
1105
1106 /* Update edit only if item is in the list */
1107 if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
1108 CBUpdateEdit( lphc, lphc->droppedIndex );
1109 }
1110 else
1111 {
1112 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1113
1114 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
1115 (WPARAM)(lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex), 0 );
1116 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1117 }
1118
1119 /* now set popup position */
1120 GetWindowRect( lphc->self, &rect );
1121
1122 /*
1123 * If it's a dropdown, the listbox is offset
1124 */
1125 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1126 rect.left += COMBO_EDITBUTTONSPACE();
1127
1128 /* if the dropped height is greater than the total height of the dropped
1129 items list, then force the drop down list height to be the total height
1130 of the items in the dropped list */
1131
1132 /* And Remove any extra space (Best Fit) */
1133 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1134 /* if listbox length has been set directly by its handle */
1135 GetWindowRect(lphc->hWndLBox, &r);
1136 if (nDroppedHeight < r.bottom - r.top)
1137 nDroppedHeight = r.bottom - r.top;
1138 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1139
1140 if (nItems > 0)
1141 {
1142 int nHeight;
1143 int nIHeight;
1144
1145 nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1146
1147 nHeight = nIHeight*nItems;
1148
1149 if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1150 nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1151
1152 if (nDroppedHeight < nIHeight)
1153 {
1154 if (nItems < 5)
1155 nDroppedHeight = (nItems+1)*nIHeight;
1156 else
1157 nDroppedHeight = 6*nIHeight;
1158 }
1159 }
1160
1161 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1162 if( (rect.bottom + nDroppedHeight) >= GetSystemMetrics( SM_CYSCREEN ) )
1163 rect.bottom = rect.top - nDroppedHeight;
1164
1165 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1166 lphc->droppedRect.right - lphc->droppedRect.left,
1167 nDroppedHeight,
1168 SWP_NOACTIVATE | SWP_SHOWWINDOW);
1169
1170
1171 if( !(lphc->wState & CBF_NOREDRAW) )
1172 RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
1173 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1174
1175 EnableWindow( lphc->hWndLBox, TRUE );
1176 if (GetCapture() != lphc->self)
1177 SetCapture(lphc->hWndLBox);
1178 }
1179
1180 /***********************************************************************
1181 * CBRollUp
1182 *
1183 * Hide listbox popup.
1184 */
1185 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1186 {
1187 HWND hWnd = lphc->self;
1188
1189 DbgPrint("[%p]: sel ok? [%i] dropped? [%i]\n",
1190 lphc->self, (INT)ok, (INT)(lphc->wState & CBF_DROPPED));
1191
1192 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1193
1194 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1195 {
1196
1197 if( lphc->wState & CBF_DROPPED )
1198 {
1199 RECT rect;
1200
1201 lphc->wState &= ~CBF_DROPPED;
1202 ShowWindow( lphc->hWndLBox, SW_HIDE );
1203
1204 if(GetCapture() == lphc->hWndLBox)
1205 {
1206 ReleaseCapture();
1207 }
1208
1209 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1210 {
1211 rect = lphc->buttonRect;
1212 }
1213 else
1214 {
1215 if( bButton )
1216 {
1217 UnionRect( &rect,
1218 &lphc->buttonRect,
1219 &lphc->textRect);
1220 }
1221 else
1222 rect = lphc->textRect;
1223
1224 bButton = TRUE;
1225 }
1226
1227 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1228 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1229 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1230 CB_NOTIFY( lphc, CBN_CLOSEUP );
1231 }
1232 }
1233 }
1234
1235 /***********************************************************************
1236 * COMBO_FlipListbox
1237 *
1238 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1239 */
1240 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1241 {
1242 if( lphc->wState & CBF_DROPPED )
1243 {
1244 CBRollUp( lphc, ok, bRedrawButton );
1245 return FALSE;
1246 }
1247
1248 CBDropDown( lphc );
1249 return TRUE;
1250 }
1251
1252 /***********************************************************************
1253 * CBRepaintButton
1254 */
1255 static void CBRepaintButton( LPHEADCOMBO lphc )
1256 {
1257 InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1258 UpdateWindow(lphc->self);
1259 }
1260
1261 /***********************************************************************
1262 * COMBO_SetFocus
1263 */
1264 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1265 {
1266 if( !(lphc->wState & CBF_FOCUSED) )
1267 {
1268 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1269 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1270
1271 /* This is wrong. Message sequences seem to indicate that this
1272 is set *after* the notify. */
1273 /* lphc->wState |= CBF_FOCUSED; */
1274
1275 if( !(lphc->wState & CBF_EDIT) )
1276 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1277
1278 CB_NOTIFY( lphc, CBN_SETFOCUS );
1279 lphc->wState |= CBF_FOCUSED;
1280 }
1281 }
1282
1283 /***********************************************************************
1284 * COMBO_KillFocus
1285 */
1286 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1287 {
1288 HWND hWnd = lphc->self;
1289
1290 if( lphc->wState & CBF_FOCUSED )
1291 {
1292 CBRollUp( lphc, FALSE, TRUE );
1293 if( IsWindow( hWnd ) )
1294 {
1295 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1296 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1297
1298 lphc->wState &= ~CBF_FOCUSED;
1299
1300 /* redraw text */
1301 if( !(lphc->wState & CBF_EDIT) )
1302 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1303
1304 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1305 }
1306 }
1307 }
1308
1309 /***********************************************************************
1310 * COMBO_Command
1311 */
1312 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1313 {
1314 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1315 {
1316 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1317
1318 switch( HIWORD(wParam) >> 8 )
1319 {
1320 case (EN_SETFOCUS >> 8):
1321
1322 DbgPrint("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1323
1324 COMBO_SetFocus( lphc );
1325 break;
1326
1327 case (EN_KILLFOCUS >> 8):
1328
1329 DbgPrint("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1330
1331 /* NOTE: it seems that Windows' edit control sends an
1332 * undocumented message WM_USER + 0x1B instead of this
1333 * notification (only when it happens to be a part of
1334 * the combo). ?? - AK.
1335 */
1336
1337 COMBO_KillFocus( lphc );
1338 break;
1339
1340
1341 case (EN_CHANGE >> 8):
1342 /*
1343 * In some circumstances (when the selection of the combobox
1344 * is changed for example) we don't wans the EN_CHANGE notification
1345 * to be forwarded to the parent of the combobox. This code
1346 * checks a flag that is set in these occasions and ignores the
1347 * notification.
1348 */
1349 if (lphc->wState & CBF_NOLBSELECT)
1350 {
1351 lphc->wState &= ~CBF_NOLBSELECT;
1352 }
1353 else
1354 {
1355 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1356 }
1357
1358 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1359 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1360 break;
1361
1362 case (EN_UPDATE >> 8):
1363 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1364 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1365 break;
1366
1367 case (EN_ERRSPACE >> 8):
1368 CB_NOTIFY( lphc, CBN_ERRSPACE );
1369 }
1370 }
1371 else if( lphc->hWndLBox == hWnd )
1372 {
1373 switch( HIWORD(wParam) )
1374 {
1375 case LBN_ERRSPACE:
1376 CB_NOTIFY( lphc, CBN_ERRSPACE );
1377 break;
1378
1379 case LBN_DBLCLK:
1380 CB_NOTIFY( lphc, CBN_DBLCLK );
1381 break;
1382
1383 case LBN_SELCHANGE:
1384 case LBN_SELCANCEL:
1385
1386 DbgPrint("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1387
1388 if( HIWORD(wParam) == LBN_SELCHANGE)
1389 {
1390 if( lphc->wState & CBF_EDIT )
1391 {
1392 INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1393 lphc->wState |= CBF_NOLBSELECT;
1394 CBUpdateEdit( lphc, index );
1395 /* select text in edit, as Windows does */
1396 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1397 }
1398 else
1399 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1400 }
1401
1402 /* do not roll up if selection is being tracked
1403 * by arrowkeys in the dropdown listbox */
1404 if( ((lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP)) )
1405 {
1406 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1407 }
1408 else lphc->wState &= ~CBF_NOROLLUP;
1409
1410 CB_NOTIFY( lphc, CBN_SELCHANGE );
1411
1412 /* fall through */
1413
1414 case LBN_SETFOCUS:
1415 case LBN_KILLFOCUS:
1416 /* nothing to do here since ComboLBox always resets the focus to its
1417 * combo/edit counterpart */
1418 break;
1419 }
1420 }
1421 return 0;
1422 }
1423
1424 /***********************************************************************
1425 * COMBO_ItemOp
1426 *
1427 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1428 */
1429 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1430 {
1431 HWND hWnd = lphc->self;
1432 UINT id = GetWindowLongA( hWnd, GWL_ID );
1433
1434 DbgPrint("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1435
1436 switch( msg )
1437 {
1438 case WM_DELETEITEM:
1439 {
1440 DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1441 lpIS->CtlType = ODT_COMBOBOX;
1442 lpIS->CtlID = id;
1443 lpIS->hwndItem = hWnd;
1444 break;
1445 }
1446 case WM_DRAWITEM:
1447 {
1448 DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1449 lpIS->CtlType = ODT_COMBOBOX;
1450 lpIS->CtlID = id;
1451 lpIS->hwndItem = hWnd;
1452 break;
1453 }
1454 case WM_COMPAREITEM:
1455 {
1456 COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1457 lpIS->CtlType = ODT_COMBOBOX;
1458 lpIS->CtlID = id;
1459 lpIS->hwndItem = hWnd;
1460 break;
1461 }
1462 case WM_MEASUREITEM:
1463 {
1464 MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1465 lpIS->CtlType = ODT_COMBOBOX;
1466 lpIS->CtlID = id;
1467 break;
1468 }
1469 }
1470 return SendMessageW(lphc->owner, msg, id, lParam);
1471 }
1472
1473
1474 /***********************************************************************
1475 * COMBO_GetTextW
1476 */
1477 static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf )
1478 {
1479 INT length;
1480
1481 if( lphc->wState & CBF_EDIT )
1482 return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1483
1484 /* get it from the listbox */
1485
1486 if (!count || !buf) return 0;
1487 if( lphc->hWndLBox )
1488 {
1489 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1490 if (idx == LB_ERR) goto error;
1491 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1492 if (length == LB_ERR) goto error;
1493
1494 /* 'length' is without the terminating character */
1495 if (length >= count)
1496 {
1497 LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1498 if (!lpBuffer) goto error;
1499 length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1500
1501 /* truncate if buffer is too short */
1502 if (length != LB_ERR)
1503 {
1504 lstrcpynW( buf, lpBuffer, count );
1505 length = count;
1506 }
1507 HeapFree( GetProcessHeap(), 0, lpBuffer );
1508 }
1509 else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1510
1511 if (length == LB_ERR) return 0;
1512 return length;
1513 }
1514
1515 error: /* error - truncate string, return zero */
1516 buf[0] = 0;
1517 return 0;
1518 }
1519
1520
1521 /***********************************************************************
1522 * COMBO_GetTextA
1523 *
1524 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1525 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1526 */
1527 static LRESULT COMBO_GetTextA( LPHEADCOMBO lphc, INT count, LPSTR buf )
1528 {
1529 INT length;
1530
1531 if( lphc->wState & CBF_EDIT )
1532 return SendMessageA( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1533
1534 /* get it from the listbox */
1535
1536 if (!count || !buf) return 0;
1537 if( lphc->hWndLBox )
1538 {
1539 INT idx = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1540 if (idx == LB_ERR) goto error;
1541 length = SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1542 if (length == LB_ERR) goto error;
1543
1544 /* 'length' is without the terminating character */
1545 if (length >= count)
1546 {
1547 LPSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) );
1548 if (!lpBuffer) goto error;
1549 length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1550
1551 /* truncate if buffer is too short */
1552 if (length != LB_ERR)
1553 {
1554 lstrcpynA( buf, lpBuffer, count );
1555 length = count;
1556 }
1557 HeapFree( GetProcessHeap(), 0, lpBuffer );
1558 }
1559 else length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1560
1561 if (length == LB_ERR) return 0;
1562 return length;
1563 }
1564
1565 error: /* error - truncate string, return zero */
1566 buf[0] = 0;
1567 return 0;
1568 }
1569
1570
1571 /***********************************************************************
1572 * CBResetPos
1573 *
1574 * This function sets window positions according to the updated
1575 * component placement struct.
1576 */
1577 static void CBResetPos(
1578 LPHEADCOMBO lphc,
1579 LPRECT rectEdit,
1580 LPRECT rectLB,
1581 BOOL bRedraw)
1582 {
1583 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1584
1585 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1586 * sizing messages */
1587
1588 if( lphc->wState & CBF_EDIT )
1589 SetWindowPos( lphc->hWndEdit, 0,
1590 rectEdit->left, rectEdit->top,
1591 rectEdit->right - rectEdit->left,
1592 rectEdit->bottom - rectEdit->top,
1593 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1594
1595 SetWindowPos( lphc->hWndLBox, 0,
1596 rectLB->left, rectLB->top,
1597 rectLB->right - rectLB->left,
1598 rectLB->bottom - rectLB->top,
1599 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1600
1601 if( bDrop )
1602 {
1603 if( lphc->wState & CBF_DROPPED )
1604 {
1605 lphc->wState &= ~CBF_DROPPED;
1606 ShowWindow( lphc->hWndLBox, SW_HIDE );
1607 }
1608
1609 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1610 RedrawWindow( lphc->self, NULL, 0,
1611 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1612 }
1613 }
1614
1615
1616 /***********************************************************************
1617 * COMBO_Size
1618 */
1619 static void COMBO_Size( LPHEADCOMBO lphc )
1620 {
1621 CBCalcPlacement(lphc->self,
1622 lphc,
1623 &lphc->textRect,
1624 &lphc->buttonRect,
1625 &lphc->droppedRect);
1626
1627 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1628 }
1629
1630
1631 /***********************************************************************
1632 * COMBO_Font
1633 */
1634 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1635 {
1636 /*
1637 * Set the font
1638 */
1639 lphc->hFont = hFont;
1640
1641 /*
1642 * Propagate to owned windows.
1643 */
1644 if( lphc->wState & CBF_EDIT )
1645 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1646 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1647
1648 /*
1649 * Redo the layout of the control.
1650 */
1651 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1652 {
1653 CBCalcPlacement(lphc->self,
1654 lphc,
1655 &lphc->textRect,
1656 &lphc->buttonRect,
1657 &lphc->droppedRect);
1658
1659 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1660 }
1661 else
1662 {
1663 CBForceDummyResize(lphc);
1664 }
1665 }
1666
1667
1668 /***********************************************************************
1669 * COMBO_SetItemHeight
1670 */
1671 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1672 {
1673 LRESULT lRet = CB_ERR;
1674
1675 if( index == -1 ) /* set text field height */
1676 {
1677 if( height < 32768 )
1678 {
1679 lphc->editHeight = height;
1680
1681 /*
1682 * Redo the layout of the control.
1683 */
1684 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1685 {
1686 CBCalcPlacement(lphc->self,
1687 lphc,
1688 &lphc->textRect,
1689 &lphc->buttonRect,
1690 &lphc->droppedRect);
1691
1692 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1693 }
1694 else
1695 {
1696 CBForceDummyResize(lphc);
1697 }
1698
1699 lRet = height;
1700 }
1701 }
1702 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1703 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT,
1704 (WPARAM)index, (LPARAM)height );
1705 return lRet;
1706 }
1707
1708 /***********************************************************************
1709 * COMBO_SelectString
1710 */
1711 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1712 {
1713 INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText) :
1714 SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText);
1715 if( index >= 0 )
1716 {
1717 if( lphc->wState & CBF_EDIT )
1718 CBUpdateEdit( lphc, index );
1719 else
1720 {
1721 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1722 }
1723 }
1724 return (LRESULT)index;
1725 }
1726
1727 /***********************************************************************
1728 * COMBO_LButtonDown
1729 */
1730 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1731 {
1732 POINT pt;
1733 BOOL bButton;
1734 HWND hWnd = lphc->self;
1735
1736 pt.x = LOWORD(lParam);
1737 pt.y = HIWORD(lParam);
1738 bButton = PtInRect(&lphc->buttonRect, pt);
1739
1740 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1741 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1742 {
1743 lphc->wState |= CBF_BUTTONDOWN;
1744 if( lphc->wState & CBF_DROPPED )
1745 {
1746 /* got a click to cancel selection */
1747
1748 lphc->wState &= ~CBF_BUTTONDOWN;
1749 CBRollUp( lphc, TRUE, FALSE );
1750 if( !IsWindow( hWnd ) ) return;
1751
1752 if( lphc->wState & CBF_CAPTURE )
1753 {
1754 lphc->wState &= ~CBF_CAPTURE;
1755 ReleaseCapture();
1756 }
1757 }
1758 else
1759 {
1760 /* drop down the listbox and start tracking */
1761
1762 lphc->wState |= CBF_CAPTURE;
1763 SetCapture( hWnd );
1764 CBDropDown( lphc );
1765 }
1766 if( bButton ) CBRepaintButton( lphc );
1767 }
1768 }
1769
1770 /***********************************************************************
1771 * COMBO_LButtonUp
1772 *
1773 * Release capture and stop tracking if needed.
1774 */
1775 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1776 {
1777 if( lphc->wState & CBF_CAPTURE )
1778 {
1779 lphc->wState &= ~CBF_CAPTURE;
1780 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1781 {
1782 INT index = CBUpdateLBox( lphc, TRUE );
1783 /* Update edit only if item is in the list */
1784 if(index >= 0)
1785 {
1786 lphc->wState |= CBF_NOLBSELECT;
1787 CBUpdateEdit( lphc, index );
1788 lphc->wState &= ~CBF_NOLBSELECT;
1789 }
1790 }
1791 ReleaseCapture();
1792 SetCapture(lphc->hWndLBox);
1793 }
1794
1795 if( lphc->wState & CBF_BUTTONDOWN )
1796 {
1797 lphc->wState &= ~CBF_BUTTONDOWN;
1798 CBRepaintButton( lphc );
1799 }
1800 }
1801
1802 /***********************************************************************
1803 * COMBO_MouseMove
1804 *
1805 * Two things to do - track combo button and release capture when
1806 * pointer goes into the listbox.
1807 */
1808 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1809 {
1810 POINT pt;
1811 RECT lbRect;
1812
1813 pt.x = LOWORD(lParam);
1814 pt.y = HIWORD(lParam);
1815
1816 if( lphc->wState & CBF_BUTTONDOWN )
1817 {
1818 BOOL bButton;
1819
1820 bButton = PtInRect(&lphc->buttonRect, pt);
1821
1822 if( !bButton )
1823 {
1824 lphc->wState &= ~CBF_BUTTONDOWN;
1825 CBRepaintButton( lphc );
1826 }
1827 }
1828
1829 GetClientRect( lphc->hWndLBox, &lbRect );
1830 MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1831 if( PtInRect(&lbRect, pt) )
1832 {
1833 lphc->wState &= ~CBF_CAPTURE;
1834 ReleaseCapture();
1835 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1836
1837 /* hand over pointer tracking */
1838 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1839 }
1840 }
1841
1842
1843 /***********************************************************************
1844 * ComboWndProc_common
1845 *
1846 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1847 */
1848 static LRESULT ComboWndProc_common( HWND hwnd, UINT message,
1849 WPARAM wParam, LPARAM lParam, BOOL unicode )
1850 {
1851 LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongA( hwnd, 0 );
1852
1853 //TRACE("[%p]: msg %s wp %08x lp %08lx\n",
1854 // hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1855
1856 if( lphc || message == WM_NCCREATE )
1857 switch(message)
1858 {
1859
1860 /* System messages */
1861
1862 case WM_NCCREATE:
1863 {
1864 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1865 ((LPCREATESTRUCTA)lParam)->style;
1866 return COMBO_NCCreate(hwnd, style);
1867 }
1868 case WM_NCDESTROY:
1869 COMBO_NCDestroy(lphc);
1870 break;/* -> DefWindowProc */
1871
1872 case WM_CREATE:
1873 {
1874 HWND hwndParent;
1875 LONG style;
1876 if(unicode)
1877 {
1878 hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1879 style = ((LPCREATESTRUCTW)lParam)->style;
1880 }
1881 else
1882 {
1883 hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1884 style = ((LPCREATESTRUCTA)lParam)->style;
1885 }
1886 return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
1887 }
1888
1889 case WM_PRINTCLIENT:
1890 if (lParam & PRF_ERASEBKGND)
1891 COMBO_EraseBackground(hwnd, lphc, (HDC)wParam);
1892
1893 /* Fallthrough */
1894 case WM_PAINT:
1895 /* wParam may contain a valid HDC! */
1896 return COMBO_Paint(lphc, (HDC)wParam);
1897 case WM_ERASEBKGND:
1898 return COMBO_EraseBackground(hwnd, lphc, (HDC)wParam);
1899 case WM_GETDLGCODE:
1900 {
1901 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1902 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1903 {
1904 int vk = (int)((LPMSG)lParam)->wParam;
1905
1906 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1907 result |= DLGC_WANTMESSAGE;
1908 }
1909 return result;
1910 }
1911 case WM_WINDOWPOSCHANGING:
1912 return COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1913 case WM_WINDOWPOSCHANGED:
1914 /* SetWindowPos can be called on a Combobox to resize its Listbox.
1915 * In that case, the Combobox itself will not be resized, so we won't
1916 * get a WM_SIZE. Since we still want to update the Listbox, we have to
1917 * do it here.
1918 */
1919 /* fall through */
1920 case WM_SIZE:
1921 if( lphc->hWndLBox &&
1922 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1923 return TRUE;
1924 case WM_SETFONT:
1925 COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1926 return TRUE;
1927 case WM_GETFONT:
1928 return (LRESULT)lphc->hFont;
1929 case WM_SETFOCUS:
1930 if( lphc->wState & CBF_EDIT )
1931 SetFocus( lphc->hWndEdit );
1932 else
1933 COMBO_SetFocus( lphc );
1934 return TRUE;
1935 case WM_KILLFOCUS:
1936 {
1937 //HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1938 //if( !hwndFocus ||
1939 // (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1940 // COMBO_KillFocus( lphc );
1941 return TRUE;
1942 }
1943 case WM_COMMAND:
1944 //return COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1945 return COMBO_Command( lphc, wParam, (HWND)lParam );
1946
1947 case WM_GETTEXT:
1948 return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
1949 : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
1950 case WM_SETTEXT:
1951 case WM_GETTEXTLENGTH:
1952 case WM_CLEAR:
1953 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1954 {
1955 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1956 if (j == -1) return 0;
1957 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
1958 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1959 }
1960 else if( lphc->wState & CBF_EDIT )
1961 {
1962 LRESULT ret;
1963 lphc->wState |= CBF_NOEDITNOTIFY;
1964 ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1965 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1966 lphc->wState &= ~CBF_NOEDITNOTIFY;
1967 return ret;
1968 }
1969 else return CB_ERR;
1970 case WM_CUT:
1971 case WM_PASTE:
1972 case WM_COPY:
1973 if( lphc->wState & CBF_EDIT )
1974 {
1975 return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1976 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1977 }
1978 else return CB_ERR;
1979
1980 case WM_DRAWITEM:
1981 case WM_DELETEITEM:
1982 case WM_COMPAREITEM:
1983 case WM_MEASUREITEM:
1984 return COMBO_ItemOp(lphc, message, lParam);
1985 case WM_ENABLE:
1986 if( lphc->wState & CBF_EDIT )
1987 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1988 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1989
1990 /* Force the control to repaint when the enabled state changes. */
1991 InvalidateRect(lphc->self, NULL, TRUE);
1992 return TRUE;
1993 case WM_SETREDRAW:
1994 if( wParam )
1995 lphc->wState &= ~CBF_NOREDRAW;
1996 else
1997 lphc->wState |= CBF_NOREDRAW;
1998
1999 if( lphc->wState & CBF_EDIT )
2000 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
2001 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
2002 return 0;
2003 case WM_SYSKEYDOWN:
2004 if( KEYDATA_ALT & HIWORD(lParam) )
2005 if( wParam == VK_UP || wParam == VK_DOWN )
2006 COMBO_FlipListbox( lphc, FALSE, FALSE );
2007 return 0;
2008
2009 case WM_CHAR:
2010 case WM_IME_CHAR:
2011 case WM_KEYDOWN:
2012 {
2013 HWND hwndTarget;
2014
2015 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
2016 (lphc->wState & CBF_DROPPED))
2017 {
2018 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
2019 return TRUE;
2020 }
2021
2022 if( lphc->wState & CBF_EDIT )
2023 hwndTarget = lphc->hWndEdit;
2024 else
2025 hwndTarget = lphc->hWndLBox;
2026
2027 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
2028 SendMessageA(hwndTarget, message, wParam, lParam);
2029 }
2030 case WM_LBUTTONDOWN:
2031 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
2032 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2033 return TRUE;
2034 case WM_LBUTTONUP:
2035 COMBO_LButtonUp( lphc );
2036 return TRUE;
2037 case WM_MOUSEMOVE:
2038 if( lphc->wState & CBF_CAPTURE )
2039 COMBO_MouseMove( lphc, wParam, lParam );
2040 return TRUE;
2041
2042 case WM_MOUSEWHEEL:
2043 if (wParam & (MK_SHIFT | MK_CONTROL))
2044 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2045 DefWindowProcA(hwnd, message, wParam, lParam);
2046 if (SHIWORD(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
2047 if (SHIWORD(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2048 return TRUE;
2049
2050 /* Combo messages */
2051
2052 case CB_ADDSTRING:
2053 return unicode ? SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam) :
2054 SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2055 case CB_INSERTSTRING:
2056 return unicode ? SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam) :
2057 SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2058 case CB_DELETESTRING:
2059 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2060 SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2061 case CB_SELECTSTRING:
2062 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2063 case CB_FINDSTRING:
2064 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2065 SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2066 case CB_FINDSTRINGEXACT:
2067 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2068 SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2069 case CB_SETITEMHEIGHT:
2070 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2071 case CB_GETITEMHEIGHT:
2072 if( (INT)wParam >= 0 ) /* listbox item */
2073 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2074 return CBGetTextAreaHeight(hwnd, lphc);
2075 case CB_RESETCONTENT:
2076 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2077 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2078 {
2079 static const WCHAR empty_stringW[] = { 0 };
2080 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2081 }
2082 else
2083 InvalidateRect(lphc->self, NULL, TRUE);
2084 return TRUE;
2085 case CB_INITSTORAGE:
2086 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2087 case CB_GETHORIZONTALEXTENT:
2088 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2089 case CB_SETHORIZONTALEXTENT:
2090 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2091 case CB_GETTOPINDEX:
2092 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2093 case CB_GETLOCALE:
2094 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2095 case CB_SETLOCALE:
2096 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2097 case CB_GETDROPPEDWIDTH:
2098 if( lphc->droppedWidth )
2099 return lphc->droppedWidth;
2100 return lphc->droppedRect.right - lphc->droppedRect.left;
2101 case CB_SETDROPPEDWIDTH:
2102 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2103 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2104 return CB_ERR;
2105 case CB_GETDROPPEDCONTROLRECT:
2106 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2107 return CB_OKAY;
2108 case CB_GETDROPPEDSTATE:
2109 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2110 case CB_DIR:
2111 if(message == CB_DIR) message = LB_DIR;
2112 return unicode ? SendMessageW(lphc->hWndLBox, message, wParam, lParam) :
2113 SendMessageA(lphc->hWndLBox, message, wParam, lParam);
2114
2115 case CB_SHOWDROPDOWN:
2116 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2117 {
2118 if( wParam )
2119 {
2120 if( !(lphc->wState & CBF_DROPPED) )
2121 CBDropDown( lphc );
2122 }
2123 else
2124 if( lphc->wState & CBF_DROPPED )
2125 CBRollUp( lphc, FALSE, TRUE );
2126 }
2127 return TRUE;
2128 case CB_GETCOUNT:
2129 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2130 case CB_GETCURSEL:
2131 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2132 case CB_SETCURSEL:
2133 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2134 if( lParam >= 0 )
2135 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2136
2137 /* no LBN_SELCHANGE in this case, update manually */
2138 if( lphc->wState & CBF_EDIT )
2139 CBUpdateEdit( lphc, (INT)wParam );
2140 else
2141 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
2142 lphc->wState &= ~CBF_SELCHANGE;
2143 return lParam;
2144 case CB_GETLBTEXT:
2145 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2146 SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2147 case CB_GETLBTEXTLEN:
2148 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
2149 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2150 case CB_GETITEMDATA:
2151 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2152 case CB_SETITEMDATA:
2153 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2154 case CB_GETEDITSEL:
2155 /* Edit checks passed parameters itself */
2156 if( lphc->wState & CBF_EDIT )
2157 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2158 return CB_ERR;
2159 case CB_SETEDITSEL:
2160 if( lphc->wState & CBF_EDIT )
2161 return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2162 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2163 return CB_ERR;
2164 case CB_SETEXTENDEDUI:
2165 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2166 return CB_ERR;
2167 if( wParam )
2168 lphc->wState |= CBF_EUI;
2169 else lphc->wState &= ~CBF_EUI;
2170 return CB_OKAY;
2171 case CB_GETEXTENDEDUI:
2172 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2173
2174 default:
2175 if (message >= WM_USER)
2176 DbgPrint("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
2177 message - WM_USER, wParam, lParam );
2178 break;
2179 }
2180 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2181 DefWindowProcA(hwnd, message, wParam, lParam);
2182 }
2183
2184 #if 0
2185 /***********************************************************************
2186 * ComboWndProcA
2187 *
2188 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2189 * window structs.
2190 */
2191 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2192 {
2193 if (!IsWindow(hwnd)) return 0;
2194 return ComboWndProc_common( hwnd, message, wParam, lParam, FALSE );
2195 }
2196 #endif
2197
2198 /***********************************************************************
2199 * ComboWndProcW
2200 */
2201 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2202 {
2203 if (!IsWindow(hwnd)) return 0;
2204 return ComboWndProc_common( hwnd, message, wParam, lParam, TRUE );
2205 }
2206
2207 /*************************************************************************
2208 * GetComboBoxInfo (USER32.@)
2209 */
2210 BOOL WINAPI GetComboBoxInfo(HWND hwndCombo, /* [in] handle to combo box */
2211 PCOMBOBOXINFO pcbi /* [in/out] combo box information */)
2212 {
2213 FIXME("\n");
2214 return FALSE;
2215
2216 }