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

Last change on this file since 21916 was 21916, checked in by dmik, 14 years ago

Merge branch gcc-kmk to trunk.

File size: 66.6 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(void)
88{
89 HDC hDC;
90
91 if( hComboBmp ) return TRUE;
92 if( (hDC = CreateCompatibleDC(0)) != 0)
93 {
94 BOOL bRet = FALSE;
95 if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) != 0)
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) != FALSE)
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))) != NULL)
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))) != NULL)
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 (WORD)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", hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1900
1901 if( lphc || message == WM_NCCREATE )
1902 switch(message)
1903 {
1904
1905 /* System messages */
1906
1907 case WM_NCCREATE:
1908 {
1909 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1910 ((LPCREATESTRUCTA)lParam)->style;
1911 return COMBO_NCCreate(hwnd, style);
1912 }
1913 case WM_NCDESTROY:
1914 COMBO_NCDestroy(lphc);
1915 break;/* -> DefWindowProc */
1916
1917 case WM_CREATE:
1918 {
1919 HWND hwndParent;
1920 LONG style;
1921 if(unicode)
1922 {
1923 hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1924 style = ((LPCREATESTRUCTW)lParam)->style;
1925 }
1926 else
1927 {
1928 hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1929 style = ((LPCREATESTRUCTA)lParam)->style;
1930 }
1931 return COMBO_Create(hwnd, lphc, hwndParent, style);
1932 }
1933
1934 case WM_PRINTCLIENT:
1935 if (lParam & PRF_ERASEBKGND)
1936 COMBO_EraseBackground(hwnd, lphc, wParam);
1937
1938 /* Fallthrough */
1939 case WM_PAINT:
1940 /* wParam may contain a valid HDC! */
1941 return COMBO_Paint(lphc, wParam);
1942 case WM_ERASEBKGND:
1943 return COMBO_EraseBackground(hwnd, lphc, wParam);
1944 case WM_GETDLGCODE:
1945 {
1946 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1947 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1948 {
1949 int vk = (int)((LPMSG)lParam)->wParam;
1950
1951 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1952 result |= DLGC_WANTMESSAGE;
1953 }
1954 return result;
1955 }
1956 case WM_WINDOWPOSCHANGING:
1957 return COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1958 case WM_WINDOWPOSCHANGED:
1959 /* SetWindowPos can be called on a Combobox to resize its Listbox.
1960 * In that case, the Combobox itself will not be resized, so we won't
1961 * get a WM_SIZE. Since we still want to update the Listbox, we have to
1962 * do it here.
1963 */
1964 /* fall through */
1965 case WM_SIZE:
1966 if( lphc->hWndLBox &&
1967 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1968 return TRUE;
1969 case WM_SETFONT:
1970 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1971 return TRUE;
1972 case WM_GETFONT:
1973 return (LRESULT)lphc->hFont;
1974 case WM_SETFOCUS:
1975 if( lphc->wState & CBF_EDIT )
1976 SetFocus( lphc->hWndEdit );
1977 else
1978 COMBO_SetFocus( lphc );
1979 return TRUE;
1980 case WM_KILLFOCUS:
1981 {
1982 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1983 if( !hwndFocus ||
1984 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1985 COMBO_KillFocus( lphc );
1986 return TRUE;
1987 }
1988 case WM_COMMAND:
1989 return COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1990 case WM_GETTEXT:
1991 return COMBO_GetText( lphc, (INT)wParam, lParam, unicode );
1992 case WM_SETTEXT:
1993 case WM_GETTEXTLENGTH:
1994 case WM_CLEAR:
1995 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1996 {
1997 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1998 if (j == -1) return 0;
1999 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
2000 }
2001 else if( lphc->wState & CBF_EDIT )
2002 {
2003 LRESULT ret;
2004 lphc->wState |= CBF_NOEDITNOTIFY;
2005 ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
2006 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
2007 lphc->wState &= ~CBF_NOEDITNOTIFY;
2008 return ret;
2009 }
2010 else return CB_ERR;
2011 case WM_CUT:
2012 case WM_PASTE:
2013 case WM_COPY:
2014 if( lphc->wState & CBF_EDIT )
2015 {
2016 return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
2017 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
2018 }
2019 else return CB_ERR;
2020
2021 case WM_DRAWITEM:
2022 case WM_DELETEITEM:
2023 case WM_COMPAREITEM:
2024 case WM_MEASUREITEM:
2025 return COMBO_ItemOp(lphc, message, lParam);
2026 case WM_ENABLE:
2027 if( lphc->wState & CBF_EDIT )
2028 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
2029 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
2030
2031 /* Force the control to repaint when the enabled state changes. */
2032 InvalidateRect(lphc->self, NULL, TRUE);
2033 return TRUE;
2034 case WM_SETREDRAW:
2035 if( wParam )
2036 lphc->wState &= ~CBF_NOREDRAW;
2037 else
2038 lphc->wState |= CBF_NOREDRAW;
2039
2040 if( lphc->wState & CBF_EDIT )
2041 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
2042 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
2043 return 0;
2044 case WM_SYSKEYDOWN:
2045 if( KEYDATA_ALT & HIWORD(lParam) )
2046 if( wParam == VK_UP || wParam == VK_DOWN )
2047 COMBO_FlipListbox( lphc, FALSE, FALSE );
2048 return 0;
2049
2050#ifdef __WIN32OS2__
2051 case WM_IME_CHAR:
2052#endif
2053 case WM_CHAR:
2054 case WM_KEYDOWN:
2055 {
2056 HWND hwndTarget;
2057
2058 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
2059 (lphc->wState & CBF_DROPPED))
2060 {
2061 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
2062 return TRUE;
2063 }
2064
2065 if( lphc->wState & CBF_EDIT )
2066 hwndTarget = lphc->hWndEdit;
2067 else
2068 hwndTarget = lphc->hWndLBox;
2069
2070 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
2071 SendMessageA(hwndTarget, message, wParam, lParam);
2072 }
2073 case WM_LBUTTONDOWN:
2074 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
2075 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2076 return TRUE;
2077 case WM_LBUTTONUP:
2078 COMBO_LButtonUp( lphc );
2079 return TRUE;
2080 case WM_MOUSEMOVE:
2081 if( lphc->wState & CBF_CAPTURE )
2082 COMBO_MouseMove( lphc, wParam, lParam );
2083 return TRUE;
2084
2085 case WM_MOUSEWHEEL:
2086 if (wParam & (MK_SHIFT | MK_CONTROL))
2087 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2088 DefWindowProcA(hwnd, message, wParam, lParam);
2089 if (SHIWORD(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
2090 if (SHIWORD(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2091 return TRUE;
2092
2093 /* Combo messages */
2094
2095 case CB_ADDSTRING16:
2096 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2097 /* fall through */
2098 case CB_ADDSTRING:
2099 return unicode ? SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam) :
2100 SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2101 case CB_INSERTSTRING16:
2102 wParam = (INT)(INT16)wParam;
2103 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2104 /* fall through */
2105 case CB_INSERTSTRING:
2106 return unicode ? SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam) :
2107 SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2108 case CB_DELETESTRING16:
2109 case CB_DELETESTRING:
2110 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2111 SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2112 case CB_SELECTSTRING16:
2113 wParam = (INT)(INT16)wParam;
2114 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2115 /* fall through */
2116 case CB_SELECTSTRING:
2117 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2118 case CB_FINDSTRING16:
2119 wParam = (INT)(INT16)wParam;
2120 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2121 /* fall through */
2122 case CB_FINDSTRING:
2123 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2124 SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2125 case CB_FINDSTRINGEXACT16:
2126 wParam = (INT)(INT16)wParam;
2127 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2128 /* fall through */
2129 case CB_FINDSTRINGEXACT:
2130 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2131 SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2132 case CB_SETITEMHEIGHT16:
2133 wParam = (INT)(INT16)wParam; /* signed integer */
2134 /* fall through */
2135 case CB_SETITEMHEIGHT:
2136 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2137 case CB_GETITEMHEIGHT16:
2138 wParam = (INT)(INT16)wParam;
2139 /* fall through */
2140 case CB_GETITEMHEIGHT:
2141 if( (INT)wParam >= 0 ) /* listbox item */
2142 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2143 return CBGetTextAreaHeight(hwnd, lphc);
2144 case CB_RESETCONTENT16:
2145 case CB_RESETCONTENT:
2146 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2147 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2148 {
2149 static const WCHAR empty_stringW[] = { 0 };
2150 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2151 }
2152 else
2153 InvalidateRect(lphc->self, NULL, TRUE);
2154 return TRUE;
2155 case CB_INITSTORAGE:
2156 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2157 case CB_GETHORIZONTALEXTENT:
2158 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2159 case CB_SETHORIZONTALEXTENT:
2160 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2161 case CB_GETTOPINDEX:
2162 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2163 case CB_GETLOCALE:
2164 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2165 case CB_SETLOCALE:
2166 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2167 case CB_GETDROPPEDWIDTH:
2168 if( lphc->droppedWidth )
2169 return lphc->droppedWidth;
2170 return lphc->droppedRect.right - lphc->droppedRect.left;
2171 case CB_SETDROPPEDWIDTH:
2172 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2173 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2174 return CB_ERR;
2175 case CB_GETDROPPEDCONTROLRECT16:
2176 lParam = (LPARAM)MapSL(lParam);
2177 if( lParam )
2178 {
2179 RECT r;
2180 CBGetDroppedControlRect( lphc, &r );
2181 CONV_RECT32TO16( &r, (LPRECT16)lParam );
2182 }
2183 return CB_OKAY;
2184 case CB_GETDROPPEDCONTROLRECT:
2185 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2186 return CB_OKAY;
2187 case CB_GETDROPPEDSTATE16:
2188 case CB_GETDROPPEDSTATE:
2189 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2190 case CB_DIR16:
2191 lParam = (LPARAM)MapSL(lParam);
2192 message = LB_DIR16;
2193 /* fall through */
2194 case CB_DIR:
2195 if(message == CB_DIR) message = LB_DIR;
2196 return unicode ? SendMessageW(lphc->hWndLBox, message, wParam, lParam) :
2197 SendMessageA(lphc->hWndLBox, message, wParam, lParam);
2198
2199 case CB_SHOWDROPDOWN16:
2200 case CB_SHOWDROPDOWN:
2201 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2202 {
2203 if( wParam )
2204 {
2205 if( !(lphc->wState & CBF_DROPPED) )
2206 CBDropDown( lphc );
2207 }
2208 else
2209 if( lphc->wState & CBF_DROPPED )
2210 CBRollUp( lphc, FALSE, TRUE );
2211 }
2212 return TRUE;
2213 case CB_GETCOUNT16:
2214 case CB_GETCOUNT:
2215 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2216 case CB_GETCURSEL16:
2217 case CB_GETCURSEL:
2218 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2219 case CB_SETCURSEL16:
2220 wParam = (INT)(INT16)wParam;
2221 /* fall through */
2222 case CB_SETCURSEL:
2223 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2224 if( lParam >= 0 )
2225 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2226
2227 /* no LBN_SELCHANGE in this case, update manually */
2228 if( lphc->wState & CBF_EDIT )
2229 CBUpdateEdit( lphc, (INT)wParam );
2230 else
2231 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
2232 lphc->wState &= ~CBF_SELCHANGE;
2233 return lParam;
2234 case CB_GETLBTEXT16:
2235 wParam = (INT)(INT16)wParam;
2236 lParam = (LPARAM)MapSL(lParam);
2237 /* fall through */
2238 case CB_GETLBTEXT:
2239 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2240 SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2241 case CB_GETLBTEXTLEN16:
2242 wParam = (INT)(INT16)wParam;
2243 /* fall through */
2244 case CB_GETLBTEXTLEN:
2245 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2246 case CB_GETITEMDATA16:
2247 wParam = (INT)(INT16)wParam;
2248 /* fall through */
2249 case CB_GETITEMDATA:
2250 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2251 case CB_SETITEMDATA16:
2252 wParam = (INT)(INT16)wParam;
2253 /* fall through */
2254 case CB_SETITEMDATA:
2255 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2256 case CB_GETEDITSEL16:
2257 wParam = lParam = 0; /* just in case */
2258 /* fall through */
2259 case CB_GETEDITSEL:
2260 /* Edit checks passed parameters itself */
2261 if( lphc->wState & CBF_EDIT )
2262 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2263 return CB_ERR;
2264 case CB_SETEDITSEL16:
2265 case CB_SETEDITSEL:
2266 if( lphc->wState & CBF_EDIT )
2267 return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2268 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2269 return CB_ERR;
2270 case CB_SETEXTENDEDUI16:
2271 case CB_SETEXTENDEDUI:
2272 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2273 return CB_ERR;
2274 if( wParam )
2275 lphc->wState |= CBF_EUI;
2276 else lphc->wState &= ~CBF_EUI;
2277 return CB_OKAY;
2278 case CB_GETEXTENDEDUI16:
2279 case CB_GETEXTENDEDUI:
2280 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2281
2282 default:
2283 if (message >= WM_USER)
2284 WARN("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
2285 message - WM_USER, wParam, lParam );
2286 break;
2287 }
2288 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2289 DefWindowProcA(hwnd, message, wParam, lParam);
2290}
2291
2292/***********************************************************************
2293 * ComboWndProcA
2294 *
2295 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2296 * window structs.
2297 */
2298static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2299{
2300 if (!IsWindow(hwnd)) return 0;
2301 return ComboWndProc_common( hwnd, message, wParam, lParam, FALSE );
2302}
2303
2304/***********************************************************************
2305 * ComboWndProcW
2306 */
2307static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2308{
2309 if (!IsWindow(hwnd)) return 0;
2310 return ComboWndProc_common( hwnd, message, wParam, lParam, TRUE );
2311}
Note: See TracBrowser for help on using the repository browser.