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

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

EB's fixes

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