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

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

* empty log message *

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