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

Last change on this file since 8301 was 8301, checked in by sandervl, 23 years ago

PF: listbox fix when button clicked outside control (rollup dropdown); minimize fixes (activation + z-order)

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