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

Last change on this file since 3241 was 3241, checked in by cbratschi, 25 years ago

WM_DRAWITEM fix

File size: 68.3 KB
Line 
1/* $Id: combo.cpp,v 1.29 2000-03-26 16:30:29 cbratschi Exp $ */
2/*
3 * Combo controls
4 *
5 * Copyright 1997 Alex Korobka
6 * Copyright 1999 Christoph Bratschi
7 *
8 * FIXME: roll up in Netscape 3.01.
9 *
10 * 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 ShowWindow( lphc->hWndLBox, SW_SHOW);
1149
1150}
1151
1152/***********************************************************************
1153 * CBRollUp
1154 *
1155 * Hide listbox popup.
1156 */
1157static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1158{
1159 HWND hWnd = lphc->hwndself;
1160
1161 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1162
1163 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1164 {
1165
1166 //TRACE("[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
1167
1168 if( lphc->wState & CBF_DROPPED )
1169 {
1170 RECT rect;
1171
1172 lphc->wState &= ~CBF_DROPPED;
1173 ShowWindow( lphc->hWndLBox, SW_HIDE );
1174 if(GetCapture() == lphc->hWndLBox)
1175 {
1176 ReleaseCapture();
1177 }
1178
1179 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1180 {
1181 INT index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1182 CBUpdateEdit( lphc, index );
1183 rect = lphc->buttonRect;
1184 }
1185 else
1186 {
1187 if( bButton )
1188 {
1189 UnionRect( &rect,
1190 &lphc->buttonRect,
1191 &lphc->textRect);
1192 }
1193 else
1194 rect = lphc->textRect;
1195
1196 bButton = TRUE;
1197 }
1198
1199 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1200 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1201 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1202 CB_NOTIFY( lphc, CBN_CLOSEUP );
1203 }
1204 }
1205}
1206
1207/***********************************************************************
1208 * COMBO_FlipListbox
1209 *
1210 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1211 */
1212BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL bRedrawButton )
1213{
1214 if( lphc->wState & CBF_DROPPED )
1215 {
1216 CBRollUp( lphc, TRUE, bRedrawButton );
1217 return FALSE;
1218 }
1219
1220 CBDropDown( lphc );
1221 return TRUE;
1222}
1223
1224/***********************************************************************
1225 * COMBO_GetLBWindow
1226 *
1227 * Edit control helper.
1228 */
1229HWND COMBO_GetLBWindow( HWND hwnd )
1230{
1231 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1232
1233 return lphc ? lphc->hWndLBox:0;
1234}
1235
1236
1237/***********************************************************************
1238 * CBRepaintButton
1239 */
1240static void CBRepaintButton( LPHEADCOMBO lphc )
1241 {
1242 InvalidateRect(CB_HWND(lphc), &lphc->buttonRect, TRUE);
1243 UpdateWindow(CB_HWND(lphc));
1244}
1245
1246static VOID COMBO_EditSetFocus(LPHEADCOMBO lphc)
1247{
1248 if(!(lphc->wState & CBF_FOCUSED))
1249 {
1250 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1251 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1252
1253 lphc->wState |= CBF_FOCUSED;
1254
1255 if( !(lphc->wState & CBF_EDIT) )
1256 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1257
1258 CB_NOTIFY( lphc, CBN_SETFOCUS );
1259 }
1260}
1261
1262/***********************************************************************
1263 * COMBO_SetFocus
1264 */
1265static LRESULT COMBO_SetFocus(HWND hwnd,WPARAM wParam,LPARAM lParam)
1266{
1267 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1268
1269//SvL: This doesn't work. Example:
1270// Click on combo box in Abiword (when it doesn't have the focus)
1271// COMBO_LButtonDown checks focus, not set -> SetFocus to combo box
1272// Next thing it does is check if it has the focus (CBF_FOCUSED flag).
1273// This check fails as SetFocus(lphc->hWndEdit) doesn't change this flag.
1274// Removing this check doesn't work as the listbox is not removed after it
1275// loses the focus.
1276// if(lphc->wState & CBF_EDIT)
1277// SetFocus(lphc->hWndEdit);
1278// else
1279 COMBO_EditSetFocus(lphc);
1280
1281 return 0;
1282}
1283
1284static VOID COMBO_EditKillFocus(LPHEADCOMBO lphc)
1285{
1286 if( lphc->wState & CBF_FOCUSED )
1287 {
1288 CBRollUp( lphc, FALSE, TRUE );
1289 if( IsWindow( lphc->hwndself ) )
1290 {
1291 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1292 SendMessageA( lphc->hWndLBox, LB_CARETOFF, 0, 0 );
1293
1294 lphc->wState &= ~CBF_FOCUSED;
1295
1296 /* redraw text */
1297 if( !(lphc->wState & CBF_EDIT) )
1298 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1299
1300 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1301 }
1302 }
1303}
1304
1305/***********************************************************************
1306 * COMBO_KillFocus
1307 */
1308static LRESULT COMBO_KillFocus(HWND hwnd,WPARAM wParam,LPARAM lParam)
1309{
1310 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1311
1312 if(!wParam || (wParam != lphc->hWndEdit && wParam != lphc->hWndLBox ))
1313 {
1314 COMBO_EditKillFocus(lphc);
1315 }
1316
1317 return 0;
1318}
1319
1320/***********************************************************************
1321 * COMBO_Command
1322 */
1323static LRESULT COMBO_Command(HWND hwnd,WPARAM wParam,LPARAM lParam)
1324{
1325 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1326
1327 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == lParam )
1328 {
1329 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1330
1331 switch( HIWORD(wParam) >> 8 )
1332 {
1333 case (EN_SETFOCUS >> 8):
1334
1335 //TRACE("[%04x]: edit [%04x] got focus\n",
1336 // CB_HWND(lphc), lphc->hWndEdit );
1337
1338 COMBO_EditSetFocus(lphc);
1339 break;
1340
1341 case (EN_KILLFOCUS >> 8):
1342
1343 //TRACE("[%04x]: edit [%04x] lost focus\n",
1344 // CB_HWND(lphc), lphc->hWndEdit );
1345
1346 /* NOTE: it seems that Windows' edit control sends an
1347 * undocumented message WM_USER + 0x1B instead of this
1348 * notification (only when it happens to be a part of
1349 * the combo). ?? - AK.
1350 */
1351
1352 COMBO_EditKillFocus(lphc);
1353 break;
1354
1355
1356 case (EN_CHANGE >> 8):
1357 /*
1358 * In some circumstances (when the selection of the combobox
1359 * is changed for example) we don't wans the EN_CHANGE notification
1360 * to be forwarded to the parent of the combobox. This code
1361 * checks a flag that is set in these occasions and ignores the
1362 * notification.
1363 */
1364 if (lphc->wState & CBF_NOEDITNOTIFY)
1365 {
1366 lphc->wState &= ~CBF_NOEDITNOTIFY;
1367 }
1368 else
1369 {
1370 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1371 }
1372
1373 if (lphc->wState & CBF_NOLBSELECT)
1374 {
1375 lphc->wState &= ~CBF_NOLBSELECT;
1376 }
1377 else
1378 {
1379 CBUpdateLBox( lphc );
1380 }
1381 break;
1382
1383 case (EN_UPDATE >> 8):
1384 //SvL: Don't send updates either. (Realplayer 7 infinite loops)
1385 //CB: note: EN_UPDATE changes in Corel 20000317
1386 /*
1387 * In some circumstances (when the selection of the combobox
1388 * is changed for example) we don't wans the EN_CHANGE notification
1389 * to be forwarded to the parent of the combobox. This code
1390 * checks a flag that is set in these occasions and ignores the
1391 * notification.
1392 */
1393 if (lphc->wState & CBF_NOEDITNOTIFY)
1394 {
1395 lphc->wState &= ~CBF_NOEDITNOTIFY;
1396 }
1397 else
1398 {
1399 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1400 }
1401 break;
1402
1403 case (EN_ERRSPACE >> 8):
1404 CB_NOTIFY( lphc, CBN_ERRSPACE );
1405 }
1406 }
1407 else if( lphc->hWndLBox == lParam )
1408 {
1409 switch( HIWORD(wParam) )
1410 {
1411 case LBN_ERRSPACE:
1412 CB_NOTIFY( lphc, CBN_ERRSPACE );
1413 break;
1414
1415 case LBN_DBLCLK:
1416 CB_NOTIFY( lphc, CBN_DBLCLK );
1417 break;
1418
1419 case LBN_SELCHANGE:
1420 case LBN_SELCANCEL:
1421
1422 //TRACE("[%04x]: lbox selection change [%04x]\n",
1423 // CB_HWND(lphc), lphc->wState );
1424
1425 /* do not roll up if selection is being tracked
1426 * by arrowkeys in the dropdown listbox */
1427
1428 if( (lphc->dwStyle & CBS_SIMPLE) ||
1429 ((lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP)) )
1430 {
1431 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1432 }
1433 else lphc->wState &= ~CBF_NOROLLUP;
1434
1435 if( lphc->wState & CBF_EDIT )
1436 {
1437 INT index = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1438
1439 lphc->wState |= CBF_NOLBSELECT;
1440 CBUpdateEdit( lphc, index );
1441 /* select text in edit, as Windows does */
1442 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1443 }
1444 else
1445 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1446
1447 CB_NOTIFY( lphc, CBN_SELCHANGE );
1448 /* fall through */
1449
1450 case LBN_SETFOCUS:
1451 case LBN_KILLFOCUS:
1452 /* nothing to do here since ComboLBox always resets the focus to its
1453 * combo/edit counterpart */
1454 break;
1455 }
1456 }
1457 return 0;
1458}
1459
1460/***********************************************************************
1461 * COMBO_HandleItem
1462 *
1463 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1464 */
1465static LRESULT COMBO_HandleItem(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
1466{
1467 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1468
1469 //TRACE("[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
1470
1471#define lpIS ((LPDELETEITEMSTRUCT)lParam)
1472
1473 /* two first items are the same in all 4 structs */
1474 lpIS->CtlType = ODT_COMBOBOX;
1475 lpIS->CtlID = GetWindowLongA(hwnd,GWL_ID);
1476
1477 switch( msg ) /* patch window handle */
1478 {
1479 case WM_DELETEITEM:
1480 lpIS->hwndItem = hwnd;
1481#undef lpIS
1482 break;
1483 case WM_DRAWITEM:
1484#define lpIS ((LPDRAWITEMSTRUCT)lParam)
1485 lpIS->hwndItem = hwnd;
1486#undef lpIS
1487 break;
1488 case WM_COMPAREITEM:
1489#define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
1490 lpIS->hwndItem = hwnd;
1491#undef lpIS
1492 break;
1493 }
1494
1495 return SendMessageA( lphc->owner, msg, GetWindowLongA(hwnd,GWL_ID), lParam );
1496}
1497
1498/***********************************************************************
1499 * COMBO_GetText
1500 */
1501static LRESULT COMBO_GetText(HWND hwnd,WPARAM wParam,LPARAM lParam)
1502{
1503 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1504
1505 if( lphc->wState & CBF_EDIT )
1506 return SendMessageA( lphc->hWndEdit, WM_GETTEXT,
1507 wParam,lParam);
1508
1509 /* get it from the listbox */
1510
1511 if( lphc->hWndLBox )
1512 {
1513 INT idx = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1514 if( idx != LB_ERR )
1515 {
1516 LPSTR lpBuffer;
1517 INT length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN,
1518 (WPARAM)idx, 0 );
1519
1520 /* 'length' is without the terminating character */
1521 if( length >= (UINT)wParam )
1522 lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 );
1523 else
1524 lpBuffer = (LPSTR)lParam;
1525
1526 if( lpBuffer )
1527 {
1528 INT n = SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1529 (WPARAM)idx, (LPARAM)lpBuffer );
1530
1531 /* truncate if buffer is too short */
1532
1533 if( length >= (UINT)wParam )
1534 {
1535 if ((UINT)wParam && lParam) {
1536 if( n != LB_ERR ) memcpy( (LPSTR)lParam, lpBuffer, ((UINT)wParam>n) ? n+1 : (UINT)wParam-1 );
1537 ((LPSTR)lParam)[(UINT)wParam - 1] = '\0';
1538 }
1539 HeapFree( GetProcessHeap(), 0, lpBuffer );
1540 }
1541 return (LRESULT)n;
1542 }
1543 }
1544 }
1545 return 0;
1546}
1547
1548static LRESULT COMBO_HandleText(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
1549{
1550 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1551
1552 if (lphc == NULL)
1553 {
1554 dprintf(("COMBO_HandleText Info Pointer NULL!\n"));
1555 return CB_ERR;
1556 }
1557
1558 if ((message == WM_GETTEXTLENGTH) && !(lphc->wState & CBF_EDIT))
1559 {
1560 int j = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1561
1562 if (j == -1) return 0;
1563 return SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1564 } else if( lphc->wState & CBF_EDIT )
1565 {
1566 lphc->wState |= CBF_NOEDITNOTIFY;
1567
1568 return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1569 } else return CB_ERR;
1570}
1571
1572/***********************************************************************
1573 * CBResetPos
1574 *
1575 * This function sets window positions according to the updated
1576 * component placement struct.
1577 */
1578static void CBResetPos(
1579 LPHEADCOMBO lphc,
1580 LPRECT rectEdit,
1581 LPRECT rectLB,
1582 BOOL bRedraw)
1583{
1584 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1585
1586 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1587 * sizing messages */
1588
1589 if( lphc->wState & CBF_EDIT )
1590 SetWindowPos( lphc->hWndEdit, 0,
1591 rectEdit->left, rectEdit->top,
1592 rectEdit->right - rectEdit->left,
1593 rectEdit->bottom - rectEdit->top,
1594 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1595
1596 SetWindowPos( lphc->hWndLBox, 0,
1597 rectLB->left, rectLB->top,
1598 rectLB->right - rectLB->left,
1599 rectLB->bottom - rectLB->top,
1600 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1601
1602 if( bDrop )
1603 {
1604 if( lphc->wState & CBF_DROPPED )
1605 {
1606 lphc->wState &= ~CBF_DROPPED;
1607 ShowWindow( lphc->hWndLBox, SW_HIDE );
1608 }
1609
1610 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1611 RedrawWindow( lphc->hwndself, NULL, 0,
1612 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1613 }
1614}
1615
1616
1617/***********************************************************************
1618 * COMBO_Size
1619 */
1620static LRESULT COMBO_Size(HWND hwnd,WPARAM wParam,LPARAM lParam)
1621{
1622 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1623
1624 dprintf(("COMBO_Size"));
1625
1626 if(lphc->hWndLBox && !(lphc->wState & CBF_NORESIZE))
1627 {
1628 CBCalcPlacement(hwnd,
1629 lphc,
1630 &lphc->textRect,
1631 &lphc->buttonRect,
1632 &lphc->droppedRect);
1633
1634 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1635 }
1636
1637 return 0;
1638}
1639
1640
1641/***********************************************************************
1642 * COMBO_Font
1643 */
1644static LRESULT COMBO_SetFont(HWND hwnd,WPARAM wParam,LPARAM lParam)
1645{
1646 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1647
1648 /*
1649 * Set the font
1650 */
1651 lphc->hFont = wParam;
1652
1653 /*
1654 * Propagate to owned windows.
1655 */
1656 if( lphc->wState & CBF_EDIT )
1657 SendMessageA(lphc->hWndEdit,WM_SETFONT,wParam,lParam);
1658 SendMessageA(lphc->hWndLBox,WM_SETFONT,wParam,lParam);
1659
1660 /*
1661 * Redo the layout of the control.
1662 */
1663 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1664 {
1665 CBCalcPlacement(hwnd,
1666 lphc,
1667 &lphc->textRect,
1668 &lphc->buttonRect,
1669 &lphc->droppedRect);
1670
1671 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1672 }
1673 else
1674 {
1675 CBForceDummyResize(lphc);
1676 }
1677
1678 return 0;
1679}
1680
1681static LRESULT COMBO_GetFont(HWND hwnd,WPARAM wParam,LPARAM lParam)
1682{
1683 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1684
1685 return lphc->hFont;
1686}
1687
1688
1689/***********************************************************************
1690 * COMBO_LButtonDown
1691 */
1692static LRESULT COMBO_LButtonDown(HWND hwnd,WPARAM wParam,LPARAM lParam)
1693{
1694 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1695 POINT pt;
1696 BOOL bButton;
1697
1698 if(!(lphc->wState & CBF_FOCUSED)) SetFocus(hwnd);
1699 if(!(lphc->wState & CBF_FOCUSED)) return 0;
1700
1701 pt.x = LOWORD(lParam);
1702 pt.y = HIWORD(lParam);
1703 bButton = PtInRect(&lphc->buttonRect, pt);
1704
1705 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1706 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1707 {
1708 lphc->wState |= CBF_BUTTONDOWN;
1709 if( lphc->wState & CBF_DROPPED )
1710 {
1711 /* got a click to cancel selection */
1712
1713 lphc->wState &= ~CBF_BUTTONDOWN;
1714 CBRollUp( lphc, TRUE, FALSE );
1715 if( !IsWindow( hwnd ) ) return 0;
1716
1717 if( lphc->wState & CBF_CAPTURE )
1718 {
1719 lphc->wState &= ~CBF_CAPTURE;
1720 ReleaseCapture();
1721 }
1722 }
1723 else
1724 {
1725 /* drop down the listbox and start tracking */
1726
1727 lphc->wState |= CBF_CAPTURE;
1728 CBDropDown( lphc );
1729 SetCapture( hwnd );
1730 }
1731 if( bButton ) CBRepaintButton( lphc );
1732 }
1733
1734 return 0;
1735}
1736
1737/***********************************************************************
1738 * COMBO_LButtonUp
1739 *
1740 * Release capture and stop tracking if needed.
1741 */
1742static LRESULT COMBO_LButtonUp(HWND hwnd,WPARAM wParam,LPARAM lParam)
1743{
1744 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1745
1746 if( lphc->wState & CBF_CAPTURE )
1747 {
1748 lphc->wState &= ~CBF_CAPTURE;
1749 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1750 {
1751 INT index = CBUpdateLBox( lphc );
1752 CBUpdateEdit( lphc, index );
1753 }
1754 ReleaseCapture();
1755//SvL: Don't set the capture here. Otherwise other controls don't respond the first time!
1756// SetCapture(lphc->hWndLBox);
1757 }
1758
1759 if( lphc->wState & CBF_BUTTONDOWN )
1760 {
1761 lphc->wState &= ~CBF_BUTTONDOWN;
1762 CBRepaintButton( lphc );
1763 }
1764
1765 return 0;
1766}
1767
1768/***********************************************************************
1769 * COMBO_MouseMove
1770 *
1771 * Two things to do - track combo button and release capture when
1772 * pointer goes into the listbox.
1773 */
1774static LRESULT COMBO_MouseMove(HWND hwnd,WPARAM wParam,LPARAM lParam)
1775{
1776 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1777 POINT pt;
1778 RECT lbRect;
1779
1780 if(!(lphc->wState & CBF_CAPTURE)) return 0;
1781
1782 pt.x = LOWORD(lParam);
1783 pt.y = HIWORD(lParam);
1784
1785 if( lphc->wState & CBF_BUTTONDOWN )
1786 {
1787 BOOL bButton;
1788
1789 bButton = PtInRect(&lphc->buttonRect, pt);
1790
1791 if( !bButton )
1792 {
1793 lphc->wState &= ~CBF_BUTTONDOWN;
1794 CBRepaintButton( lphc );
1795 }
1796 }
1797
1798 GetClientRect( lphc->hWndLBox, &lbRect );
1799 MapWindowPoints(hwnd, lphc->hWndLBox, &pt, 1 );
1800 if( PtInRect(&lbRect, pt) )
1801 {
1802 lphc->wState &= ~CBF_CAPTURE;
1803 ReleaseCapture();
1804 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc );
1805
1806 /* hand over pointer tracking */
1807 SendMessageA( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam );
1808 }
1809
1810 return 0;
1811}
1812
1813static LRESULT COMBO_MouseWheel(HWND hwnd,WPARAM wParam,LPARAM lParam)
1814{
1815 if (wParam & (MK_SHIFT | MK_CONTROL))
1816 return DefWindowProcA(hwnd,WM_MOUSEWHEEL,wParam,lParam);
1817
1818 if ((short) HIWORD(wParam) > 0) return SendMessageA(hwnd,WM_KEYDOWN,VK_UP,0);
1819 if ((short) HIWORD(wParam) < 0) return SendMessageA(hwnd,WM_KEYDOWN,VK_DOWN,0);
1820
1821 return TRUE;
1822}
1823
1824static LRESULT COMBO_Enable(HWND hwnd,WPARAM wParam,LPARAM lParam)
1825{
1826 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1827
1828 if( lphc->wState & CBF_EDIT )
1829 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1830 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1831
1832 /* Force the control to repaint when the enabled state changes. */
1833 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
1834
1835 return 0;
1836}
1837
1838static LRESULT COMBO_SetRedraw(HWND hwnd,WPARAM wParam,LPARAM lParam)
1839{
1840 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1841
1842 if( wParam )
1843 lphc->wState &= ~CBF_NOREDRAW;
1844 else
1845 lphc->wState |= CBF_NOREDRAW;
1846
1847 if( lphc->wState & CBF_EDIT )
1848 SendMessageA( lphc->hWndEdit, WM_SETREDRAW, wParam, lParam );
1849 SendMessageA( lphc->hWndLBox, WM_SETREDRAW, wParam, lParam );
1850
1851 return 0;
1852}
1853
1854static LRESULT COMBO_SysKeyDown(HWND hwnd,WPARAM wParam,LPARAM lParam)
1855{
1856 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1857
1858 if( KEYDATA_ALT & HIWORD(lParam) )
1859 if( wParam == VK_UP || wParam == VK_DOWN )
1860 COMBO_FlipListbox( lphc, TRUE );
1861
1862 return DefWindowProcA(hwnd,WM_SYSKEYDOWN,wParam,lParam);
1863}
1864
1865static LRESULT COMBO_HandleKey(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
1866{
1867 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1868
1869 if( lphc->wState & CBF_EDIT )
1870 return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1871 else
1872 return SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1873}
1874
1875/* combobox messages */
1876
1877static LRESULT COMBO_AddString(HWND hwnd,WPARAM wParam,LPARAM lParam)
1878{
1879 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1880
1881 return SendMessageA(lphc->hWndLBox,LB_ADDSTRING,0,lParam);
1882}
1883
1884static LRESULT COMBO_InsertString(HWND hwnd,WPARAM wParam,LPARAM lParam)
1885{
1886 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1887
1888 return SendMessageA(lphc->hWndLBox,LB_INSERTSTRING,wParam,lParam);
1889}
1890
1891static LRESULT COMBO_DeleteString(HWND hwnd,WPARAM wParam,LPARAM lParam)
1892{
1893 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1894
1895 return SendMessageA(lphc->hWndLBox,LB_DELETESTRING,wParam,0);
1896}
1897
1898/***********************************************************************
1899 * COMBO_SelectString
1900 */
1901static LRESULT COMBO_SelectString(HWND hwnd,WPARAM wParam,LPARAM lParam)
1902{
1903 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1904 INT CurSel = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1905 INT index = SendMessageA(lphc->hWndLBox,LB_SELECTSTRING,wParam,lParam);
1906
1907 if( index >= 0 )
1908 {
1909 if( lphc->wState & CBF_EDIT )
1910 {
1911 if (CurSel != index)
1912 CBUpdateEdit( lphc, index );
1913 } else
1914 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1915 }
1916
1917 return (LRESULT)index;
1918}
1919
1920static LRESULT COMBO_FindString(HWND hwnd,WPARAM wParam,LPARAM lParam)
1921{
1922 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1923
1924 return SendMessageA(lphc->hWndLBox,LB_FINDSTRING,wParam,lParam);
1925}
1926
1927static LRESULT COMBO_FindStringExact(HWND hwnd,WPARAM wParam,LPARAM lParam)
1928{
1929 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1930
1931 return SendMessageA(lphc->hWndLBox,LB_FINDSTRINGEXACT,wParam,lParam);
1932}
1933
1934/***********************************************************************
1935 * COMBO_SetItemHeight
1936 */
1937static LRESULT COMBO_SetItemHeight(HWND hwnd,WPARAM wParam,LPARAM lParam)
1938{
1939 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1940 LRESULT lRet = CB_ERR;
1941
1942 if( wParam == -1 ) /* set text field height */
1943 {
1944 if( lParam < 32768 )
1945 {
1946 lphc->editHeight = lParam;
1947
1948 /*
1949 * Redo the layout of the control.
1950 */
1951 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1952 {
1953 CBCalcPlacement(hwnd,
1954 lphc,
1955 &lphc->textRect,
1956 &lphc->buttonRect,
1957 &lphc->droppedRect);
1958
1959 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1960 }
1961 else
1962 {
1963 CBForceDummyResize(lphc);
1964 }
1965
1966 lRet = lParam;
1967 }
1968 }
1969 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1970 lRet = SendMessageA( lphc->hWndLBox, LB_SETITEMHEIGHT,
1971 wParam,lParam);
1972 return lRet;
1973}
1974
1975static LRESULT COMBO_GetItemHeight(HWND hwnd,WPARAM wParam,LPARAM lParam)
1976{
1977 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1978
1979 if( (INT)wParam >= 0 ) /* listbox item */
1980 return SendMessageA( lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1981
1982 return CBGetTextAreaHeight(hwnd, lphc);
1983}
1984
1985static LRESULT COMBO_ResetContent(HWND hwnd,WPARAM wParam,LPARAM lParam)
1986{
1987 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1988
1989 SendMessageA( lphc->hWndLBox, LB_RESETCONTENT, 0, 0 );
1990 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
1991
1992 return TRUE;
1993}
1994
1995static LRESULT COMBO_InitStorage(HWND hwnd,WPARAM wParam,LPARAM lParam)
1996{
1997 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
1998
1999 return SendMessageA( lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2000}
2001
2002static LRESULT COMBO_GetHorizontalExtent(HWND hwnd,WPARAM wParam,LPARAM lParam)
2003{
2004 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2005
2006 return SendMessageA( lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2007}
2008
2009static LRESULT COMBO_SetHorizontalExtent(HWND hwnd,WPARAM wParam,LPARAM lParam)
2010{
2011 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2012
2013 return SendMessageA( lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2014}
2015
2016static LRESULT COMBO_GetTopIndex(HWND hwnd,WPARAM wParam,LPARAM lParam)
2017{
2018 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2019
2020 return SendMessageA( lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2021}
2022
2023static LRESULT COMBO_GetLocale(HWND hwnd,WPARAM wParam,LPARAM lParam)
2024{
2025 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2026
2027 return SendMessageA( lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2028}
2029
2030static LRESULT COMBO_SetLocale(HWND hwnd,WPARAM wParam,LPARAM lParam)
2031{
2032 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2033
2034 return SendMessageA( lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2035}
2036
2037static LRESULT COMBO_GetDroppedWidth(HWND hwnd,WPARAM wParam,LPARAM lParam)
2038{
2039 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2040
2041 if( lphc->droppedWidth )
2042 return lphc->droppedWidth;
2043
2044 return lphc->droppedRect.right - lphc->droppedRect.left;
2045}
2046
2047static LRESULT COMBO_SetDroppedWidth(HWND hwnd,WPARAM wParam,LPARAM lParam)
2048{
2049 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2050
2051 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2052 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2053
2054 return CB_ERR;
2055}
2056
2057static LRESULT COMBO_GetDroppedControlRect(HWND hwnd,WPARAM wParam,LPARAM lParam)
2058{
2059 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2060
2061 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2062
2063 return CB_OKAY;
2064}
2065
2066static LRESULT COMBO_GetDroppedState(HWND hwnd,WPARAM wParam,LPARAM lParam)
2067{
2068 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2069
2070 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2071}
2072
2073static LRESULT COMBO_Dir(HWND hwnd,WPARAM wParam,LPARAM lParam)
2074{
2075 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2076
2077 return COMBO_Directory( lphc, (UINT)wParam,
2078 (LPSTR)lParam,TRUE);
2079}
2080
2081static LRESULT COMBO_ShowDropDown(HWND hwnd,WPARAM wParam,LPARAM lParam)
2082{
2083 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2084
2085 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2086 {
2087 if( wParam )
2088 {
2089 if( !(lphc->wState & CBF_DROPPED) )
2090 CBDropDown( lphc );
2091 }
2092 else
2093 if( lphc->wState & CBF_DROPPED )
2094 CBRollUp( lphc, FALSE, TRUE );
2095 }
2096
2097 return TRUE;
2098}
2099
2100static LRESULT COMBO_GetCount(HWND hwnd,WPARAM wParam,LPARAM lParam)
2101{
2102 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2103
2104 return SendMessageA( lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2105}
2106
2107static LRESULT COMBO_GetCurSel(HWND hwnd,WPARAM wParam,LPARAM lParam)
2108{
2109 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2110
2111 return SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2112}
2113
2114static LRESULT COMBO_SetCurSel(HWND hwnd,WPARAM wParam,LPARAM lParam)
2115{
2116 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2117
2118 lParam = SendMessageA( lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2119 if( lParam >= 0 )
2120 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2121 if( lphc->wState & CBF_SELCHANGE )
2122 {
2123 //SvL: Clear the flag here; doing it after calling CBUpdateEdit causes
2124 // an infinite loop in RealPlayer 7
2125 lphc->wState &= ~CBF_SELCHANGE;
2126
2127 /* no LBN_SELCHANGE in this case, update manually */
2128 if( lphc->wState & CBF_EDIT )
2129 CBUpdateEdit( lphc, (INT)wParam );
2130 else
2131 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
2132 }
2133
2134 return lParam;
2135}
2136
2137static LRESULT COMBO_GetLBText(HWND hwnd,WPARAM wParam,LPARAM lParam)
2138{
2139 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2140
2141 return SendMessageA( lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2142}
2143
2144static LRESULT COMBO_GetLBTextLen(HWND hwnd,WPARAM wParam,LPARAM lParam)
2145{
2146 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2147
2148 return SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2149}
2150
2151static LRESULT COMBO_GetItemData(HWND hwnd,WPARAM wParam,LPARAM lParam)
2152{
2153 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2154
2155 return SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2156}
2157
2158static LRESULT COMBO_SetItemData(HWND hwnd,WPARAM wParam,LPARAM lParam)
2159{
2160 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2161
2162 return SendMessageA( lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2163}
2164
2165static LRESULT COMBO_GetEditSel(HWND hwnd,WPARAM wParam,LPARAM lParam)
2166{
2167 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2168
2169 if( lphc->wState & CBF_EDIT )
2170 {
2171 INT a, b;
2172
2173 return SendMessageA( lphc->hWndEdit, EM_GETSEL,
2174 (wParam) ? wParam : (WPARAM)&a,
2175 (lParam) ? lParam : (LPARAM)&b );
2176 }
2177
2178 return CB_ERR;
2179}
2180
2181static LRESULT COMBO_SetEditSel(HWND hwnd,WPARAM wParam,LPARAM lParam)
2182{
2183 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2184
2185 if( lphc->wState & CBF_EDIT )
2186 return SendMessageA( lphc->hWndEdit, EM_SETSEL,
2187 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2188
2189 return CB_ERR;
2190}
2191
2192static LRESULT COMBO_SetExtendedUI(HWND hwnd,WPARAM wParam,LPARAM lParam)
2193{
2194 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2195
2196 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2197 return CB_ERR;
2198 if( wParam )
2199 lphc->wState |= CBF_EUI;
2200 else lphc->wState &= ~CBF_EUI;
2201 return CB_OKAY;
2202}
2203
2204static LRESULT COMBO_GetExtendedUI(HWND hwnd,WPARAM wParam,LPARAM lParam)
2205{
2206 LPHEADCOMBO lphc = (LPHEADCOMBO)GetInfoPtr(hwnd);
2207
2208 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2209}
2210
2211/***********************************************************************
2212 * ComboWndProc
2213 *
2214 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
2215 */
2216LRESULT WINAPI ComboWndProc( HWND hwnd, UINT message,
2217 WPARAM wParam, LPARAM lParam )
2218{
2219// dprintf(("ComboWndProc hwnd: %04x, msg %s, wp %08x lp %08lx\n",
2220// hwnd, GetMsgText(message), wParam, lParam));
2221
2222 switch(message)
2223 {
2224
2225 /* System messages */
2226
2227 case WM_NCCREATE:
2228 return COMBO_NCCreate(hwnd,wParam,lParam);
2229
2230 case WM_NCDESTROY:
2231 return COMBO_NCDestroy(hwnd,wParam,lParam);
2232
2233 case WM_CREATE:
2234 return COMBO_Create(hwnd,wParam,lParam);
2235
2236 case WM_PRINTCLIENT:
2237 return COMBO_PrintClient(hwnd,wParam,lParam);
2238
2239 case WM_PAINT:
2240 return COMBO_Paint(hwnd,wParam,lParam);
2241
2242 case WM_ERASEBKGND:
2243 return COMBO_EraseBackground(hwnd,wParam,lParam);
2244
2245 case WM_GETDLGCODE:
2246 return COMBO_GetDlgCode(hwnd,wParam,lParam);
2247
2248 case WM_WINDOWPOSCHANGING:
2249 return COMBO_WindowPosChanging(hwnd,wParam,lParam);
2250
2251 case WM_SIZE:
2252 return COMBO_Size(hwnd,wParam,lParam);
2253
2254 case WM_SETFONT:
2255 return COMBO_SetFont(hwnd,wParam,lParam);
2256
2257 case WM_GETFONT:
2258 return COMBO_GetFont(hwnd,wParam,lParam);
2259
2260 case WM_SETFOCUS:
2261 return COMBO_SetFocus(hwnd,wParam,lParam);
2262
2263 case WM_KILLFOCUS:
2264 return COMBO_KillFocus(hwnd,wParam,lParam);
2265
2266 case WM_COMMAND:
2267 return COMBO_Command(hwnd,wParam,lParam);
2268
2269 case WM_GETTEXT:
2270 return COMBO_GetText(hwnd,wParam,lParam);
2271
2272 case WM_SETTEXT:
2273 case WM_GETTEXTLENGTH:
2274 case WM_CLEAR:
2275 case WM_CUT:
2276 case WM_PASTE:
2277 case WM_COPY:
2278 return COMBO_HandleText(hwnd,message,wParam,lParam);
2279
2280 case WM_DRAWITEM:
2281 case WM_DELETEITEM:
2282 case WM_COMPAREITEM:
2283 case WM_MEASUREITEM:
2284 return COMBO_HandleItem(hwnd,message,wParam,lParam);
2285
2286 case WM_ENABLE:
2287 return COMBO_Enable(hwnd,wParam,lParam);
2288
2289 case WM_SETREDRAW:
2290 return COMBO_SetRedraw(hwnd,wParam,lParam);
2291
2292 case WM_SYSKEYDOWN:
2293 return COMBO_SysKeyDown(hwnd,wParam,lParam);
2294
2295 case WM_CHAR:
2296 case WM_KEYDOWN:
2297 return COMBO_HandleKey(hwnd,message,wParam,lParam);
2298
2299 case WM_LBUTTONDOWN:
2300 return COMBO_LButtonDown(hwnd,wParam,lParam);
2301
2302 case WM_LBUTTONUP:
2303 return COMBO_LButtonUp(hwnd,wParam,lParam);
2304
2305 case WM_MOUSEMOVE:
2306 return COMBO_MouseMove(hwnd,wParam,lParam);
2307
2308 case WM_MOUSEWHEEL:
2309 return COMBO_MouseWheel(hwnd,wParam,lParam);
2310
2311 /* Combo messages */
2312
2313 case CB_ADDSTRING:
2314 return COMBO_AddString(hwnd,wParam,lParam);
2315
2316 case CB_INSERTSTRING:
2317 return COMBO_InsertString(hwnd,wParam,lParam);
2318
2319 case CB_DELETESTRING:
2320 return COMBO_DeleteString(hwnd,wParam,lParam);
2321
2322 case CB_SELECTSTRING:
2323 return COMBO_SelectString(hwnd,wParam,lParam);
2324
2325 case CB_FINDSTRING:
2326 return COMBO_FindString(hwnd,wParam,lParam);
2327
2328 case CB_FINDSTRINGEXACT:
2329 return COMBO_FindStringExact(hwnd,wParam,lParam);
2330
2331 case CB_SETITEMHEIGHT:
2332 return COMBO_SetItemHeight(hwnd,wParam,lParam);
2333
2334 case CB_GETITEMHEIGHT:
2335 return COMBO_GetItemHeight(hwnd,wParam,lParam);
2336
2337 case CB_RESETCONTENT:
2338 return COMBO_ResetContent(hwnd,wParam,lParam);
2339
2340 case CB_INITSTORAGE:
2341 return COMBO_InitStorage(hwnd,wParam,lParam);
2342
2343 case CB_GETHORIZONTALEXTENT:
2344 return COMBO_GetHorizontalExtent(hwnd,wParam,lParam);
2345
2346 case CB_SETHORIZONTALEXTENT:
2347 return COMBO_SetHorizontalExtent(hwnd,wParam,lParam);
2348
2349 case CB_GETTOPINDEX:
2350 return COMBO_GetTopIndex(hwnd,wParam,lParam);
2351
2352 case CB_GETLOCALE:
2353 return COMBO_GetLocale(hwnd,wParam,lParam);
2354
2355 case CB_SETLOCALE:
2356 return COMBO_SetLocale(hwnd,wParam,lParam);
2357
2358 case CB_GETDROPPEDWIDTH:
2359 return COMBO_GetDroppedWidth(hwnd,wParam,lParam);
2360
2361 case CB_SETDROPPEDWIDTH:
2362 return COMBO_SetDroppedWidth(hwnd,wParam,lParam);
2363
2364 case CB_GETDROPPEDCONTROLRECT:
2365 return COMBO_GetDroppedControlRect(hwnd,wParam,lParam);
2366
2367 case CB_GETDROPPEDSTATE:
2368 return COMBO_GetDroppedState(hwnd,wParam,lParam);
2369
2370 case CB_DIR:
2371 return COMBO_Dir(hwnd,wParam,lParam);
2372
2373 case CB_SHOWDROPDOWN:
2374 return COMBO_ShowDropDown(hwnd,wParam,lParam);
2375
2376 case CB_GETCOUNT:
2377 return COMBO_GetCount(hwnd,wParam,lParam);
2378
2379 case CB_GETCURSEL:
2380 return COMBO_GetCurSel(hwnd,wParam,lParam);
2381
2382 case CB_SETCURSEL:
2383 return COMBO_SetCurSel(hwnd,wParam,lParam);
2384
2385 case CB_GETLBTEXT:
2386 return COMBO_GetLBText(hwnd,wParam,lParam);
2387
2388 case CB_GETLBTEXTLEN:
2389 return COMBO_GetLBTextLen(hwnd,wParam,lParam);
2390
2391 case CB_GETITEMDATA:
2392 return COMBO_GetItemData(hwnd,wParam,lParam);
2393
2394 case CB_SETITEMDATA:
2395 return COMBO_SetItemData(hwnd,wParam,lParam);
2396
2397 case CB_GETEDITSEL:
2398 return COMBO_GetEditSel(hwnd,wParam,lParam);
2399
2400 case CB_SETEDITSEL:
2401 return COMBO_SetEditSel(hwnd,wParam,lParam);
2402
2403 case CB_SETEXTENDEDUI:
2404 return COMBO_SetExtendedUI(hwnd,wParam,lParam);
2405
2406 case CB_GETEXTENDEDUI:
2407 return COMBO_GetExtendedUI(hwnd,wParam,lParam);
2408
2409 //case (WM_USER + 0x1B):
2410 // WARN("[%04x]: undocumented msg!\n", hwnd );
2411 }
2412 return DefWindowProcA(hwnd, message, wParam, lParam);
2413}
2414
2415BOOL COMBOBOX_Register()
2416{
2417 WNDCLASSA wndClass;
2418
2419//SvL: Don't check this now
2420// if (GlobalFindAtomA(COMBOBOXCLASSNAME)) return FALSE;
2421
2422 ZeroMemory(&wndClass,sizeof(WNDCLASSA));
2423 wndClass.style = CS_GLOBALCLASS | CS_PARENTDC;
2424 wndClass.lpfnWndProc = (WNDPROC)ComboWndProc;
2425 wndClass.cbClsExtra = 0;
2426 wndClass.cbWndExtra = sizeof(VOID*);
2427 wndClass.hCursor = LoadCursorA(0,IDC_ARROWA);
2428 wndClass.hbrBackground = (HBRUSH)0;
2429 wndClass.lpszClassName = COMBOBOXCLASSNAME;
2430
2431 return RegisterClassA(&wndClass);
2432}
2433
2434BOOL COMBOBOX_Unregister()
2435{
2436 if (GlobalFindAtomA(COMBOBOXCLASSNAME))
2437 return UnregisterClassA(COMBOBOXCLASSNAME,(HINSTANCE)NULL);
2438 else return FALSE;
2439}
2440
Note: See TracBrowser for help on using the repository browser.