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

Last change on this file since 5087 was 4515, checked in by sandervl, 25 years ago

missing OBM_TNTYPE + fix for messages sent to windows after they received WM_DESTROY & WM_NCDESTORY

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