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

Last change on this file since 2781 was 2781, checked in by cbratschi, 26 years ago

merged combobox with core 20000212

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