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

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

resource and other fixes

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