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

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

combobox bug fixes (rp7 infinite loop)

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