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

Last change on this file since 1784 was 1784, checked in by cbratschi, 26 years ago

* empty log message *

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