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

Last change on this file since 1391 was 1391, checked in by sandervl, 26 years ago

wm_adjustwindowpos & combobox fixes

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