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