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

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

compile fixes

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