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

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

combo focus fix

File size: 68.5 KB
Line 
1/* $Id: combo.cpp,v 1.30 2000-03-28 17:13:46 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: 20000317
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 lphc->droppedRect.bottom = lphc->droppedRect.top + posChanging->cy - newComboHeight;
422
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) |
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_CHILD | 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 else /* popup listbox */
521 {
522 lbeStyle |= WS_POPUP;
523 lbeStyle |= WS_EX_TOOLWINDOW;
524 }
525
526 /* Dropdown ComboLBox is not a child window and we cannot pass
527 * ID_CB_LISTBOX directly because it will be treated as a menu handle.
528 */
529 lphc->hWndLBox = CreateWindowExA(lbeExStyle,
530 clbName,
531 NULL,
532 lbeStyle,
533 lphc->droppedRect.left,
534 lphc->droppedRect.top,
535 lphc->droppedRect.right - lphc->droppedRect.left,
536 lphc->droppedRect.bottom - lphc->droppedRect.top,
537 lphc->hwndself,
538 (lphc->dwStyle & CBS_DROPDOWN)? (HMENU)0 : (HMENU)ID_CB_LISTBOX,
539 GetWindowLongA(lphc->hwndself,GWL_HINSTANCE),
540 (LPVOID)lphc );
541
542 /*
543 * The ComboLBox is a strange little beast (when it's not a CBS_SIMPLE)...
544 * It's a popup window but, when you get the window style, you get WS_CHILD.
545 * When created, it's parent is the combobox but, when you ask for it's parent
546 * after that, you're supposed to get the desktop. (see MFC code function
547 * AfxCancelModes)
548 * To achieve this in Wine, we have to create it as a popup and change
549 * it's style to child after the creation.
550 */
551 if ( (lphc->hWndLBox!= 0) &&
552 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
553 {
554 /* Not using SetWindowLongA here because it doesn't allow us to
555 * change the WS_CHILD style. This code should be equivalent (excluding
556 * the change in owner) to:
557 * SetWindowLongA(lphc->hWndLBox, GWL_STYLE,
558 * (GetWindowLongA(lphc->hWndLBox,GWL_STYLE) | WS_CHILD) & ~WS_POPUP);
559 */
560 STYLESTRUCT style;
561 Win32BaseWindow *win32wnd = Win32BaseWindow::GetWindowFromHandle(lphc->hWndLBox);
562
563 if (!win32wnd)
564 {
565 /* Is this the right error? */
566 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
567 return 0;
568 }
569
570 /* save old style, set new style */
571 style.styleOld = win32wnd->getStyle();
572 style.styleNew = (style.styleOld | WS_CHILD) & ~WS_POPUP;
573
574 SendMessageA(lphc->hWndLBox,WM_STYLECHANGING,GWL_STYLE,(LPARAM)&style);
575#if 0 //CB: our code doesn't work with this style change
576 win32wnd->setStyle(style.styleNew);
577#endif
578 SendMessageA(lphc->hWndLBox,WM_STYLECHANGED,GWL_STYLE,(LPARAM)&style);
579
580 /* Set owner to NULL - it seems to be what Windows does to a ComboLBox
581 * after creation.
582 */
583 win32wnd->setOwner(NULL);
584 }
585
586 if( lphc->hWndLBox )
587 {
588 BOOL bEdit = TRUE;
589 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT;
590
591 /*
592 * In Win95 look, the border fo the edit control is
593 * provided by the combobox
594 */
595 if( lphc->wState & CBF_EDIT )
596 {
597 if( lphc->dwStyle & CBS_OEMCONVERT )
598 lbeStyle |= ES_OEMCONVERT;
599 if( lphc->dwStyle & CBS_AUTOHSCROLL )
600 lbeStyle |= ES_AUTOHSCROLL;
601 if( lphc->dwStyle & CBS_LOWERCASE )
602 lbeStyle |= ES_LOWERCASE;
603 else if( lphc->dwStyle & CBS_UPPERCASE )
604 lbeStyle |= ES_UPPERCASE;
605
606 lphc->hWndEdit = CreateWindowExA(0,
607 editName,
608 NULL,
609 lbeStyle,
610 lphc->textRect.left, lphc->textRect.top,
611 lphc->textRect.right - lphc->textRect.left,
612 lphc->textRect.bottom - lphc->textRect.top,
613 lphc->hwndself,
614 (HMENU)ID_CB_EDIT,
615 GetWindowLongA(lphc->hwndself,GWL_HINSTANCE),
616 NULL );
617
618 if( !lphc->hWndEdit )
619 bEdit = FALSE;
620 }
621
622 if( bEdit )
623 {
624 /*
625 * If the combo is a dropdown, we must resize the control to fit only
626 * the text area and button. To do this, we send a dummy resize and the
627 * WM_WINDOWPOSCHANGING message will take care of setting the height for
628 * us.
629 */
630 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
631 {
632 CBForceDummyResize(lphc);
633 }
634
635 //TRACE("init done\n");
636 return hwnd;
637 }
638 //ERR("edit control failure.\n");
639 } //else ERR("listbox failure.\n");
640 } //else ERR("no owner for visible combo.\n");
641
642 /* CreateWindow() will send WM_NCDESTROY to cleanup */
643
644 return -1;
645}
646
647/***********************************************************************
648 * CBPaintButton
649 *
650 * Paint combo button (normal, pressed, and disabled states).
651 */
652static void CBPaintButton(
653 LPHEADCOMBO lphc,
654 HDC hdc,
655 RECT rectButton)
656{
657 if( lphc->wState & CBF_NOREDRAW )
658 return;
659
660
661 UINT buttonState = DFCS_SCROLLCOMBOBOX;
662
663 if (lphc->wState & CBF_BUTTONDOWN)
664 {
665 buttonState |= DFCS_PUSHED;
666 }
667
668 if (CB_DISABLED(lphc))
669 {
670 buttonState |= DFCS_INACTIVE;
671 }
672
673 DrawFrameControl(hdc,&rectButton,DFC_SCROLL,buttonState);
674}
675
676/***********************************************************************
677 * CBPaintText
678 *
679 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
680 */
681static void CBPaintText(
682 LPHEADCOMBO lphc,
683 HDC hdc,
684 RECT rectEdit)
685{
686 INT id, size = 0;
687 LPSTR pText = NULL;
688
689 if( lphc->wState & CBF_NOREDRAW ) return;
690
691 /* follow Windows combobox that sends a bunch of text
692 * inquiries to its listbox while processing WM_PAINT. */
693
694 if( (id = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
695 {
696 size = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
697 if( (pText = (char*)HeapAlloc( GetProcessHeap(), 0, size + 1)) != NULL )
698 {
699 SendMessageA( lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText );
700 pText[size] = '\0'; /* just in case */
701 } else return;
702 }
703
704 if( lphc->wState & CBF_EDIT )
705 {
706 if( CB_HASSTRINGS(lphc) ) SetWindowTextA( lphc->hWndEdit, pText ? pText : "" );
707 if( lphc->wState & CBF_FOCUSED )
708 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
709 }
710 else /* paint text field ourselves */
711 {
712 UINT itemState;
713 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
714
715 /*
716 * Give ourselves some space.
717 */
718 InflateRect( &rectEdit, -1, -1 );
719
720 if ( (lphc->wState & CBF_FOCUSED) &&
721 !(lphc->wState & CBF_DROPPED) )
722 {
723 /* highlight */
724
725 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
726 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
727 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
728 itemState = ODS_SELECTED | ODS_FOCUS;
729 }
730 else
731 itemState = 0;
732
733 if( CB_OWNERDRAWN(lphc) )
734 {
735 DRAWITEMSTRUCT dis;
736 HRGN clipRegion;
737 DWORD dwStyle = GetWindowLongA(lphc->hwndself,GWL_STYLE);
738
739 /*
740 * Save the current clip region.
741 * To retrieve the clip region, we need to create one "dummy"
742 * clip region.
743 */
744 clipRegion = CreateRectRgnIndirect(&rectEdit);
745
746 if (GetClipRgn(hdc, clipRegion)!=1)
747 {
748 DeleteObject(clipRegion);
749 clipRegion=(HRGN)NULL;
750 }
751
752 if ( dwStyle & WS_DISABLED )
753 itemState |= ODS_DISABLED;
754
755 dis.CtlType = ODT_COMBOBOX;
756 dis.CtlID = GetWindowLongA(lphc->hwndself,GWL_ID);
757 dis.hwndItem = lphc->hwndself;
758 dis.itemAction = ODA_DRAWENTIRE;
759 dis.itemID = id;
760 dis.itemState = itemState | ODS_COMBOBOXEDIT;
761 dis.hDC = hdc;
762 dis.rcItem = rectEdit;
763 dis.itemData = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA,
764 (WPARAM)id, 0 );
765
766 /*
767 * Clip the DC and have the parent draw the item.
768 */
769 IntersectClipRect(hdc,
770 rectEdit.left, rectEdit.top,
771 rectEdit.right, rectEdit.bottom);
772
773 SendMessageA(lphc->owner, WM_DRAWITEM,
774 GetWindowLongA(lphc->hwndself,GWL_ID), (LPARAM)&dis );
775
776 /*
777 * Reset the clipping region.
778 */
779 SelectClipRgn(hdc, clipRegion);
780 }
781 else
782 {
783 ExtTextOutA( hdc,
784 rectEdit.left + 1,
785 rectEdit.top + 1,
786 ETO_OPAQUE | ETO_CLIPPED,
787 &rectEdit,
788 pText ? pText : "" , size, NULL );
789
790 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
791 DrawFocusRect( hdc, &rectEdit );
792 }
793
794 if( hPrevFont )
795 SelectObject(hdc, hPrevFont );
796 }
797 if (pText)
798 HeapFree( GetProcessHeap(), 0, pText );
799}
800
801/***********************************************************************
802 * CBPaintBorder
803 */
804static void CBPaintBorder(
805 HWND hwnd,
806 LPHEADCOMBO lphc,
807 HDC hdc)
808{
809 RECT clientRect;
810
811 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
812 {
813 GetClientRect(hwnd, &clientRect);
814 }
815 else
816 {
817 CopyRect(&clientRect, &lphc->textRect);
818
819 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
820 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
821 }
822
823 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
824}
825
826/***********************************************************************
827 * COMBO_PrepareColors
828 *
829 * This method will sent the appropriate WM_CTLCOLOR message to
830 * prepare and setup the colors for the combo's DC.
831 *
832 * It also returns the brush to use for the background.
833 */
834static HBRUSH COMBO_PrepareColors(
835 HWND hwnd,
836 LPHEADCOMBO lphc,
837 HDC hDC)
838{
839 HBRUSH hBkgBrush;
840
841 /*
842 * Get the background brush for this control.
843 */
844 if (CB_DISABLED(lphc))
845 {
846 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORSTATIC,
847 hDC, lphc->hwndself );
848
849 /*
850 * We have to change the text color since WM_CTLCOLORSTATIC will
851 * set it to the "enabled" color. This is the same behavior as the
852 * edit control
853 */
854 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
855 }
856 else
857 {
858 if (lphc->wState & CBF_EDIT)
859 {
860 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLOREDIT,
861 hDC, lphc->hwndself );
862 }
863 else
864 {
865 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
866 hDC, lphc->hwndself );
867 }
868 }
869
870 /*
871 * Catch errors.
872 */
873 if( !hBkgBrush )
874 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
875
876 return hBkgBrush;
877}
878
879/***********************************************************************
880 * COMBO_EraseBackground
881 */
882static LRESULT COMBO_EraseBackground(HWND hwnd,WPARAM wParam,LPARAM lParam)
883{
884 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
885 HBRUSH hBkgBrush;
886 RECT clientRect;
887 HDC hDC;
888
889 hDC = (wParam) ? (HDC)wParam:GetDC(hwnd);
890
891 /*
892 * Calculate the area that we want to erase.
893 */
894 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
895 {
896 GetClientRect(hwnd, &clientRect);
897 }
898 else
899 {
900 CopyRect(&clientRect, &lphc->textRect);
901
902 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
903 }
904
905 /*
906 * Retrieve the background brush
907 */
908 hBkgBrush = COMBO_PrepareColors(hwnd, lphc, hDC);
909
910 FillRect(hDC, &clientRect, hBkgBrush);
911
912 if (!wParam)
913 ReleaseDC(hwnd, hDC);
914
915 return TRUE;
916}
917
918static LRESULT COMBO_GetDlgCode(HWND hwnd,WPARAM wParam,LPARAM lParam)
919{
920 return DLGC_WANTARROWS | DLGC_WANTCHARS;
921}
922
923/***********************************************************************
924 * COMBO_Paint
925 */
926static LRESULT COMBO_Paint(HWND hwnd,WPARAM wParam,LPARAM lParam)
927{
928 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
929 PAINTSTRUCT ps;
930 HDC hDC;
931
932 hDC = (wParam) ? (HDC)wParam:BeginPaint(hwnd,&ps);
933
934
935 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
936 {
937 HBRUSH hPrevBrush, hBkgBrush;
938
939 /*
940 * Retrieve the background brush and select it in the
941 * DC.
942 */
943 hBkgBrush = COMBO_PrepareColors(hwnd,lphc,hDC);
944
945 hPrevBrush = SelectObject( hDC, hBkgBrush );
946
947 /*
948 * In non 3.1 look, there is a sunken border on the combobox
949 */
950 CBPaintBorder(CB_HWND(lphc), lphc, hDC);
951
952 if( !IsRectEmpty(&lphc->buttonRect) )
953 {
954 CBPaintButton(lphc, hDC, lphc->buttonRect);
955 }
956
957 if( !(lphc->wState & CBF_EDIT) )
958 {
959 /*
960 * The text area has a border only in Win 3.1 look.
961 */
962
963 CBPaintText( lphc, hDC, lphc->textRect);
964 }
965
966 if( hPrevBrush )
967 SelectObject( hDC, hPrevBrush );
968 }
969
970 if(!wParam) EndPaint(hwnd,&ps);
971
972 return 0;
973}
974
975static LRESULT COMBO_PrintClient(HWND hwnd,WPARAM wParam,LPARAM lParam)
976{
977 if (lParam & PRF_ERASEBKGND) COMBO_EraseBackground(hwnd,wParam,lParam);
978
979 return COMBO_Paint(hwnd,wParam,lParam);
980}
981
982/***********************************************************************
983 * CBUpdateLBox
984 *
985 * Select listbox entry according to the contents of the edit control.
986 */
987static INT CBUpdateLBox( LPHEADCOMBO lphc )
988{
989 INT length, idx;
990 LPSTR pText = NULL;
991
992 idx = LB_ERR;
993 length = CB_GETEDITTEXTLENGTH( lphc );
994
995 if( length > 0 )
996 pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1);
997
998 //TRACE("\t edit text length %i\n", length );
999
1000 if( pText )
1001 {
1002 if( length ) GetWindowTextA( lphc->hWndEdit, pText, length + 1);
1003 else pText[0] = '\0';
1004 idx = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
1005 (WPARAM)(-1), (LPARAM)pText );
1006 HeapFree( GetProcessHeap(), 0, pText );
1007 }
1008
1009 if( idx >= 0 )
1010 {
1011 /* the LB_SETCARETINDEX message should scroll the list box if required
1012 and sending LB_SETTOPINDEX can generate annoying behaviors */
1013 /* probably superfluous but Windows sends this too */
1014 SendMessageA( lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)idx, 0 );
1015 }
1016 return idx;
1017}
1018
1019/***********************************************************************
1020 * CBUpdateEdit
1021 *
1022 * Copy a listbox entry to the edit control.
1023 */
1024static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
1025{
1026 INT length;
1027 LPSTR pText = NULL;
1028
1029 //TRACE("\t %i\n", index );
1030
1031 if( index >= 0 ) /* got an entry */
1032 {
1033 length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1034 if( length )
1035 {
1036 pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1);
1037 if(pText)
1038 {
1039 SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1040 (WPARAM)index, (LPARAM)pText );
1041 }
1042 }
1043 }
1044 lphc->wState |= CBF_NOEDITNOTIFY;
1045
1046 SendMessageA( lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)"" );
1047 if( lphc->wState & CBF_FOCUSED )
1048 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1049
1050
1051 if( pText )
1052 HeapFree( GetProcessHeap(), 0, pText );
1053}
1054
1055/***********************************************************************
1056 * CBDropDown
1057 *
1058 * Show listbox popup.
1059 */
1060static void CBDropDown( LPHEADCOMBO lphc )
1061{
1062 RECT rect;
1063 int nItems = 0;
1064 int i;
1065 int nHeight;
1066 int nDroppedHeight,nTempDroppedHeight;
1067 int screenH
1068
1069 //TRACE("[%04x]: drop down\n", CB_HWND(lphc));
1070
1071 CB_NOTIFY( lphc, CBN_DROPDOWN );
1072
1073 /* set selection */
1074
1075 lphc->wState |= CBF_DROPPED;
1076 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1077 {
1078 lphc->droppedIndex = CBUpdateLBox( lphc );
1079
1080 if( !(lphc->wState & CBF_CAPTURE) )
1081 CBUpdateEdit( lphc, lphc->droppedIndex );
1082 }
1083 else
1084 {
1085 lphc->droppedIndex = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1086
1087 if( lphc->droppedIndex == LB_ERR )
1088 lphc->droppedIndex = 0;
1089
1090 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)lphc->droppedIndex, 0 );
1091 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1092 }
1093
1094 /* now set popup position */
1095 GetWindowRect( lphc->hwndself, &rect );
1096
1097 /*
1098 * If it's a dropdown, the listbox is offset
1099 */
1100 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1101 rect.left += COMBO_EDITBUTTONSPACE();
1102
1103 /* if the dropped height is greater than the total height of the dropped
1104 items list, then force the drop down list height to be the total height
1105 of the items in the dropped list */
1106
1107 /* And Remove any extra space (Best Fit) */
1108 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1109 nItems = (int)SendMessageA (lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1110 nHeight = COMBO_YBORDERSIZE();
1111 nTempDroppedHeight = 0;
1112 for (i = 0; i < nItems; i++)
1113 {
1114 nHeight += (int)SendMessageA (lphc->hWndLBox, LB_GETITEMHEIGHT, i, 0);
1115
1116 /* Did we pass the limit of what can be displayed */
1117 if (nHeight > nDroppedHeight)
1118 {
1119 break;
1120 }
1121 nTempDroppedHeight = nHeight;
1122 }
1123
1124 nDroppedHeight = nTempDroppedHeight;
1125
1126 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1127 screenH = GetSystemMetrics(SM_CYSCREEN);
1128 if((rect.bottom + nDroppedHeight) >= screenH)
1129 {
1130 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.top - nDroppedHeight,
1131 lphc->droppedRect.right - lphc->droppedRect.left,
1132 nDroppedHeight,
1133 SWP_NOACTIVATE | SWP_NOREDRAW);
1134 }
1135 else
1136 {
1137 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1138 lphc->droppedRect.right - lphc->droppedRect.left,
1139 nDroppedHeight,
1140 SWP_NOACTIVATE | SWP_NOREDRAW);
1141 }
1142
1143 if( !(lphc->wState & CBF_NOREDRAW) )
1144 RedrawWindow( lphc->hwndself, NULL, 0, RDW_INVALIDATE |
1145 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1146
1147 EnableWindow( lphc->hWndLBox, TRUE );
1148 //SvL: Don't activate combo listbox window or else focus will change
1149 // (this window's parent is the desktop)
1150 ShowWindow( lphc->hWndLBox, SW_SHOWNOACTIVATE);
1151
1152}
1153
1154/***********************************************************************
1155 * CBRollUp
1156 *
1157 * Hide listbox popup.
1158 */
1159static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1160{
1161 HWND hWnd = lphc->hwndself;
1162
1163 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1164
1165 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1166 {
1167
1168 //TRACE("[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
1169
1170 if( lphc->wState & CBF_DROPPED )
1171 {
1172 RECT rect;
1173
1174 lphc->wState &= ~CBF_DROPPED;
1175 ShowWindow( lphc->hWndLBox, SW_HIDE );
1176 if(GetCapture() == lphc->hWndLBox)
1177 {
1178 ReleaseCapture();
1179 }
1180
1181 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1182 {
1183 INT index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1184 CBUpdateEdit( lphc, index );
1185 rect = lphc->buttonRect;
1186 }
1187 else
1188 {
1189 if( bButton )
1190 {
1191 UnionRect( &rect,
1192 &lphc->buttonRect,
1193 &lphc->textRect);
1194 }
1195 else
1196 rect = lphc->textRect;
1197
1198 bButton = TRUE;
1199 }
1200
1201 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1202 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1203 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1204 CB_NOTIFY( lphc, CBN_CLOSEUP );
1205 }
1206 }
1207}
1208
1209/***********************************************************************
1210 * COMBO_FlipListbox
1211 *
1212 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1213 */
1214BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL bRedrawButton )
1215{
1216 if( lphc->wState & CBF_DROPPED )
1217 {
1218 CBRollUp( lphc, TRUE, bRedrawButton );
1219 return FALSE;
1220 }
1221
1222 CBDropDown( lphc );
1223 return TRUE;
1224}
1225
1226/***********************************************************************
1227 * COMBO_GetLBWindow
1228 *
1229 * Edit control helper.
1230 */
1231HWND COMBO_GetLBWindow( HWND hwnd )
1232{
1233 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1234
1235 return lphc ? lphc->hWndLBox:0;
1236}
1237
1238
1239/***********************************************************************
1240 * CBRepaintButton
1241 */
1242static void CBRepaintButton( LPHEADCOMBO lphc )
1243 {
1244 InvalidateRect(CB_HWND(lphc), &lphc->buttonRect, TRUE);
1245 UpdateWindow(CB_HWND(lphc));
1246}
1247
1248static VOID COMBO_EditSetFocus(LPHEADCOMBO lphc)
1249{
1250 if(!(lphc->wState & CBF_FOCUSED))
1251 {
1252 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1253 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1254
1255 lphc->wState |= CBF_FOCUSED;
1256
1257 if( !(lphc->wState & CBF_EDIT) )
1258 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1259
1260 CB_NOTIFY( lphc, CBN_SETFOCUS );
1261 }
1262}
1263
1264/***********************************************************************
1265 * COMBO_SetFocus
1266 */
1267static LRESULT COMBO_SetFocus(HWND hwnd,WPARAM wParam,LPARAM lParam)
1268{
1269 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1270
1271//SvL: This doesn't work. Example:
1272// Click on combo box in Abiword (when it doesn't have the focus)
1273// COMBO_LButtonDown checks focus, not set -> SetFocus to combo box
1274// Next thing it does is check if it has the focus (CBF_FOCUSED flag).
1275// This check fails as SetFocus(lphc->hWndEdit) doesn't change this flag.
1276// Removing this check doesn't work as the listbox is not removed after it
1277// loses the focus.
1278// if(lphc->wState & CBF_EDIT)
1279// SetFocus(lphc->hWndEdit);
1280// else
1281 COMBO_EditSetFocus(lphc);
1282
1283 return 0;
1284}
1285
1286static VOID COMBO_EditKillFocus(LPHEADCOMBO lphc)
1287{
1288 if( lphc->wState & CBF_FOCUSED )
1289 {
1290 CBRollUp( lphc, FALSE, TRUE );
1291 if( IsWindow( lphc->hwndself ) )
1292 {
1293 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1294 SendMessageA( lphc->hWndLBox, LB_CARETOFF, 0, 0 );
1295
1296 lphc->wState &= ~CBF_FOCUSED;
1297
1298 /* redraw text */
1299 if( !(lphc->wState & CBF_EDIT) )
1300 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1301
1302 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1303 }
1304 }
1305}
1306
1307/***********************************************************************
1308 * COMBO_KillFocus
1309 */
1310static LRESULT COMBO_KillFocus(HWND hwnd,WPARAM wParam,LPARAM lParam)
1311{
1312 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1313
1314 if(!wParam || (wParam != lphc->hWndEdit && wParam != lphc->hWndLBox ))
1315 {
1316 COMBO_EditKillFocus(lphc);
1317 }
1318
1319 return 0;
1320}
1321
1322/***********************************************************************
1323 * COMBO_Command
1324 */
1325static LRESULT COMBO_Command(HWND hwnd,WPARAM wParam,LPARAM lParam)
1326{
1327 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1328
1329 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == lParam )
1330 {
1331 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1332
1333 switch( HIWORD(wParam) >> 8 )
1334 {
1335 case (EN_SETFOCUS >> 8):
1336
1337 //TRACE("[%04x]: edit [%04x] got focus\n",
1338 // CB_HWND(lphc), lphc->hWndEdit );
1339
1340 COMBO_EditSetFocus(lphc);
1341 break;
1342
1343 case (EN_KILLFOCUS >> 8):
1344
1345 //TRACE("[%04x]: edit [%04x] lost focus\n",
1346 // CB_HWND(lphc), lphc->hWndEdit );
1347
1348 /* NOTE: it seems that Windows' edit control sends an
1349 * undocumented message WM_USER + 0x1B instead of this
1350 * notification (only when it happens to be a part of
1351 * the combo). ?? - AK.
1352 */
1353
1354 COMBO_EditKillFocus(lphc);
1355 break;
1356
1357
1358 case (EN_CHANGE >> 8):
1359 /*
1360 * In some circumstances (when the selection of the combobox
1361 * is changed for example) we don't wans the EN_CHANGE notification
1362 * to be forwarded to the parent of the combobox. This code
1363 * checks a flag that is set in these occasions and ignores the
1364 * notification.
1365 */
1366 if (lphc->wState & CBF_NOEDITNOTIFY)
1367 {
1368 lphc->wState &= ~CBF_NOEDITNOTIFY;
1369 }
1370 else
1371 {
1372 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1373 }
1374
1375 if (lphc->wState & CBF_NOLBSELECT)
1376 {
1377 lphc->wState &= ~CBF_NOLBSELECT;
1378 }
1379 else
1380 {
1381 CBUpdateLBox( lphc );
1382 }
1383 break;
1384
1385 case (EN_UPDATE >> 8):
1386 //SvL: Don't send updates either. (Realplayer 7 infinite loops)
1387 //CB: note: EN_UPDATE changes in Corel 20000317
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 CurSel = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1907 INT index = SendMessageA(lphc->hWndLBox,LB_SELECTSTRING,wParam,lParam);
1908
1909 if( index >= 0 )
1910 {
1911 if( lphc->wState & CBF_EDIT )
1912 {
1913 if (CurSel != index)
1914 CBUpdateEdit( lphc, index );
1915 } else
1916 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1917 }
1918
1919 return (LRESULT)index;
1920}
1921
1922static LRESULT COMBO_FindString(HWND hwnd,WPARAM wParam,LPARAM lParam)
1923{
1924 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1925
1926 return SendMessageA(lphc->hWndLBox,LB_FINDSTRING,wParam,lParam);
1927}
1928
1929static LRESULT COMBO_FindStringExact(HWND hwnd,WPARAM wParam,LPARAM lParam)
1930{
1931 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1932
1933 return SendMessageA(lphc->hWndLBox,LB_FINDSTRINGEXACT,wParam,lParam);
1934}
1935
1936/***********************************************************************
1937 * COMBO_SetItemHeight
1938 */
1939static LRESULT COMBO_SetItemHeight(HWND hwnd,WPARAM wParam,LPARAM lParam)
1940{
1941 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1942 LRESULT lRet = CB_ERR;
1943
1944 if( wParam == -1 ) /* set text field height */
1945 {
1946 if( lParam < 32768 )
1947 {
1948 lphc->editHeight = lParam;
1949
1950 /*
1951 * Redo the layout of the control.
1952 */
1953 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1954 {
1955 CBCalcPlacement(hwnd,
1956 lphc,
1957 &lphc->textRect,
1958 &lphc->buttonRect,
1959 &lphc->droppedRect);
1960
1961 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1962 }
1963 else
1964 {
1965 CBForceDummyResize(lphc);
1966 }
1967
1968 lRet = lParam;
1969 }
1970 }
1971 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1972 lRet = SendMessageA( lphc->hWndLBox, LB_SETITEMHEIGHT,
1973 wParam,lParam);
1974 return lRet;
1975}
1976
1977static LRESULT COMBO_GetItemHeight(HWND hwnd,WPARAM wParam,LPARAM lParam)
1978{
1979 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1980
1981 if( (INT)wParam >= 0 ) /* listbox item */
1982 return SendMessageA( lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1983
1984 return CBGetTextAreaHeight(hwnd, lphc);
1985}
1986
1987static LRESULT COMBO_ResetContent(HWND hwnd,WPARAM wParam,LPARAM lParam)
1988{
1989 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1990
1991 SendMessageA( lphc->hWndLBox, LB_RESETCONTENT, 0, 0 );
1992 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
1993
1994 return TRUE;
1995}
1996
1997static LRESULT COMBO_InitStorage(HWND hwnd,WPARAM wParam,LPARAM lParam)
1998{
1999 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2000
2001 return SendMessageA( lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2002}
2003
2004static LRESULT COMBO_GetHorizontalExtent(HWND hwnd,WPARAM wParam,LPARAM lParam)
2005{
2006 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2007
2008 return SendMessageA( lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2009}
2010
2011static LRESULT COMBO_SetHorizontalExtent(HWND hwnd,WPARAM wParam,LPARAM lParam)
2012{
2013 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2014
2015 return SendMessageA( lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2016}
2017
2018static LRESULT COMBO_GetTopIndex(HWND hwnd,WPARAM wParam,LPARAM lParam)
2019{
2020 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2021
2022 return SendMessageA( lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2023}
2024
2025static LRESULT COMBO_GetLocale(HWND hwnd,WPARAM wParam,LPARAM lParam)
2026{
2027 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2028
2029 return SendMessageA( lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2030}
2031
2032static LRESULT COMBO_SetLocale(HWND hwnd,WPARAM wParam,LPARAM lParam)
2033{
2034 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2035
2036 return SendMessageA( lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2037}
2038
2039static LRESULT COMBO_GetDroppedWidth(HWND hwnd,WPARAM wParam,LPARAM lParam)
2040{
2041 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2042
2043 if( lphc->droppedWidth )
2044 return lphc->droppedWidth;
2045
2046 return lphc->droppedRect.right - lphc->droppedRect.left;
2047}
2048
2049static LRESULT COMBO_SetDroppedWidth(HWND hwnd,WPARAM wParam,LPARAM lParam)
2050{
2051 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2052
2053 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2054 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2055
2056 return CB_ERR;
2057}
2058
2059static LRESULT COMBO_GetDroppedControlRect(HWND hwnd,WPARAM wParam,LPARAM lParam)
2060{
2061 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2062
2063 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2064
2065 return CB_OKAY;
2066}
2067
2068static LRESULT COMBO_GetDroppedState(HWND hwnd,WPARAM wParam,LPARAM lParam)
2069{
2070 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2071
2072 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2073}
2074
2075static LRESULT COMBO_Dir(HWND hwnd,WPARAM wParam,LPARAM lParam)
2076{
2077 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2078
2079 return COMBO_Directory( lphc, (UINT)wParam,
2080 (LPSTR)lParam,TRUE);
2081}
2082
2083static LRESULT COMBO_ShowDropDown(HWND hwnd,WPARAM wParam,LPARAM lParam)
2084{
2085 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2086
2087 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2088 {
2089 if( wParam )
2090 {
2091 if( !(lphc->wState & CBF_DROPPED) )
2092 CBDropDown( lphc );
2093 }
2094 else
2095 if( lphc->wState & CBF_DROPPED )
2096 CBRollUp( lphc, FALSE, TRUE );
2097 }
2098
2099 return TRUE;
2100}
2101
2102static LRESULT COMBO_GetCount(HWND hwnd,WPARAM wParam,LPARAM lParam)
2103{
2104 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2105
2106 return SendMessageA( lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2107}
2108
2109static LRESULT COMBO_GetCurSel(HWND hwnd,WPARAM wParam,LPARAM lParam)
2110{
2111 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2112
2113 return SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2114}
2115
2116static LRESULT COMBO_SetCurSel(HWND hwnd,WPARAM wParam,LPARAM lParam)
2117{
2118 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2119
2120 lParam = SendMessageA( lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2121 if( lParam >= 0 )
2122 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2123 if( lphc->wState & CBF_SELCHANGE )
2124 {
2125 //SvL: Clear the flag here; doing it after calling CBUpdateEdit causes
2126 // an infinite loop in RealPlayer 7
2127 lphc->wState &= ~CBF_SELCHANGE;
2128
2129 /* no LBN_SELCHANGE in this case, update manually */
2130 if( lphc->wState & CBF_EDIT )
2131 CBUpdateEdit( lphc, (INT)wParam );
2132 else
2133 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
2134 }
2135
2136 return lParam;
2137}
2138
2139static LRESULT COMBO_GetLBText(HWND hwnd,WPARAM wParam,LPARAM lParam)
2140{
2141 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2142
2143 return SendMessageA( lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2144}
2145
2146static LRESULT COMBO_GetLBTextLen(HWND hwnd,WPARAM wParam,LPARAM lParam)
2147{
2148 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2149
2150 return SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2151}
2152
2153static LRESULT COMBO_GetItemData(HWND hwnd,WPARAM wParam,LPARAM lParam)
2154{
2155 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2156
2157 return SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2158}
2159
2160static LRESULT COMBO_SetItemData(HWND hwnd,WPARAM wParam,LPARAM lParam)
2161{
2162 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2163
2164 return SendMessageA( lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2165}
2166
2167static LRESULT COMBO_GetEditSel(HWND hwnd,WPARAM wParam,LPARAM lParam)
2168{
2169 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2170
2171 if( lphc->wState & CBF_EDIT )
2172 {
2173 INT a, b;
2174
2175 return SendMessageA( lphc->hWndEdit, EM_GETSEL,
2176 (wParam) ? wParam : (WPARAM)&a,
2177 (lParam) ? lParam : (LPARAM)&b );
2178 }
2179
2180 return CB_ERR;
2181}
2182
2183static LRESULT COMBO_SetEditSel(HWND hwnd,WPARAM wParam,LPARAM lParam)
2184{
2185 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2186
2187 if( lphc->wState & CBF_EDIT )
2188 return SendMessageA( lphc->hWndEdit, EM_SETSEL,
2189 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2190
2191 return CB_ERR;
2192}
2193
2194static LRESULT COMBO_SetExtendedUI(HWND hwnd,WPARAM wParam,LPARAM lParam)
2195{
2196 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2197
2198 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2199 return CB_ERR;
2200 if( wParam )
2201 lphc->wState |= CBF_EUI;
2202 else lphc->wState &= ~CBF_EUI;
2203 return CB_OKAY;
2204}
2205
2206static LRESULT COMBO_GetExtendedUI(HWND hwnd,WPARAM wParam,LPARAM lParam)
2207{
2208 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2209
2210 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2211}
2212
2213/***********************************************************************
2214 * ComboWndProc
2215 *
2216 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
2217 */
2218LRESULT WINAPI ComboWndProc( HWND hwnd, UINT message,
2219 WPARAM wParam, LPARAM lParam )
2220{
2221// dprintf(("ComboWndProc hwnd: %04x, msg %s, wp %08x lp %08lx\n",
2222// hwnd, GetMsgText(message), wParam, lParam));
2223
2224 switch(message)
2225 {
2226
2227 /* System messages */
2228
2229 case WM_NCCREATE:
2230 return COMBO_NCCreate(hwnd,wParam,lParam);
2231
2232 case WM_NCDESTROY:
2233 return COMBO_NCDestroy(hwnd,wParam,lParam);
2234
2235 case WM_CREATE:
2236 return COMBO_Create(hwnd,wParam,lParam);
2237
2238 case WM_PRINTCLIENT:
2239 return COMBO_PrintClient(hwnd,wParam,lParam);
2240
2241 case WM_PAINT:
2242 return COMBO_Paint(hwnd,wParam,lParam);
2243
2244 case WM_ERASEBKGND:
2245 return COMBO_EraseBackground(hwnd,wParam,lParam);
2246
2247 case WM_GETDLGCODE:
2248 return COMBO_GetDlgCode(hwnd,wParam,lParam);
2249
2250 case WM_WINDOWPOSCHANGING:
2251 return COMBO_WindowPosChanging(hwnd,wParam,lParam);
2252
2253 case WM_SIZE:
2254 return COMBO_Size(hwnd,wParam,lParam);
2255
2256 case WM_SETFONT:
2257 return COMBO_SetFont(hwnd,wParam,lParam);
2258
2259 case WM_GETFONT:
2260 return COMBO_GetFont(hwnd,wParam,lParam);
2261
2262 case WM_SETFOCUS:
2263 return COMBO_SetFocus(hwnd,wParam,lParam);
2264
2265 case WM_KILLFOCUS:
2266 return COMBO_KillFocus(hwnd,wParam,lParam);
2267
2268 case WM_COMMAND:
2269 return COMBO_Command(hwnd,wParam,lParam);
2270
2271 case WM_GETTEXT:
2272 return COMBO_GetText(hwnd,wParam,lParam);
2273
2274 case WM_SETTEXT:
2275 case WM_GETTEXTLENGTH:
2276 case WM_CLEAR:
2277 case WM_CUT:
2278 case WM_PASTE:
2279 case WM_COPY:
2280 return COMBO_HandleText(hwnd,message,wParam,lParam);
2281
2282 case WM_DRAWITEM:
2283 case WM_DELETEITEM:
2284 case WM_COMPAREITEM:
2285 case WM_MEASUREITEM:
2286 return COMBO_HandleItem(hwnd,message,wParam,lParam);
2287
2288 case WM_ENABLE:
2289 return COMBO_Enable(hwnd,wParam,lParam);
2290
2291 case WM_SETREDRAW:
2292 return COMBO_SetRedraw(hwnd,wParam,lParam);
2293
2294 case WM_SYSKEYDOWN:
2295 return COMBO_SysKeyDown(hwnd,wParam,lParam);
2296
2297 case WM_CHAR:
2298 case WM_KEYDOWN:
2299 return COMBO_HandleKey(hwnd,message,wParam,lParam);
2300
2301 case WM_LBUTTONDOWN:
2302 return COMBO_LButtonDown(hwnd,wParam,lParam);
2303
2304 case WM_LBUTTONUP:
2305 return COMBO_LButtonUp(hwnd,wParam,lParam);
2306
2307 case WM_MOUSEMOVE:
2308 return COMBO_MouseMove(hwnd,wParam,lParam);
2309
2310 case WM_MOUSEWHEEL:
2311 return COMBO_MouseWheel(hwnd,wParam,lParam);
2312
2313 /* Combo messages */
2314
2315 case CB_ADDSTRING:
2316 return COMBO_AddString(hwnd,wParam,lParam);
2317
2318 case CB_INSERTSTRING:
2319 return COMBO_InsertString(hwnd,wParam,lParam);
2320
2321 case CB_DELETESTRING:
2322 return COMBO_DeleteString(hwnd,wParam,lParam);
2323
2324 case CB_SELECTSTRING:
2325 return COMBO_SelectString(hwnd,wParam,lParam);
2326
2327 case CB_FINDSTRING:
2328 return COMBO_FindString(hwnd,wParam,lParam);
2329
2330 case CB_FINDSTRINGEXACT:
2331 return COMBO_FindStringExact(hwnd,wParam,lParam);
2332
2333 case CB_SETITEMHEIGHT:
2334 return COMBO_SetItemHeight(hwnd,wParam,lParam);
2335
2336 case CB_GETITEMHEIGHT:
2337 return COMBO_GetItemHeight(hwnd,wParam,lParam);
2338
2339 case CB_RESETCONTENT:
2340 return COMBO_ResetContent(hwnd,wParam,lParam);
2341
2342 case CB_INITSTORAGE:
2343 return COMBO_InitStorage(hwnd,wParam,lParam);
2344
2345 case CB_GETHORIZONTALEXTENT:
2346 return COMBO_GetHorizontalExtent(hwnd,wParam,lParam);
2347
2348 case CB_SETHORIZONTALEXTENT:
2349 return COMBO_SetHorizontalExtent(hwnd,wParam,lParam);
2350
2351 case CB_GETTOPINDEX:
2352 return COMBO_GetTopIndex(hwnd,wParam,lParam);
2353
2354 case CB_GETLOCALE:
2355 return COMBO_GetLocale(hwnd,wParam,lParam);
2356
2357 case CB_SETLOCALE:
2358 return COMBO_SetLocale(hwnd,wParam,lParam);
2359
2360 case CB_GETDROPPEDWIDTH:
2361 return COMBO_GetDroppedWidth(hwnd,wParam,lParam);
2362
2363 case CB_SETDROPPEDWIDTH:
2364 return COMBO_SetDroppedWidth(hwnd,wParam,lParam);
2365
2366 case CB_GETDROPPEDCONTROLRECT:
2367 return COMBO_GetDroppedControlRect(hwnd,wParam,lParam);
2368
2369 case CB_GETDROPPEDSTATE:
2370 return COMBO_GetDroppedState(hwnd,wParam,lParam);
2371
2372 case CB_DIR:
2373 return COMBO_Dir(hwnd,wParam,lParam);
2374
2375 case CB_SHOWDROPDOWN:
2376 return COMBO_ShowDropDown(hwnd,wParam,lParam);
2377
2378 case CB_GETCOUNT:
2379 return COMBO_GetCount(hwnd,wParam,lParam);
2380
2381 case CB_GETCURSEL:
2382 return COMBO_GetCurSel(hwnd,wParam,lParam);
2383
2384 case CB_SETCURSEL:
2385 return COMBO_SetCurSel(hwnd,wParam,lParam);
2386
2387 case CB_GETLBTEXT:
2388 return COMBO_GetLBText(hwnd,wParam,lParam);
2389
2390 case CB_GETLBTEXTLEN:
2391 return COMBO_GetLBTextLen(hwnd,wParam,lParam);
2392
2393 case CB_GETITEMDATA:
2394 return COMBO_GetItemData(hwnd,wParam,lParam);
2395
2396 case CB_SETITEMDATA:
2397 return COMBO_SetItemData(hwnd,wParam,lParam);
2398
2399 case CB_GETEDITSEL:
2400 return COMBO_GetEditSel(hwnd,wParam,lParam);
2401
2402 case CB_SETEDITSEL:
2403 return COMBO_SetEditSel(hwnd,wParam,lParam);
2404
2405 case CB_SETEXTENDEDUI:
2406 return COMBO_SetExtendedUI(hwnd,wParam,lParam);
2407
2408 case CB_GETEXTENDEDUI:
2409 return COMBO_GetExtendedUI(hwnd,wParam,lParam);
2410
2411 //case (WM_USER + 0x1B):
2412 // WARN("[%04x]: undocumented msg!\n", hwnd );
2413 }
2414 return DefWindowProcA(hwnd, message, wParam, lParam);
2415}
2416
2417BOOL COMBOBOX_Register()
2418{
2419 WNDCLASSA wndClass;
2420
2421//SvL: Don't check this now
2422// if (GlobalFindAtomA(COMBOBOXCLASSNAME)) return FALSE;
2423
2424 ZeroMemory(&wndClass,sizeof(WNDCLASSA));
2425 wndClass.style = CS_GLOBALCLASS | CS_PARENTDC;
2426 wndClass.lpfnWndProc = (WNDPROC)ComboWndProc;
2427 wndClass.cbClsExtra = 0;
2428 wndClass.cbWndExtra = sizeof(VOID*);
2429 wndClass.hCursor = LoadCursorA(0,IDC_ARROWA);
2430 wndClass.hbrBackground = (HBRUSH)0;
2431 wndClass.lpszClassName = COMBOBOXCLASSNAME;
2432
2433 return RegisterClassA(&wndClass);
2434}
2435
2436BOOL COMBOBOX_Unregister()
2437{
2438 if (GlobalFindAtomA(COMBOBOXCLASSNAME))
2439 return UnregisterClassA(COMBOBOXCLASSNAME,(HINSTANCE)NULL);
2440 else return FALSE;
2441}
2442
Note: See TracBrowser for help on using the repository browser.