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

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

Moved new user32 here

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