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

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

Fixed combo box focus bug (combo box wouldn't expand if it does not have the focus (AbiWord))

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