source: trunk/src/user32/new/combo.cpp@ 384

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

combobox, listbox ported

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