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

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

fixed listbox scrolling, first work on combo code

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