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

Last change on this file since 3153 was 3153, checked in by cbratschi, 25 years ago

merged with Corel 20000317, small icon

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