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

Last change on this file since 1433 was 1433, checked in by sandervl, 26 years ago

class changes

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