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

Last change on this file since 9523 was 9523, checked in by sandervl, 23 years ago

removed obsolete files

File size: 62.1 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}
1255
1256/***********************************************************************
1257 * COMBO_FlipListbox
1258 *
1259 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1260 */
1261BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1262{
1263 if( lphc->wState & CBF_DROPPED )
1264 {
1265 CBRollUp( lphc, ok, bRedrawButton );
1266 return FALSE;
1267 }
1268
1269 CBDropDown( lphc );
1270 return TRUE;
1271}
1272
1273/***********************************************************************
1274 * CBRepaintButton
1275 */
1276static void CBRepaintButton( LPHEADCOMBO lphc )
1277 {
1278 InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1279 UpdateWindow(lphc->self);
1280}
1281
1282/***********************************************************************
1283 * COMBO_SetFocus
1284 */
1285static void COMBO_SetFocus( LPHEADCOMBO lphc )
1286{
1287 if( !(lphc->wState & CBF_FOCUSED) )
1288 {
1289 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1290 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1291
1292 /* This is wrong. Message sequences seem to indicate that this
1293 is set *after* the notify. */
1294 /* lphc->wState |= CBF_FOCUSED; */
1295
1296 if( !(lphc->wState & CBF_EDIT) )
1297 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1298
1299 CB_NOTIFY( lphc, CBN_SETFOCUS );
1300 lphc->wState |= CBF_FOCUSED;
1301 }
1302}
1303
1304/***********************************************************************
1305 * COMBO_KillFocus
1306 */
1307static void COMBO_KillFocus( LPHEADCOMBO lphc )
1308{
1309 HWND hWnd = lphc->self;
1310
1311 if( lphc->wState & CBF_FOCUSED )
1312 {
1313 CBRollUp( lphc, FALSE, TRUE );
1314 if( IsWindow( hWnd ) )
1315 {
1316 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1317 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1318
1319 lphc->wState &= ~CBF_FOCUSED;
1320
1321 /* redraw text */
1322 if( !(lphc->wState & CBF_EDIT) )
1323 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1324
1325 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1326 }
1327 }
1328}
1329
1330/***********************************************************************
1331 * COMBO_Command
1332 */
1333static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1334{
1335 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1336 {
1337 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1338
1339 switch( HIWORD(wParam) >> 8 )
1340 {
1341 case (EN_SETFOCUS >> 8):
1342
1343 TRACE("[%04x]: edit [%04x] got focus\n",
1344 lphc->self, lphc->hWndEdit );
1345
1346 COMBO_SetFocus( lphc );
1347 break;
1348
1349 case (EN_KILLFOCUS >> 8):
1350
1351 TRACE("[%04x]: edit [%04x] lost focus\n",
1352 lphc->self, lphc->hWndEdit );
1353
1354 /* NOTE: it seems that Windows' edit control sends an
1355 * undocumented message WM_USER + 0x1B instead of this
1356 * notification (only when it happens to be a part of
1357 * the combo). ?? - AK.
1358 */
1359
1360 COMBO_KillFocus( lphc );
1361 break;
1362
1363
1364 case (EN_CHANGE >> 8):
1365 /*
1366 * In some circumstances (when the selection of the combobox
1367 * is changed for example) we don't wans the EN_CHANGE notification
1368 * to be forwarded to the parent of the combobox. This code
1369 * checks a flag that is set in these occasions and ignores the
1370 * notification.
1371 */
1372 if (lphc->wState & CBF_NOLBSELECT)
1373 {
1374 lphc->wState &= ~CBF_NOLBSELECT;
1375 }
1376 else
1377 {
1378 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1379 }
1380
1381 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1382 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1383 break;
1384
1385 case (EN_UPDATE >> 8):
1386 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1387 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1388 break;
1389
1390 case (EN_ERRSPACE >> 8):
1391 CB_NOTIFY( lphc, CBN_ERRSPACE );
1392 }
1393 }
1394 else if( lphc->hWndLBox == hWnd )
1395 {
1396 switch( HIWORD(wParam) )
1397 {
1398 case LBN_ERRSPACE:
1399 CB_NOTIFY( lphc, CBN_ERRSPACE );
1400 break;
1401
1402 case LBN_DBLCLK:
1403 CB_NOTIFY( lphc, CBN_DBLCLK );
1404 break;
1405
1406 case LBN_SELCHANGE:
1407 case LBN_SELCANCEL:
1408
1409 TRACE("[%04x]: lbox selection change [%04x]\n",
1410 lphc->self, lphc->wState );
1411
1412 if( HIWORD(wParam) == LBN_SELCHANGE)
1413 {
1414 if( lphc->wState & CBF_EDIT )
1415 {
1416 INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1417 lphc->wState |= CBF_NOLBSELECT;
1418 CBUpdateEdit( lphc, index );
1419 /* select text in edit, as Windows does */
1420 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1421 }
1422 else
1423 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1424 }
1425
1426 /* do not roll up if selection is being tracked
1427 * by arrowkeys in the dropdown listbox */
1428 if( ((lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP)) )
1429 {
1430 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1431 }
1432 else lphc->wState &= ~CBF_NOROLLUP;
1433
1434 CB_NOTIFY( lphc, CBN_SELCHANGE );
1435
1436 /* fall through */
1437
1438 case LBN_SETFOCUS:
1439 case LBN_KILLFOCUS:
1440 /* nothing to do here since ComboLBox always resets the focus to its
1441 * combo/edit counterpart */
1442 break;
1443 }
1444 }
1445 return 0;
1446}
1447
1448/***********************************************************************
1449 * COMBO_ItemOp
1450 *
1451 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1452 */
1453static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1454{
1455 HWND hWnd = lphc->self;
1456 UINT id = GetWindowLongA( hWnd, GWL_ID );
1457
1458 TRACE("[%04x]: ownerdraw op %04x\n", lphc->self, msg );
1459
1460 switch( msg )
1461 {
1462 case WM_DELETEITEM:
1463 {
1464 DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1465 lpIS->CtlType = ODT_COMBOBOX;
1466 lpIS->CtlID = id;
1467 lpIS->hwndItem = hWnd;
1468 break;
1469 }
1470 case WM_DRAWITEM:
1471 {
1472 DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1473 lpIS->CtlType = ODT_COMBOBOX;
1474 lpIS->CtlID = id;
1475 lpIS->hwndItem = hWnd;
1476 break;
1477 }
1478 case WM_COMPAREITEM:
1479 {
1480 COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1481 lpIS->CtlType = ODT_COMBOBOX;
1482 lpIS->CtlID = id;
1483 lpIS->hwndItem = hWnd;
1484 break;
1485 }
1486 case WM_MEASUREITEM:
1487 {
1488 MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1489 lpIS->CtlType = ODT_COMBOBOX;
1490 lpIS->CtlID = id;
1491 break;
1492 }
1493 }
1494 return SendMessageW(lphc->owner, msg, id, lParam);
1495}
1496
1497/***********************************************************************
1498 * COMBO_GetText
1499 *
1500 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1501 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1502 */
1503static LRESULT COMBO_GetText( LPHEADCOMBO lphc, INT N, LPARAM lParam, BOOL unicode)
1504{
1505 if( lphc->wState & CBF_EDIT )
1506 return unicode ? SendMessageW(lphc->hWndEdit, WM_GETTEXT, (WPARAM)N, lParam) :
1507 SendMessageA(lphc->hWndEdit, WM_GETTEXT, (WPARAM)N, lParam);
1508
1509 /* get it from the listbox */
1510
1511 if( lphc->hWndLBox )
1512 {
1513 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1514 if( idx != LB_ERR )
1515 {
1516 INT n = 0;
1517 INT length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN,
1518 (WPARAM)idx, 0 );
1519
1520 if(unicode)
1521 {
1522 LPWSTR lpBuffer, lpText = (LPWSTR)lParam;
1523
1524 /* 'length' is without the terminating character */
1525 if(length >= N)
1526 lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1527 else
1528 lpBuffer = lpText;
1529
1530 if(lpBuffer)
1531 {
1532 n = SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)idx, (LPARAM)lpBuffer);
1533
1534 /* truncate if buffer is too short */
1535 if(length >= N)
1536 {
1537 if(N && lpText)
1538 {
1539 if(n != LB_ERR)
1540 strncpyW(lpText, lpBuffer, (N > n) ? n+1 : N-1);
1541 lpText[N - 1] = '\0';
1542 }
1543 HeapFree( GetProcessHeap(), 0, lpBuffer );
1544 }
1545 }
1546 }
1547 else
1548 {
1549 LPSTR lpBuffer, lpText = (LPSTR)lParam;
1550
1551 /* 'length' is without the terminating character */
1552 if(length >= N)
1553 lpBuffer = HeapAlloc(GetProcessHeap(), 0, length + 1);
1554 else
1555 lpBuffer = lpText;
1556
1557 if(lpBuffer)
1558 {
1559 n = SendMessageA(lphc->hWndLBox, LB_GETTEXT, (WPARAM)idx, (LPARAM)lpBuffer);
1560
1561 /* truncate if buffer is too short */
1562 if(length >= N)
1563 {
1564 if(N && lpText)
1565 {
1566 if(n != LB_ERR)
1567 strncpy(lpText, lpBuffer, (N > n) ? n+1 : N-1);
1568 lpText[N - 1] = '\0';
1569 }
1570 HeapFree( GetProcessHeap(), 0, lpBuffer );
1571 }
1572 }
1573 }
1574 if (n<0)
1575 n=0;
1576 else
1577 n++;
1578 return (LRESULT)n;
1579 }
1580 }
1581 return 0;
1582}
1583
1584
1585/***********************************************************************
1586 * CBResetPos
1587 *
1588 * This function sets window positions according to the updated
1589 * component placement struct.
1590 */
1591static void CBResetPos(
1592 LPHEADCOMBO lphc,
1593 LPRECT rectEdit,
1594 LPRECT rectLB,
1595 BOOL bRedraw)
1596{
1597 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1598
1599 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1600 * sizing messages */
1601
1602 if( lphc->wState & CBF_EDIT )
1603 SetWindowPos( lphc->hWndEdit, 0,
1604 rectEdit->left, rectEdit->top,
1605 rectEdit->right - rectEdit->left,
1606 rectEdit->bottom - rectEdit->top,
1607 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1608
1609 SetWindowPos( lphc->hWndLBox, 0,
1610 rectLB->left, rectLB->top,
1611 rectLB->right - rectLB->left,
1612 rectLB->bottom - rectLB->top,
1613 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1614
1615 if( bDrop )
1616 {
1617 if( lphc->wState & CBF_DROPPED )
1618 {
1619 lphc->wState &= ~CBF_DROPPED;
1620 ShowWindow( lphc->hWndLBox, SW_HIDE );
1621 }
1622
1623 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1624 RedrawWindow( lphc->self, NULL, 0,
1625 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1626 }
1627}
1628
1629
1630/***********************************************************************
1631 * COMBO_Size
1632 */
1633static void COMBO_Size( LPHEADCOMBO lphc )
1634 {
1635 CBCalcPlacement(lphc->self,
1636 lphc,
1637 &lphc->textRect,
1638 &lphc->buttonRect,
1639 &lphc->droppedRect);
1640
1641 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1642}
1643
1644
1645/***********************************************************************
1646 * COMBO_Font
1647 */
1648static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1649{
1650 /*
1651 * Set the font
1652 */
1653 lphc->hFont = hFont;
1654
1655 /*
1656 * Propagate to owned windows.
1657 */
1658 if( lphc->wState & CBF_EDIT )
1659 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1660 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1661
1662 /*
1663 * Redo the layout of the control.
1664 */
1665 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
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 else
1676 {
1677 CBForceDummyResize(lphc);
1678 }
1679}
1680
1681
1682/***********************************************************************
1683 * COMBO_SetItemHeight
1684 */
1685static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1686{
1687 LRESULT lRet = CB_ERR;
1688
1689 if( index == -1 ) /* set text field height */
1690 {
1691 if( height < 32768 )
1692 {
1693 lphc->editHeight = height;
1694
1695 /*
1696 * Redo the layout of the control.
1697 */
1698 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1699 {
1700 CBCalcPlacement(lphc->self,
1701 lphc,
1702 &lphc->textRect,
1703 &lphc->buttonRect,
1704 &lphc->droppedRect);
1705
1706 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1707 }
1708 else
1709 {
1710 CBForceDummyResize(lphc);
1711 }
1712
1713 lRet = height;
1714 }
1715 }
1716 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1717 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT,
1718 (WPARAM)index, (LPARAM)height );
1719 return lRet;
1720}
1721
1722/***********************************************************************
1723 * COMBO_SelectString
1724 */
1725static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1726{
1727 INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText) :
1728 SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText);
1729 if( index >= 0 )
1730 {
1731 if( lphc->wState & CBF_EDIT )
1732 CBUpdateEdit( lphc, index );
1733 else
1734 {
1735 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1736 }
1737 }
1738 return (LRESULT)index;
1739}
1740
1741/***********************************************************************
1742 * COMBO_LButtonDown
1743 */
1744static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1745{
1746 POINT pt;
1747 BOOL bButton;
1748 HWND hWnd = lphc->self;
1749
1750 pt.x = LOWORD(lParam);
1751 pt.y = HIWORD(lParam);
1752 bButton = PtInRect(&lphc->buttonRect, pt);
1753
1754 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1755 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1756 {
1757 lphc->wState |= CBF_BUTTONDOWN;
1758 if( lphc->wState & CBF_DROPPED )
1759 {
1760 /* got a click to cancel selection */
1761
1762 lphc->wState &= ~CBF_BUTTONDOWN;
1763 CBRollUp( lphc, TRUE, FALSE );
1764 if( !IsWindow( hWnd ) ) return;
1765
1766 if( lphc->wState & CBF_CAPTURE )
1767 {
1768 lphc->wState &= ~CBF_CAPTURE;
1769 ReleaseCapture();
1770 }
1771 }
1772 else
1773 {
1774 /* drop down the listbox and start tracking */
1775
1776 lphc->wState |= CBF_CAPTURE;
1777 SetCapture( hWnd );
1778 CBDropDown( lphc );
1779 }
1780 if( bButton ) CBRepaintButton( lphc );
1781 }
1782}
1783
1784/***********************************************************************
1785 * COMBO_LButtonUp
1786 *
1787 * Release capture and stop tracking if needed.
1788 */
1789static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1790{
1791 if( lphc->wState & CBF_CAPTURE )
1792 {
1793 lphc->wState &= ~CBF_CAPTURE;
1794 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1795 {
1796 INT index = CBUpdateLBox( lphc, TRUE );
1797 /* Update edit only if item is in the list */
1798 if(index >= 0)
1799 {
1800 lphc->wState |= CBF_NOLBSELECT;
1801 CBUpdateEdit( lphc, index );
1802 lphc->wState &= ~CBF_NOLBSELECT;
1803 }
1804 }
1805 ReleaseCapture();
1806 SetCapture(lphc->hWndLBox);
1807 }
1808
1809 if( lphc->wState & CBF_BUTTONDOWN )
1810 {
1811 lphc->wState &= ~CBF_BUTTONDOWN;
1812 CBRepaintButton( lphc );
1813 }
1814}
1815
1816/***********************************************************************
1817 * COMBO_MouseMove
1818 *
1819 * Two things to do - track combo button and release capture when
1820 * pointer goes into the listbox.
1821 */
1822static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1823{
1824 POINT pt;
1825 RECT lbRect;
1826
1827 pt.x = LOWORD(lParam);
1828 pt.y = HIWORD(lParam);
1829
1830 if( lphc->wState & CBF_BUTTONDOWN )
1831 {
1832 BOOL bButton;
1833
1834 bButton = PtInRect(&lphc->buttonRect, pt);
1835
1836 if( !bButton )
1837 {
1838 lphc->wState &= ~CBF_BUTTONDOWN;
1839 CBRepaintButton( lphc );
1840 }
1841 }
1842
1843 GetClientRect( lphc->hWndLBox, &lbRect );
1844 MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1845 if( PtInRect(&lbRect, pt) )
1846 {
1847 lphc->wState &= ~CBF_CAPTURE;
1848 ReleaseCapture();
1849 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1850
1851 /* hand over pointer tracking */
1852 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1853 }
1854}
1855
1856
1857/***********************************************************************
1858 * ComboWndProc_common
1859 *
1860 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1861 */
1862static LRESULT ComboWndProc_common( HWND hwnd, UINT message,
1863 WPARAM wParam, LPARAM lParam, BOOL unicode )
1864{
1865 LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongA( hwnd, 0 );
1866
1867 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1868 hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1869
1870 if( lphc || message == WM_NCCREATE )
1871 switch(message)
1872 {
1873
1874 /* System messages */
1875
1876 case WM_NCCREATE:
1877 {
1878 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1879 ((LPCREATESTRUCTA)lParam)->style;
1880 return COMBO_NCCreate(hwnd, style);
1881 }
1882 case WM_NCDESTROY:
1883 COMBO_NCDestroy(lphc);
1884 break;/* -> DefWindowProc */
1885
1886 case WM_CREATE:
1887 {
1888 HWND hwndParent;
1889 LONG style;
1890 if(unicode)
1891 {
1892 hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1893 style = ((LPCREATESTRUCTW)lParam)->style;
1894 }
1895 else
1896 {
1897 hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1898 style = ((LPCREATESTRUCTA)lParam)->style;
1899 }
1900 return COMBO_Create(hwnd, lphc, hwndParent, style);
1901 }
1902
1903 case WM_PRINTCLIENT:
1904 if (lParam & PRF_ERASEBKGND)
1905 COMBO_EraseBackground(hwnd, lphc, wParam);
1906
1907 /* Fallthrough */
1908 case WM_PAINT:
1909 /* wParam may contain a valid HDC! */
1910 return COMBO_Paint(lphc, wParam);
1911 case WM_ERASEBKGND:
1912 return COMBO_EraseBackground(hwnd, lphc, wParam);
1913 case WM_GETDLGCODE:
1914 {
1915 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1916 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1917 {
1918 int vk = (int)((LPMSG)lParam)->wParam;
1919
1920 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1921 result |= DLGC_WANTMESSAGE;
1922 }
1923 return result;
1924 }
1925 case WM_WINDOWPOSCHANGING:
1926 return COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1927 case WM_WINDOWPOSCHANGED:
1928 /* SetWindowPos can be called on a Combobox to resize its Listbox.
1929 * In that case, the Combobox itself will not be resized, so we won't
1930 * get a WM_SIZE. Since we still want to update the Listbox, we have to
1931 * do it here.
1932 */
1933 /* fall through */
1934 case WM_SIZE:
1935 if( lphc->hWndLBox &&
1936 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1937 return TRUE;
1938 case WM_SETFONT:
1939 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1940 return TRUE;
1941 case WM_GETFONT:
1942 return (LRESULT)lphc->hFont;
1943 case WM_SETFOCUS:
1944 if( lphc->wState & CBF_EDIT )
1945 SetFocus( lphc->hWndEdit );
1946 else
1947 COMBO_SetFocus( lphc );
1948 return TRUE;
1949 case WM_KILLFOCUS:
1950 {
1951 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1952 if( !hwndFocus ||
1953 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1954 COMBO_KillFocus( lphc );
1955 return TRUE;
1956 }
1957 case WM_COMMAND:
1958 return COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1959 case WM_GETTEXT:
1960 return COMBO_GetText( lphc, (INT)wParam, lParam, unicode );
1961 case WM_SETTEXT:
1962 case WM_GETTEXTLENGTH:
1963 case WM_CLEAR:
1964 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1965 {
1966 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1967 if (j == -1) return 0;
1968 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1969 }
1970 else if( lphc->wState & CBF_EDIT )
1971 {
1972 LRESULT ret;
1973 lphc->wState |= CBF_NOEDITNOTIFY;
1974 ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1975 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1976 lphc->wState &= ~CBF_NOEDITNOTIFY;
1977 return ret;
1978 }
1979 else return CB_ERR;
1980 case WM_CUT:
1981 case WM_PASTE:
1982 case WM_COPY:
1983 if( lphc->wState & CBF_EDIT )
1984 {
1985 return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1986 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1987 }
1988 else return CB_ERR;
1989
1990 case WM_DRAWITEM:
1991 case WM_DELETEITEM:
1992 case WM_COMPAREITEM:
1993 case WM_MEASUREITEM:
1994 return COMBO_ItemOp(lphc, message, lParam);
1995 case WM_ENABLE:
1996 if( lphc->wState & CBF_EDIT )
1997 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1998 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1999
2000 /* Force the control to repaint when the enabled state changes. */
2001 InvalidateRect(lphc->self, NULL, TRUE);
2002 return TRUE;
2003 case WM_SETREDRAW:
2004 if( wParam )
2005 lphc->wState &= ~CBF_NOREDRAW;
2006 else
2007 lphc->wState |= CBF_NOREDRAW;
2008
2009 if( lphc->wState & CBF_EDIT )
2010 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
2011 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
2012 return 0;
2013 case WM_SYSKEYDOWN:
2014 if( KEYDATA_ALT & HIWORD(lParam) )
2015 if( wParam == VK_UP || wParam == VK_DOWN )
2016 COMBO_FlipListbox( lphc, FALSE, FALSE );
2017 return 0;
2018
2019 case WM_CHAR:
2020 case WM_KEYDOWN:
2021 {
2022 HWND hwndTarget;
2023
2024 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
2025 (lphc->wState & CBF_DROPPED))
2026 {
2027 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
2028 return TRUE;
2029 }
2030
2031 if( lphc->wState & CBF_EDIT )
2032 hwndTarget = lphc->hWndEdit;
2033 else
2034 hwndTarget = lphc->hWndLBox;
2035
2036 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
2037 SendMessageA(hwndTarget, message, wParam, lParam);
2038 }
2039 case WM_LBUTTONDOWN:
2040 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
2041 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2042 return TRUE;
2043 case WM_LBUTTONUP:
2044 COMBO_LButtonUp( lphc );
2045 return TRUE;
2046 case WM_MOUSEMOVE:
2047 if( lphc->wState & CBF_CAPTURE )
2048 COMBO_MouseMove( lphc, wParam, lParam );
2049 return TRUE;
2050
2051 case WM_MOUSEWHEEL:
2052 if (wParam & (MK_SHIFT | MK_CONTROL))
2053 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2054 DefWindowProcA(hwnd, message, wParam, lParam);
2055 if (SHIWORD(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
2056 if (SHIWORD(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2057 return TRUE;
2058
2059 /* Combo messages */
2060
2061 case CB_ADDSTRING16:
2062 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2063 /* fall through */
2064 case CB_ADDSTRING:
2065 return unicode ? SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam) :
2066 SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2067 case CB_INSERTSTRING16:
2068 wParam = (INT)(INT16)wParam;
2069 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2070 /* fall through */
2071 case CB_INSERTSTRING:
2072 return unicode ? SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam) :
2073 SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2074 case CB_DELETESTRING16:
2075 case CB_DELETESTRING:
2076 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2077 SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2078 case CB_SELECTSTRING16:
2079 wParam = (INT)(INT16)wParam;
2080 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2081 /* fall through */
2082 case CB_SELECTSTRING:
2083 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2084 case CB_FINDSTRING16:
2085 wParam = (INT)(INT16)wParam;
2086 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2087 /* fall through */
2088 case CB_FINDSTRING:
2089 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2090 SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2091 case CB_FINDSTRINGEXACT16:
2092 wParam = (INT)(INT16)wParam;
2093 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2094 /* fall through */
2095 case CB_FINDSTRINGEXACT:
2096 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2097 SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2098 case CB_SETITEMHEIGHT16:
2099 wParam = (INT)(INT16)wParam; /* signed integer */
2100 /* fall through */
2101 case CB_SETITEMHEIGHT:
2102 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2103 case CB_GETITEMHEIGHT16:
2104 wParam = (INT)(INT16)wParam;
2105 /* fall through */
2106 case CB_GETITEMHEIGHT:
2107 if( (INT)wParam >= 0 ) /* listbox item */
2108 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2109 return CBGetTextAreaHeight(hwnd, lphc);
2110 case CB_RESETCONTENT16:
2111 case CB_RESETCONTENT:
2112 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2113 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2114 {
2115 static const WCHAR empty_stringW[] = { 0 };
2116 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2117 }
2118 else
2119 InvalidateRect(lphc->self, NULL, TRUE);
2120 return TRUE;
2121 case CB_INITSTORAGE:
2122 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2123 case CB_GETHORIZONTALEXTENT:
2124 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2125 case CB_SETHORIZONTALEXTENT:
2126 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2127 case CB_GETTOPINDEX:
2128 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2129 case CB_GETLOCALE:
2130 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2131 case CB_SETLOCALE:
2132 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2133 case CB_GETDROPPEDWIDTH:
2134 if( lphc->droppedWidth )
2135 return lphc->droppedWidth;
2136 return lphc->droppedRect.right - lphc->droppedRect.left;
2137 case CB_SETDROPPEDWIDTH:
2138 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2139 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2140 return CB_ERR;
2141 case CB_GETDROPPEDCONTROLRECT16:
2142 lParam = (LPARAM)MapSL(lParam);
2143 if( lParam )
2144 {
2145 RECT r;
2146 CBGetDroppedControlRect( lphc, &r );
2147 CONV_RECT32TO16( &r, (LPRECT16)lParam );
2148 }
2149 return CB_OKAY;
2150 case CB_GETDROPPEDCONTROLRECT:
2151 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2152 return CB_OKAY;
2153 case CB_GETDROPPEDSTATE16:
2154 case CB_GETDROPPEDSTATE:
2155 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2156 case CB_DIR16:
2157 lParam = (LPARAM)MapSL(lParam);
2158 message = LB_DIR16;
2159 /* fall through */
2160 case CB_DIR:
2161 if(message == CB_DIR) message = LB_DIR;
2162 return unicode ? SendMessageW(lphc->hWndLBox, message, wParam, lParam) :
2163 SendMessageA(lphc->hWndLBox, message, wParam, lParam);
2164
2165 case CB_SHOWDROPDOWN16:
2166 case CB_SHOWDROPDOWN:
2167 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2168 {
2169 if( wParam )
2170 {
2171 if( !(lphc->wState & CBF_DROPPED) )
2172 CBDropDown( lphc );
2173 }
2174 else
2175 if( lphc->wState & CBF_DROPPED )
2176 CBRollUp( lphc, FALSE, TRUE );
2177 }
2178 return TRUE;
2179 case CB_GETCOUNT16:
2180 case CB_GETCOUNT:
2181 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2182 case CB_GETCURSEL16:
2183 case CB_GETCURSEL:
2184 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2185 case CB_SETCURSEL16:
2186 wParam = (INT)(INT16)wParam;
2187 /* fall through */
2188 case CB_SETCURSEL:
2189 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2190 if( lParam >= 0 )
2191 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2192
2193 /* no LBN_SELCHANGE in this case, update manually */
2194 if( lphc->wState & CBF_EDIT )
2195 CBUpdateEdit( lphc, (INT)wParam );
2196 else
2197 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
2198 lphc->wState &= ~CBF_SELCHANGE;
2199 return lParam;
2200 case CB_GETLBTEXT16:
2201 wParam = (INT)(INT16)wParam;
2202 lParam = (LPARAM)MapSL(lParam);
2203 /* fall through */
2204 case CB_GETLBTEXT:
2205 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2206 SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2207 case CB_GETLBTEXTLEN16:
2208 wParam = (INT)(INT16)wParam;
2209 /* fall through */
2210 case CB_GETLBTEXTLEN:
2211 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2212 case CB_GETITEMDATA16:
2213 wParam = (INT)(INT16)wParam;
2214 /* fall through */
2215 case CB_GETITEMDATA:
2216 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2217 case CB_SETITEMDATA16:
2218 wParam = (INT)(INT16)wParam;
2219 /* fall through */
2220 case CB_SETITEMDATA:
2221 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2222 case CB_GETEDITSEL16:
2223 wParam = lParam = 0; /* just in case */
2224 /* fall through */
2225 case CB_GETEDITSEL:
2226 /* Edit checks passed parameters itself */
2227 if( lphc->wState & CBF_EDIT )
2228 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2229 return CB_ERR;
2230 case CB_SETEDITSEL16:
2231 case CB_SETEDITSEL:
2232 if( lphc->wState & CBF_EDIT )
2233 return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2234 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2235 return CB_ERR;
2236 case CB_SETEXTENDEDUI16:
2237 case CB_SETEXTENDEDUI:
2238 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2239 return CB_ERR;
2240 if( wParam )
2241 lphc->wState |= CBF_EUI;
2242 else lphc->wState &= ~CBF_EUI;
2243 return CB_OKAY;
2244 case CB_GETEXTENDEDUI16:
2245 case CB_GETEXTENDEDUI:
2246 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2247
2248 default:
2249 if (message >= WM_USER)
2250 WARN("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
2251 message - WM_USER, wParam, lParam );
2252 break;
2253 }
2254 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2255 DefWindowProcA(hwnd, message, wParam, lParam);
2256}
2257
2258/***********************************************************************
2259 * ComboWndProcA
2260 *
2261 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2262 * window structs.
2263 */
2264static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2265{
2266 if (!IsWindow(hwnd)) return 0;
2267 return ComboWndProc_common( hwnd, message, wParam, lParam, FALSE );
2268}
2269
2270/***********************************************************************
2271 * ComboWndProcW
2272 */
2273static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2274{
2275 if (!IsWindow(hwnd)) return 0;
2276 return ComboWndProc_common( hwnd, message, wParam, lParam, TRUE );
2277}
Note: See TracBrowser for help on using the repository browser.