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

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

merged with WINE, other fixes

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