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

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

added button styles and messages, bug fixes

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