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