source: trunk/src/user32/combo.cpp@ 1802

Last change on this file since 1802 was 1802, checked in by achimha, 26 years ago

fixed combo box behavior, added NULL check in Combo_HandleText

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