source: trunk/src/user32/combo.c@ 10367

Last change on this file since 10367 was 10216, checked in by sandervl, 22 years ago

KOM: WM_IME_CHAR generation + processing added for DBCS input

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