Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/dlls/comctl32/comboex.c
4389 views
1
/*
2
* ComboBoxEx control
3
*
4
* Copyright 1998, 1999 Eric Kohl
5
* Copyright 2000, 2001, 2002 Guy Albertelli <[email protected]>
6
* Copyright 2002 Dimitrie O. Paun
7
*
8
* This library is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU Lesser General Public
10
* License as published by the Free Software Foundation; either
11
* version 2.1 of the License, or (at your option) any later version.
12
*
13
* This library is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
* Lesser General Public License for more details.
17
*
18
* You should have received a copy of the GNU Lesser General Public
19
* License along with this library; if not, write to the Free Software
20
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21
*/
22
23
#include <stdarg.h>
24
#include <string.h>
25
#include "windef.h"
26
#include "winbase.h"
27
#include "wingdi.h"
28
#include "winuser.h"
29
#include "winnls.h"
30
#include "commctrl.h"
31
#include "comctl32.h"
32
#include "wine/debug.h"
33
34
WINE_DEFAULT_DEBUG_CHANNEL(comboex);
35
36
/* Item structure */
37
typedef struct _CBE_ITEMDATA
38
{
39
struct _CBE_ITEMDATA *next;
40
UINT mask;
41
LPWSTR pszText;
42
LPWSTR pszTemp;
43
int cchTextMax;
44
int iImage;
45
int iSelectedImage;
46
int iOverlay;
47
int iIndent;
48
LPARAM lParam;
49
} CBE_ITEMDATA;
50
51
/* ComboBoxEx structure */
52
typedef struct
53
{
54
HIMAGELIST himl;
55
HWND hwndSelf; /* my own hwnd */
56
HWND hwndNotify; /* my parent hwnd */
57
HWND hwndCombo;
58
HWND hwndEdit;
59
DWORD dwExtStyle;
60
INT selected; /* index of selected item */
61
DWORD flags; /* WINE internal flags */
62
HFONT defaultFont;
63
HFONT font;
64
INT nb_items; /* Number of items */
65
BOOL unicode; /* TRUE if this window is Unicode */
66
BOOL NtfUnicode; /* TRUE if parent wants notify in Unicode */
67
CBE_ITEMDATA edit; /* item data for edit item */
68
CBE_ITEMDATA *items; /* Array of items */
69
} COMBOEX_INFO;
70
71
/* internal flags in the COMBOEX_INFO structure */
72
#define WCBE_ACTEDIT 0x00000001 /* Edit active i.e.
73
* CBEN_BEGINEDIT issued
74
* but CBEN_ENDEDIT{A|W}
75
* not yet issued. */
76
#define WCBE_EDITCHG 0x00000002 /* Edit issued EN_CHANGE */
77
#define WCBE_EDITHASCHANGED (WCBE_ACTEDIT | WCBE_EDITCHG)
78
#define WCBE_EDITFOCUSED 0x00000004 /* Edit control has focus */
79
#define WCBE_MOUSECAPTURED 0x00000008 /* Combo has captured mouse */
80
#define WCBE_MOUSEDRAGGED 0x00000010 /* User has dragged in combo */
81
82
#define ID_CB_EDIT 1001
83
84
85
/*
86
* Special flag set in DRAWITEMSTRUCT itemState field. It is set by
87
* the ComboEx version of the Combo Window Proc so that when the
88
* WM_DRAWITEM message is then passed to ComboEx, we know that this
89
* particular WM_DRAWITEM message is for listbox only items. Any message
90
* without this flag is then for the Edit control field.
91
*
92
* We really cannot use the ODS_COMBOBOXEDIT flag because MSDN states that
93
* only version 4.0 applications will have ODS_COMBOBOXEDIT set.
94
*/
95
#define ODS_COMBOEXLBOX 0x4000
96
97
98
99
/* Height in pixels of control over the amount of the selected font */
100
#define CBE_EXTRA 3
101
102
/* Indent amount per MS documentation */
103
#define CBE_INDENT 10
104
105
/* Offset in pixels from left side for start of image or text */
106
#define CBE_STARTOFFSET 6
107
108
/* Offset between image and text */
109
#define CBE_SEP 4
110
111
#define COMBO_SUBCLASSID 1
112
#define EDIT_SUBCLASSID 2
113
114
#define COMBOEX_GetInfoPtr(hwnd) ((COMBOEX_INFO *)GetWindowLongPtrW (hwnd, 0))
115
116
static LRESULT CALLBACK COMBOEX_EditWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
117
UINT_PTR uId, DWORD_PTR ref_data);
118
static LRESULT CALLBACK COMBOEX_ComboWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
119
UINT_PTR uId, DWORD_PTR ref_data);
120
static LRESULT COMBOEX_Destroy (COMBOEX_INFO *infoPtr);
121
typedef INT (WINAPI *cmp_func_t)(LPCWSTR, LPCWSTR);
122
123
static inline BOOL is_textW(LPCWSTR str)
124
{
125
return str && str != LPSTR_TEXTCALLBACKW;
126
}
127
128
static inline BOOL is_textA(LPCSTR str)
129
{
130
return str && str != LPSTR_TEXTCALLBACKA;
131
}
132
133
static inline LPCSTR debugstr_txt(LPCWSTR str)
134
{
135
if (str == LPSTR_TEXTCALLBACKW) return "(callback)";
136
return debugstr_w(str);
137
}
138
139
static void COMBOEX_DumpItem (CBE_ITEMDATA const *item)
140
{
141
TRACE("item %p - mask=%08x, pszText=%p, cchTM=%d, iImage=%d\n",
142
item, item->mask, item->pszText, item->cchTextMax, item->iImage);
143
TRACE("item %p - iSelectedImage=%d, iOverlay=%d, iIndent=%d, lParam=%Ix\n",
144
item, item->iSelectedImage, item->iOverlay, item->iIndent, item->lParam);
145
if (item->mask & CBEIF_TEXT)
146
TRACE("item %p - pszText=%s\n", item, debugstr_txt(item->pszText));
147
}
148
149
150
static void COMBOEX_DumpInput (COMBOBOXEXITEMW const *input)
151
{
152
TRACE("input - mask=%08x, iItem=%Id, pszText=%p, cchTM=%d, iImage=%d\n",
153
input->mask, input->iItem, input->pszText, input->cchTextMax,
154
input->iImage);
155
if (input->mask & CBEIF_TEXT)
156
TRACE("input - pszText=<%s>\n", debugstr_txt(input->pszText));
157
TRACE("input - iSelectedImage=%d, iOverlay=%d, iIndent=%d, lParam=%Ix\n",
158
input->iSelectedImage, input->iOverlay, input->iIndent, input->lParam);
159
}
160
161
162
static inline CBE_ITEMDATA *get_item_data(const COMBOEX_INFO *infoPtr, INT index)
163
{
164
return (CBE_ITEMDATA *)SendMessageW (infoPtr->hwndCombo, CB_GETITEMDATA,
165
index, 0);
166
}
167
168
static inline cmp_func_t get_cmp_func(COMBOEX_INFO const *infoPtr)
169
{
170
return infoPtr->dwExtStyle & CBES_EX_CASESENSITIVE ? lstrcmpW : lstrcmpiW;
171
}
172
173
static INT COMBOEX_Notify (const COMBOEX_INFO *infoPtr, INT code, NMHDR *hdr)
174
{
175
hdr->idFrom = GetDlgCtrlID (infoPtr->hwndSelf);
176
hdr->hwndFrom = infoPtr->hwndSelf;
177
hdr->code = code;
178
if (infoPtr->NtfUnicode)
179
return SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, 0, (LPARAM)hdr);
180
else
181
return SendMessageA (infoPtr->hwndNotify, WM_NOTIFY, 0, (LPARAM)hdr);
182
}
183
184
185
static INT
186
COMBOEX_NotifyItem (const COMBOEX_INFO *infoPtr, UINT code, NMCOMBOBOXEXW *hdr)
187
{
188
/* Change the Text item from Unicode to ANSI if necessary for NOTIFY */
189
if (infoPtr->NtfUnicode)
190
return COMBOEX_Notify (infoPtr, code, &hdr->hdr);
191
else {
192
LPWSTR wstr = hdr->ceItem.pszText;
193
LPSTR astr = 0;
194
INT ret, len = 0;
195
196
if ((hdr->ceItem.mask & CBEIF_TEXT) && is_textW(wstr)) {
197
len = WideCharToMultiByte (CP_ACP, 0, wstr, -1, 0, 0, NULL, NULL);
198
if (len > 0) {
199
astr = Alloc ((len + 1)*sizeof(CHAR));
200
if (!astr) return 0;
201
WideCharToMultiByte (CP_ACP, 0, wstr, -1, astr, len, 0, 0);
202
hdr->ceItem.pszText = (LPWSTR)astr;
203
}
204
}
205
206
if (code == CBEN_ENDEDITW) code = CBEN_ENDEDITA;
207
else if (code == CBEN_GETDISPINFOW) code = CBEN_GETDISPINFOA;
208
else if (code == CBEN_DRAGBEGINW) code = CBEN_DRAGBEGINA;
209
210
ret = COMBOEX_Notify (infoPtr, code, (NMHDR *)hdr);
211
212
if (astr && hdr->ceItem.pszText == (LPWSTR)astr)
213
hdr->ceItem.pszText = wstr;
214
215
Free(astr);
216
217
return ret;
218
}
219
}
220
221
222
static INT COMBOEX_NotifyEndEdit (const COMBOEX_INFO *infoPtr, NMCBEENDEDITW *neew, LPCWSTR wstr)
223
{
224
/* Change the Text item from Unicode to ANSI if necessary for NOTIFY */
225
if (infoPtr->NtfUnicode) {
226
lstrcpynW(neew->szText, wstr, CBEMAXSTRLEN);
227
return COMBOEX_Notify (infoPtr, CBEN_ENDEDITW, &neew->hdr);
228
} else {
229
NMCBEENDEDITA neea;
230
231
neea.hdr = neew->hdr;
232
neea.fChanged = neew->fChanged;
233
neea.iNewSelection = neew->iNewSelection;
234
WideCharToMultiByte (CP_ACP, 0, wstr, -1, neea.szText, CBEMAXSTRLEN, 0, 0);
235
neea.iWhy = neew->iWhy;
236
237
return COMBOEX_Notify (infoPtr, CBEN_ENDEDITA, &neea.hdr);
238
}
239
}
240
241
242
static void COMBOEX_NotifyDragBegin(const COMBOEX_INFO *infoPtr, LPCWSTR wstr)
243
{
244
/* Change the Text item from Unicode to ANSI if necessary for NOTIFY */
245
if (infoPtr->NtfUnicode) {
246
NMCBEDRAGBEGINW ndbw;
247
248
ndbw.iItemid = -1;
249
lstrcpynW(ndbw.szText, wstr, CBEMAXSTRLEN);
250
COMBOEX_Notify (infoPtr, CBEN_DRAGBEGINW, &ndbw.hdr);
251
} else {
252
NMCBEDRAGBEGINA ndba;
253
254
ndba.iItemid = -1;
255
WideCharToMultiByte (CP_ACP, 0, wstr, -1, ndba.szText, CBEMAXSTRLEN, 0, 0);
256
257
COMBOEX_Notify (infoPtr, CBEN_DRAGBEGINA, &ndba.hdr);
258
}
259
}
260
261
262
static void COMBOEX_FreeText (CBE_ITEMDATA *item)
263
{
264
if (is_textW(item->pszText)) Free(item->pszText);
265
item->pszText = NULL;
266
Free(item->pszTemp);
267
item->pszTemp = NULL;
268
}
269
270
271
static INT COMBOEX_GetIndex(COMBOEX_INFO const *infoPtr, CBE_ITEMDATA const *item)
272
{
273
CBE_ITEMDATA const *moving;
274
INT index;
275
276
moving = infoPtr->items;
277
index = infoPtr->nb_items - 1;
278
279
while (moving && (moving != item)) {
280
moving = moving->next;
281
index--;
282
}
283
if (!moving || (index < 0)) {
284
ERR("COMBOBOXEX item structures broken. Please report!\n");
285
return -1;
286
}
287
return index;
288
}
289
290
291
static LPCWSTR COMBOEX_GetText(const COMBOEX_INFO *infoPtr, CBE_ITEMDATA *item)
292
{
293
NMCOMBOBOXEXW nmce;
294
LPWSTR text, buf;
295
INT len;
296
297
if (item->pszText != LPSTR_TEXTCALLBACKW)
298
return item->pszText;
299
300
ZeroMemory(&nmce, sizeof(nmce));
301
nmce.ceItem.mask = CBEIF_TEXT;
302
nmce.ceItem.lParam = item->lParam;
303
nmce.ceItem.iItem = COMBOEX_GetIndex(infoPtr, item);
304
COMBOEX_NotifyItem(infoPtr, CBEN_GETDISPINFOW, &nmce);
305
306
if (is_textW(nmce.ceItem.pszText)) {
307
len = MultiByteToWideChar (CP_ACP, 0, (LPSTR)nmce.ceItem.pszText, -1, NULL, 0);
308
buf = Alloc ((len + 1)*sizeof(WCHAR));
309
if (buf)
310
MultiByteToWideChar (CP_ACP, 0, (LPSTR)nmce.ceItem.pszText, -1, buf, len);
311
if (nmce.ceItem.mask & CBEIF_DI_SETITEM) {
312
COMBOEX_FreeText(item);
313
item->pszText = buf;
314
} else {
315
Free(item->pszTemp);
316
item->pszTemp = buf;
317
}
318
text = buf;
319
} else
320
text = nmce.ceItem.pszText;
321
322
if (nmce.ceItem.mask & CBEIF_DI_SETITEM)
323
item->pszText = text;
324
return text;
325
}
326
327
328
static void COMBOEX_GetComboFontSize (const COMBOEX_INFO *infoPtr, SIZE *size)
329
{
330
HFONT nfont, ofont;
331
HDC mydc;
332
333
mydc = GetDC (0); /* why the entire screen???? */
334
nfont = (HFONT)SendMessageW (infoPtr->hwndCombo, WM_GETFONT, 0, 0);
335
ofont = SelectObject (mydc, nfont);
336
GetTextExtentPointW (mydc, L"A", 1, size);
337
SelectObject (mydc, ofont);
338
ReleaseDC (0, mydc);
339
TRACE("selected font hwnd %p, height %ld\n", nfont, size->cy);
340
}
341
342
343
static void COMBOEX_CopyItem (const CBE_ITEMDATA *item, COMBOBOXEXITEMW *cit)
344
{
345
if (cit->mask & CBEIF_TEXT) {
346
/*
347
* when given a text buffer actually use that buffer
348
*/
349
if (cit->pszText) {
350
if (is_textW(item->pszText))
351
lstrcpynW(cit->pszText, item->pszText, cit->cchTextMax);
352
else
353
cit->pszText[0] = 0;
354
} else {
355
cit->pszText = item->pszText;
356
cit->cchTextMax = item->cchTextMax;
357
}
358
}
359
if (cit->mask & CBEIF_IMAGE)
360
cit->iImage = item->iImage;
361
if (cit->mask & CBEIF_SELECTEDIMAGE)
362
cit->iSelectedImage = item->iSelectedImage;
363
if (cit->mask & CBEIF_OVERLAY)
364
cit->iOverlay = item->iOverlay;
365
if (cit->mask & CBEIF_INDENT)
366
cit->iIndent = item->iIndent;
367
if (cit->mask & CBEIF_LPARAM)
368
cit->lParam = item->lParam;
369
}
370
371
372
static void COMBOEX_AdjustEditPos (const COMBOEX_INFO *infoPtr)
373
{
374
SIZE mysize;
375
INT x, y, w, h, xioff;
376
RECT rect;
377
378
if (!infoPtr->hwndEdit) return;
379
380
if (infoPtr->himl && !(infoPtr->dwExtStyle & CBES_EX_NOEDITIMAGEINDENT)) {
381
IMAGEINFO iinfo;
382
iinfo.rcImage.left = iinfo.rcImage.right = 0;
383
ImageList_GetImageInfo(infoPtr->himl, 0, &iinfo);
384
xioff = iinfo.rcImage.right - iinfo.rcImage.left + CBE_SEP;
385
} else xioff = 0;
386
387
GetClientRect (infoPtr->hwndCombo, &rect);
388
InflateRect (&rect, -2, -2);
389
InvalidateRect (infoPtr->hwndCombo, &rect, TRUE);
390
391
/* reposition the Edit control based on whether icon exists */
392
COMBOEX_GetComboFontSize (infoPtr, &mysize);
393
TRACE("Combo font x %ld, y %ld\n", mysize.cx, mysize.cy);
394
x = xioff + CBE_STARTOFFSET + 1;
395
w = rect.right-rect.left - x - GetSystemMetrics(SM_CXVSCROLL) - 1;
396
h = mysize.cy + 1;
397
y = rect.bottom - h - 1;
398
399
TRACE("Combo client (%s), setting Edit to (%d,%d)-(%d,%d)\n",
400
wine_dbgstr_rect(&rect), x, y, x + w, y + h);
401
SetWindowPos(infoPtr->hwndEdit, HWND_TOP, x, y, w, h,
402
SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOZORDER);
403
}
404
405
406
static void COMBOEX_ReSize (const COMBOEX_INFO *infoPtr)
407
{
408
SIZE mysize;
409
LONG cy;
410
IMAGEINFO iinfo;
411
412
COMBOEX_GetComboFontSize (infoPtr, &mysize);
413
cy = mysize.cy + CBE_EXTRA;
414
if (infoPtr->himl && ImageList_GetImageInfo(infoPtr->himl, 0, &iinfo)) {
415
cy = max (iinfo.rcImage.bottom - iinfo.rcImage.top, cy);
416
TRACE("upgraded height due to image: height %ld\n", cy);
417
}
418
SendMessageW (infoPtr->hwndSelf, CB_SETITEMHEIGHT, -1, cy);
419
if (infoPtr->hwndCombo) {
420
SendMessageW (infoPtr->hwndCombo, CB_SETITEMHEIGHT, 0, cy);
421
if ( !(infoPtr->flags & CBES_EX_NOSIZELIMIT)) {
422
RECT comboRect, ourRect;
423
GetWindowRect(infoPtr->hwndCombo, &comboRect);
424
GetWindowRect(infoPtr->hwndSelf, &ourRect);
425
if (comboRect.bottom > ourRect.bottom)
426
SetWindowPos( infoPtr->hwndSelf, 0, 0, 0, ourRect.right - ourRect.left,
427
comboRect.bottom - comboRect.top,
428
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW );
429
}
430
}
431
}
432
433
434
static void COMBOEX_SetEditText (const COMBOEX_INFO *infoPtr, CBE_ITEMDATA *item)
435
{
436
if (!infoPtr->hwndEdit) return;
437
438
if (item->mask & CBEIF_TEXT) {
439
SendMessageW (infoPtr->hwndEdit, WM_SETTEXT, 0, (LPARAM)COMBOEX_GetText(infoPtr, item));
440
SendMessageW (infoPtr->hwndEdit, EM_SETSEL, 0, 0);
441
SendMessageW (infoPtr->hwndEdit, EM_SETSEL, 0, -1);
442
}
443
}
444
445
446
static CBE_ITEMDATA *COMBOEX_FindItem(COMBOEX_INFO *infoPtr, INT_PTR index)
447
{
448
CBE_ITEMDATA *item;
449
INT i;
450
451
if ((index >= infoPtr->nb_items) || (index < -1))
452
return NULL;
453
if (index == -1)
454
return &infoPtr->edit;
455
item = infoPtr->items;
456
i = infoPtr->nb_items - 1;
457
458
/* find the item in the list */
459
while (item && (i > index)) {
460
item = item->next;
461
i--;
462
}
463
if (!item || (i != index)) {
464
ERR("COMBOBOXEX item structures broken. Please report!\n");
465
return 0;
466
}
467
return item;
468
}
469
470
/* *** CBEM_xxx message support *** */
471
472
static UINT COMBOEX_GetListboxText(COMBOEX_INFO *infoPtr, INT_PTR n, LPWSTR buf)
473
{
474
CBE_ITEMDATA *item;
475
LPCWSTR str;
476
477
item = COMBOEX_FindItem(infoPtr, n);
478
if (!item)
479
return 0;
480
481
str = COMBOEX_GetText(infoPtr, item);
482
if (!str)
483
{
484
if (buf)
485
{
486
if (infoPtr->unicode)
487
buf[0] = 0;
488
else
489
*((LPSTR)buf) = 0;
490
}
491
return 0;
492
}
493
494
if (infoPtr->unicode)
495
{
496
if (buf)
497
lstrcpyW(buf, str);
498
return lstrlenW(str);
499
}
500
else
501
{
502
UINT r;
503
r = WideCharToMultiByte(CP_ACP, 0, str, -1, (LPSTR)buf, 0x40000000, NULL, NULL);
504
if (r) r--;
505
return r;
506
}
507
}
508
509
510
static INT COMBOEX_DeleteItem (COMBOEX_INFO *infoPtr, INT_PTR index)
511
{
512
TRACE("index %Id\n", index);
513
514
/* if item number requested does not exist then return failure */
515
if ((index >= infoPtr->nb_items) || (index < 0)) return CB_ERR;
516
if (!COMBOEX_FindItem(infoPtr, index)) return CB_ERR;
517
518
/* doing this will result in WM_DELETEITEM being issued */
519
SendMessageW (infoPtr->hwndCombo, CB_DELETESTRING, index, 0);
520
521
return infoPtr->nb_items;
522
}
523
524
525
static BOOL COMBOEX_GetItemW (COMBOEX_INFO *infoPtr, COMBOBOXEXITEMW *cit)
526
{
527
INT_PTR index = cit->iItem;
528
CBE_ITEMDATA *item;
529
530
TRACE("\n");
531
532
/* if item number requested does not exist then return failure */
533
if ((index >= infoPtr->nb_items) || (index < -1)) return FALSE;
534
535
/* if the item is the edit control and there is no edit control, skip */
536
if ((index == -1) && !infoPtr->hwndEdit) return FALSE;
537
538
if (!(item = COMBOEX_FindItem(infoPtr, index))) return FALSE;
539
540
COMBOEX_CopyItem (item, cit);
541
542
return TRUE;
543
}
544
545
546
static BOOL COMBOEX_GetItemA (COMBOEX_INFO *infoPtr, COMBOBOXEXITEMA *cit)
547
{
548
COMBOBOXEXITEMW tmpcit;
549
550
TRACE("\n");
551
552
tmpcit.mask = cit->mask;
553
tmpcit.iItem = cit->iItem;
554
tmpcit.pszText = 0;
555
if(!COMBOEX_GetItemW (infoPtr, &tmpcit)) return FALSE;
556
557
if (cit->mask & CBEIF_TEXT)
558
{
559
if (is_textW(tmpcit.pszText) && cit->pszText)
560
WideCharToMultiByte(CP_ACP, 0, tmpcit.pszText, -1,
561
cit->pszText, cit->cchTextMax, NULL, NULL);
562
else if (cit->pszText) cit->pszText[0] = 0;
563
else cit->pszText = (LPSTR)tmpcit.pszText;
564
}
565
566
if (cit->mask & CBEIF_IMAGE)
567
cit->iImage = tmpcit.iImage;
568
if (cit->mask & CBEIF_SELECTEDIMAGE)
569
cit->iSelectedImage = tmpcit.iSelectedImage;
570
if (cit->mask & CBEIF_OVERLAY)
571
cit->iOverlay = tmpcit.iOverlay;
572
if (cit->mask & CBEIF_INDENT)
573
cit->iIndent = tmpcit.iIndent;
574
if (cit->mask & CBEIF_LPARAM)
575
cit->lParam = tmpcit.lParam;
576
577
return TRUE;
578
}
579
580
581
static inline BOOL COMBOEX_HasEditChanged (COMBOEX_INFO const *infoPtr)
582
{
583
return infoPtr->hwndEdit && (infoPtr->flags & WCBE_EDITHASCHANGED) == WCBE_EDITHASCHANGED;
584
}
585
586
587
static INT COMBOEX_InsertItemW (COMBOEX_INFO *infoPtr, COMBOBOXEXITEMW const *cit)
588
{
589
INT_PTR index;
590
CBE_ITEMDATA *item;
591
NMCOMBOBOXEXW nmcit;
592
593
TRACE("\n");
594
595
if (TRACE_ON(comboex)) COMBOEX_DumpInput (cit);
596
597
/* get real index of item to insert */
598
index = cit->iItem;
599
if (index == -1) index = infoPtr->nb_items;
600
if (index > infoPtr->nb_items) return -1;
601
602
/* get zero-filled space and chain it in */
603
if(!(item = Alloc (sizeof(*item)))) return -1;
604
605
/* locate position to insert new item in */
606
if (index == infoPtr->nb_items) {
607
/* fast path for iItem = -1 */
608
item->next = infoPtr->items;
609
infoPtr->items = item;
610
}
611
else {
612
INT i = infoPtr->nb_items-1;
613
CBE_ITEMDATA *moving = infoPtr->items;
614
615
while ((i > index) && moving) {
616
moving = moving->next;
617
i--;
618
}
619
if (!moving) {
620
ERR("COMBOBOXEX item structures broken. Please report!\n");
621
Free(item);
622
return -1;
623
}
624
item->next = moving->next;
625
moving->next = item;
626
}
627
628
/* fill in our hidden item structure */
629
item->mask = cit->mask;
630
if (item->mask & CBEIF_TEXT) {
631
INT len = 0;
632
633
if (is_textW(cit->pszText)) len = lstrlenW (cit->pszText);
634
if (len > 0) {
635
item->pszText = Alloc ((len + 1)*sizeof(WCHAR));
636
if (!item->pszText) {
637
Free(item);
638
return -1;
639
}
640
lstrcpyW (item->pszText, cit->pszText);
641
}
642
else if (cit->pszText == LPSTR_TEXTCALLBACKW)
643
item->pszText = LPSTR_TEXTCALLBACKW;
644
item->cchTextMax = cit->cchTextMax;
645
}
646
if (item->mask & CBEIF_IMAGE)
647
item->iImage = cit->iImage;
648
if (item->mask & CBEIF_SELECTEDIMAGE)
649
item->iSelectedImage = cit->iSelectedImage;
650
if (item->mask & CBEIF_OVERLAY)
651
item->iOverlay = cit->iOverlay;
652
if (item->mask & CBEIF_INDENT)
653
item->iIndent = cit->iIndent;
654
if (item->mask & CBEIF_LPARAM)
655
item->lParam = cit->lParam;
656
infoPtr->nb_items++;
657
658
if (TRACE_ON(comboex)) COMBOEX_DumpItem (item);
659
660
SendMessageW (infoPtr->hwndCombo, CB_INSERTSTRING, cit->iItem, (LPARAM)item);
661
662
memset (&nmcit.ceItem, 0, sizeof(nmcit.ceItem));
663
nmcit.ceItem.mask=~0;
664
COMBOEX_CopyItem (item, &nmcit.ceItem);
665
COMBOEX_NotifyItem (infoPtr, CBEN_INSERTITEM, &nmcit);
666
667
return index;
668
669
}
670
671
672
static INT COMBOEX_InsertItemA (COMBOEX_INFO *infoPtr, COMBOBOXEXITEMA const *cit)
673
{
674
COMBOBOXEXITEMW citW;
675
LPWSTR wstr = NULL;
676
INT ret;
677
678
memcpy(&citW,cit,sizeof(COMBOBOXEXITEMA));
679
if (cit->mask & CBEIF_TEXT && is_textA(cit->pszText)) {
680
INT len = MultiByteToWideChar (CP_ACP, 0, cit->pszText, -1, NULL, 0);
681
wstr = Alloc ((len + 1)*sizeof(WCHAR));
682
if (!wstr) return -1;
683
MultiByteToWideChar (CP_ACP, 0, cit->pszText, -1, wstr, len);
684
citW.pszText = wstr;
685
}
686
ret = COMBOEX_InsertItemW(infoPtr, &citW);
687
688
Free(wstr);
689
690
return ret;
691
}
692
693
694
static DWORD
695
COMBOEX_SetExtendedStyle (COMBOEX_INFO *infoPtr, DWORD mask, DWORD style)
696
{
697
DWORD dwTemp;
698
699
TRACE("mask %#lx, style %#lx\n", mask, style);
700
701
dwTemp = infoPtr->dwExtStyle;
702
703
if (mask)
704
infoPtr->dwExtStyle = (infoPtr->dwExtStyle & ~mask) | style;
705
else
706
infoPtr->dwExtStyle = style;
707
708
/* see if we need to change the word break proc on the edit */
709
if ((infoPtr->dwExtStyle ^ dwTemp) & CBES_EX_PATHWORDBREAKPROC)
710
SetPathWordBreakProc(infoPtr->hwndEdit,
711
(infoPtr->dwExtStyle & CBES_EX_PATHWORDBREAKPROC) != 0);
712
713
/* test if the control's appearance has changed */
714
mask = CBES_EX_NOEDITIMAGE | CBES_EX_NOEDITIMAGEINDENT;
715
if ((infoPtr->dwExtStyle & mask) != (dwTemp & mask)) {
716
/* if state of EX_NOEDITIMAGE changes, invalidate all */
717
TRACE("EX_NOEDITIMAGE state changed to %#lx\n", infoPtr->dwExtStyle & CBES_EX_NOEDITIMAGE);
718
InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
719
COMBOEX_AdjustEditPos (infoPtr);
720
if (infoPtr->hwndEdit)
721
InvalidateRect (infoPtr->hwndEdit, NULL, TRUE);
722
}
723
724
return dwTemp;
725
}
726
727
728
static HIMAGELIST COMBOEX_SetImageList (COMBOEX_INFO *infoPtr, HIMAGELIST himl)
729
{
730
HIMAGELIST himlTemp = infoPtr->himl;
731
732
TRACE("\n");
733
734
infoPtr->himl = himl;
735
736
COMBOEX_ReSize (infoPtr);
737
InvalidateRect (infoPtr->hwndCombo, NULL, TRUE);
738
739
/* reposition the Edit control based on whether icon exists */
740
COMBOEX_AdjustEditPos (infoPtr);
741
return himlTemp;
742
}
743
744
static BOOL COMBOEX_SetItemW (COMBOEX_INFO *infoPtr, const COMBOBOXEXITEMW *cit)
745
{
746
INT_PTR index = cit->iItem;
747
CBE_ITEMDATA *item;
748
749
if (TRACE_ON(comboex)) COMBOEX_DumpInput (cit);
750
751
/* if item number requested does not exist then return failure */
752
if ((index >= infoPtr->nb_items) || (index < -1)) return FALSE;
753
754
/* if the item is the edit control and there is no edit control, skip */
755
if ((index == -1) && !infoPtr->hwndEdit) return FALSE;
756
757
if (!(item = COMBOEX_FindItem(infoPtr, index))) return FALSE;
758
759
/* add/change stuff to the internal item structure */
760
item->mask |= cit->mask;
761
if (cit->mask & CBEIF_TEXT) {
762
INT len = 0;
763
764
COMBOEX_FreeText(item);
765
if (is_textW(cit->pszText)) len = lstrlenW(cit->pszText);
766
if (len > 0) {
767
item->pszText = Alloc ((len + 1)*sizeof(WCHAR));
768
if (!item->pszText) return FALSE;
769
lstrcpyW(item->pszText, cit->pszText);
770
} else if (cit->pszText == LPSTR_TEXTCALLBACKW)
771
item->pszText = LPSTR_TEXTCALLBACKW;
772
item->cchTextMax = cit->cchTextMax;
773
}
774
if (cit->mask & CBEIF_IMAGE)
775
item->iImage = cit->iImage;
776
if (cit->mask & CBEIF_SELECTEDIMAGE)
777
item->iSelectedImage = cit->iSelectedImage;
778
if (cit->mask & CBEIF_OVERLAY)
779
item->iOverlay = cit->iOverlay;
780
if (cit->mask & CBEIF_INDENT)
781
item->iIndent = cit->iIndent;
782
if (cit->mask & CBEIF_LPARAM)
783
item->lParam = cit->lParam;
784
785
if (TRACE_ON(comboex)) COMBOEX_DumpItem (item);
786
787
/* if original request was to update edit control, do some fast foot work */
788
if (cit->iItem == -1 && cit->mask & CBEIF_TEXT) {
789
COMBOEX_SetEditText (infoPtr, item);
790
RedrawWindow (infoPtr->hwndCombo, 0, 0, RDW_ERASE | RDW_INVALIDATE);
791
}
792
return TRUE;
793
}
794
795
static BOOL COMBOEX_SetItemA (COMBOEX_INFO *infoPtr, COMBOBOXEXITEMA const *cit)
796
{
797
COMBOBOXEXITEMW citW;
798
LPWSTR wstr = NULL;
799
BOOL ret;
800
801
memcpy(&citW, cit, sizeof(COMBOBOXEXITEMA));
802
if ((cit->mask & CBEIF_TEXT) && is_textA(cit->pszText)) {
803
INT len = MultiByteToWideChar (CP_ACP, 0, cit->pszText, -1, NULL, 0);
804
wstr = Alloc ((len + 1)*sizeof(WCHAR));
805
if (!wstr) return FALSE;
806
MultiByteToWideChar (CP_ACP, 0, cit->pszText, -1, wstr, len);
807
citW.pszText = wstr;
808
}
809
ret = COMBOEX_SetItemW(infoPtr, &citW);
810
811
Free(wstr);
812
813
return ret;
814
}
815
816
817
static BOOL COMBOEX_SetUnicodeFormat (COMBOEX_INFO *infoPtr, BOOL value)
818
{
819
BOOL bTemp = infoPtr->unicode;
820
821
TRACE("to %s, was %s\n", value ? "TRUE":"FALSE", bTemp ? "TRUE":"FALSE");
822
823
infoPtr->unicode = value;
824
825
return bTemp;
826
}
827
828
829
/* *** CB_xxx message support *** */
830
831
static INT
832
COMBOEX_FindStringExact (const COMBOEX_INFO *infoPtr, INT start, LPCWSTR str)
833
{
834
INT i;
835
cmp_func_t cmptext = get_cmp_func(infoPtr);
836
INT count = SendMessageW (infoPtr->hwndCombo, CB_GETCOUNT, 0, 0);
837
838
/* now search from after starting loc and wrapping back to start */
839
for(i=start+1; i<count; i++) {
840
CBE_ITEMDATA *item = get_item_data(infoPtr, i);
841
if ((LRESULT)item == CB_ERR) continue;
842
if (cmptext(COMBOEX_GetText(infoPtr, item), str) == 0) return i;
843
}
844
for(i=0; i<=start; i++) {
845
CBE_ITEMDATA *item = get_item_data(infoPtr, i);
846
if ((LRESULT)item == CB_ERR) continue;
847
if (cmptext(COMBOEX_GetText(infoPtr, item), str) == 0) return i;
848
}
849
return CB_ERR;
850
}
851
852
853
static DWORD_PTR COMBOEX_GetItemData (COMBOEX_INFO *infoPtr, INT_PTR index)
854
{
855
CBE_ITEMDATA const *item1;
856
CBE_ITEMDATA const *item2;
857
DWORD_PTR ret = 0;
858
859
item1 = get_item_data(infoPtr, index);
860
if ((item1 != NULL) && ((LRESULT)item1 != CB_ERR)) {
861
item2 = COMBOEX_FindItem (infoPtr, index);
862
if (item2 != item1) {
863
ERR("data structures damaged!\n");
864
return CB_ERR;
865
}
866
if (item1->mask & CBEIF_LPARAM) ret = item1->lParam;
867
TRACE("returning %#Ix\n", ret);
868
} else {
869
ret = (DWORD_PTR)item1;
870
TRACE("non-valid result from combo, returning %#Ix\n", ret);
871
}
872
return ret;
873
}
874
875
876
static INT COMBOEX_SetCursel (COMBOEX_INFO *infoPtr, INT_PTR index)
877
{
878
CBE_ITEMDATA *item;
879
INT sel;
880
881
if (!(item = COMBOEX_FindItem(infoPtr, index)))
882
return SendMessageW (infoPtr->hwndCombo, CB_SETCURSEL, index, 0);
883
884
TRACE("selecting item %Id text=%s\n", index, debugstr_txt(item->pszText));
885
infoPtr->selected = index;
886
887
sel = (INT)SendMessageW (infoPtr->hwndCombo, CB_SETCURSEL, index, 0);
888
COMBOEX_SetEditText (infoPtr, item);
889
return sel;
890
}
891
892
893
static DWORD_PTR COMBOEX_SetItemData (COMBOEX_INFO *infoPtr, INT_PTR index, DWORD_PTR data)
894
{
895
CBE_ITEMDATA *item1;
896
CBE_ITEMDATA const *item2;
897
898
item1 = get_item_data(infoPtr, index);
899
if ((item1 != NULL) && ((LRESULT)item1 != CB_ERR)) {
900
item2 = COMBOEX_FindItem (infoPtr, index);
901
if (item2 != item1) {
902
ERR("data structures damaged!\n");
903
return CB_ERR;
904
}
905
item1->mask |= CBEIF_LPARAM;
906
item1->lParam = data;
907
TRACE("setting lparam to %#Ix\n", data);
908
return 0;
909
}
910
TRACE("non-valid result from combo %p\n", item1);
911
return (DWORD_PTR)item1;
912
}
913
914
915
static INT COMBOEX_SetItemHeight (COMBOEX_INFO const *infoPtr, INT index, UINT height)
916
{
917
RECT cb_wrect, cbx_wrect, cbx_crect;
918
919
/* First, lets forward the message to the normal combo control
920
just like Windows. */
921
if (infoPtr->hwndCombo)
922
if (SendMessageW (infoPtr->hwndCombo, CB_SETITEMHEIGHT,
923
index, height) == CB_ERR) return CB_ERR;
924
925
GetWindowRect (infoPtr->hwndCombo, &cb_wrect);
926
GetWindowRect (infoPtr->hwndSelf, &cbx_wrect);
927
GetClientRect (infoPtr->hwndSelf, &cbx_crect);
928
/* the height of comboex as height of the combo + comboex border */
929
height = cb_wrect.bottom-cb_wrect.top
930
+ cbx_wrect.bottom-cbx_wrect.top
931
- (cbx_crect.bottom-cbx_crect.top);
932
TRACE("EX window=(%s), client=(%s)\n",
933
wine_dbgstr_rect(&cbx_wrect), wine_dbgstr_rect(&cbx_crect));
934
TRACE("CB window=(%s), EX setting=(0,0)-(%ld,%d)\n",
935
wine_dbgstr_rect(&cbx_wrect), cbx_wrect.right-cbx_wrect.left, height);
936
SetWindowPos (infoPtr->hwndSelf, HWND_TOP, 0, 0,
937
cbx_wrect.right-cbx_wrect.left, height,
938
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
939
940
return 0;
941
}
942
943
944
/* *** WM_xxx message support *** */
945
946
947
static LRESULT COMBOEX_Create (HWND hwnd, CREATESTRUCTA const *cs)
948
{
949
COMBOEX_INFO *infoPtr;
950
LOGFONTW mylogfont;
951
RECT win_rect;
952
INT i;
953
954
/* allocate memory for info structure */
955
infoPtr = Alloc (sizeof(COMBOEX_INFO));
956
if (!infoPtr) return -1;
957
958
/* initialize info structure */
959
/* note that infoPtr is allocated zero-filled */
960
961
infoPtr->hwndSelf = hwnd;
962
infoPtr->selected = -1;
963
964
infoPtr->unicode = IsWindowUnicode (hwnd);
965
infoPtr->hwndNotify = cs->hwndParent;
966
967
i = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)hwnd, NF_QUERY);
968
if ((i != NFR_ANSI) && (i != NFR_UNICODE)) {
969
WARN("wrong response to WM_NOTIFYFORMAT (%d), assuming ANSI\n", i);
970
i = NFR_ANSI;
971
}
972
infoPtr->NtfUnicode = (i == NFR_UNICODE);
973
974
SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
975
976
if (TRACE_ON(comboex)) {
977
RECT client, rect;
978
GetWindowRect(hwnd, &rect);
979
GetClientRect(hwnd, &client);
980
TRACE("EX window=(%s), client=(%s)\n",
981
wine_dbgstr_rect(&rect), wine_dbgstr_rect(&client));
982
}
983
984
/* Native version of ComboEx creates the ComboBox with DROPDOWNLIST */
985
/* specified. It then creates its own version of the EDIT control */
986
/* and makes the ComboBox the parent. This is because a normal */
987
/* DROPDOWNLIST does not have an EDIT control, but we need one. */
988
/* We also need to place the edit control at the proper location */
989
/* (allow space for the icons). */
990
991
infoPtr->hwndCombo = CreateWindowW (WC_COMBOBOXW, L"",
992
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VSCROLL |
993
CBS_NOINTEGRALHEIGHT | CBS_DROPDOWNLIST |
994
WS_CHILD | WS_VISIBLE | CBS_OWNERDRAWFIXED |
995
GetWindowLongW (hwnd, GWL_STYLE),
996
cs->y, cs->x, cs->cx, cs->cy, hwnd,
997
(HMENU) GetWindowLongPtrW (hwnd, GWLP_ID),
998
(HINSTANCE)GetWindowLongPtrW (hwnd, GWLP_HINSTANCE), NULL);
999
1000
SetWindowSubclass(infoPtr->hwndCombo, COMBOEX_ComboWndProc, COMBO_SUBCLASSID,
1001
(DWORD_PTR)hwnd);
1002
infoPtr->font = (HFONT)SendMessageW (infoPtr->hwndCombo, WM_GETFONT, 0, 0);
1003
1004
/*
1005
* Now create our own EDIT control so we can position it.
1006
* It is created only for CBS_DROPDOWN style
1007
*/
1008
if ((cs->style & CBS_DROPDOWNLIST) == CBS_DROPDOWN) {
1009
infoPtr->hwndEdit = CreateWindowExW (0, WC_EDITW, L"",
1010
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | ES_AUTOHSCROLL,
1011
0, 0, 0, 0, /* will set later */
1012
infoPtr->hwndCombo,
1013
(HMENU) GetWindowLongPtrW (hwnd, GWLP_ID),
1014
(HINSTANCE)GetWindowLongPtrW (hwnd, GWLP_HINSTANCE), NULL);
1015
1016
SetWindowSubclass(infoPtr->hwndEdit, COMBOEX_EditWndProc, EDIT_SUBCLASSID,
1017
(DWORD_PTR)hwnd);
1018
1019
infoPtr->font = (HFONT)SendMessageW(infoPtr->hwndCombo, WM_GETFONT, 0, 0);
1020
}
1021
1022
/*
1023
* Locate the default font if necessary and then set it in
1024
* all associated controls
1025
*/
1026
if (!infoPtr->font) {
1027
SystemParametersInfoW (SPI_GETICONTITLELOGFONT, sizeof(mylogfont),
1028
&mylogfont, 0);
1029
infoPtr->font = infoPtr->defaultFont = CreateFontIndirectW (&mylogfont);
1030
}
1031
SendMessageW (infoPtr->hwndCombo, WM_SETFONT, (WPARAM)infoPtr->font, 0);
1032
if (infoPtr->hwndEdit) {
1033
SendMessageW (infoPtr->hwndEdit, WM_SETFONT, (WPARAM)infoPtr->font, 0);
1034
SendMessageW (infoPtr->hwndEdit, EM_SETMARGINS, EC_USEFONTINFO, 0);
1035
}
1036
1037
COMBOEX_ReSize (infoPtr);
1038
1039
/* Above is fairly certain, below is much less certain. */
1040
1041
GetWindowRect(hwnd, &win_rect);
1042
1043
if (TRACE_ON(comboex)) {
1044
RECT client, rect;
1045
GetClientRect(hwnd, &client);
1046
GetWindowRect(infoPtr->hwndCombo, &rect);
1047
TRACE("EX window=(%s) client=(%s) CB wnd=(%s)\n",
1048
wine_dbgstr_rect(&win_rect), wine_dbgstr_rect(&client),
1049
wine_dbgstr_rect(&rect));
1050
}
1051
SetWindowPos(infoPtr->hwndCombo, HWND_TOP, 0, 0,
1052
win_rect.right - win_rect.left, win_rect.bottom - win_rect.top,
1053
SWP_NOACTIVATE | SWP_NOREDRAW);
1054
1055
GetWindowRect(infoPtr->hwndCombo, &win_rect);
1056
TRACE("CB window=(%s)\n", wine_dbgstr_rect(&win_rect));
1057
SetWindowPos(hwnd, HWND_TOP, 0, 0,
1058
win_rect.right - win_rect.left, win_rect.bottom - win_rect.top,
1059
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
1060
1061
COMBOEX_AdjustEditPos (infoPtr);
1062
1063
return 0;
1064
}
1065
1066
1067
static LRESULT COMBOEX_Command (COMBOEX_INFO *infoPtr, WPARAM wParam)
1068
{
1069
LRESULT lret;
1070
INT command = HIWORD(wParam);
1071
CBE_ITEMDATA *item = 0;
1072
WCHAR wintext[520];
1073
INT cursel, n;
1074
INT_PTR oldItem;
1075
NMCBEENDEDITW cbeend;
1076
DWORD oldflags;
1077
HWND parent = infoPtr->hwndNotify;
1078
1079
TRACE("for command %d\n", command);
1080
1081
switch (command)
1082
{
1083
case CBN_DROPDOWN:
1084
SetFocus (infoPtr->hwndCombo);
1085
ShowWindow (infoPtr->hwndEdit, SW_HIDE);
1086
infoPtr->flags |= WCBE_ACTEDIT;
1087
return SendMessageW (parent, WM_COMMAND, wParam, (LPARAM)infoPtr->hwndSelf);
1088
case CBN_CLOSEUP:
1089
SendMessageW (parent, WM_COMMAND, wParam, (LPARAM)infoPtr->hwndSelf);
1090
ShowWindow (infoPtr->hwndEdit, SW_SHOW);
1091
InvalidateRect (infoPtr->hwndCombo, 0, TRUE);
1092
if (infoPtr->hwndEdit) InvalidateRect (infoPtr->hwndEdit, 0, TRUE);
1093
cursel = SendMessageW (infoPtr->hwndCombo, CB_GETCURSEL, 0, 0);
1094
if (cursel == -1) {
1095
cmp_func_t cmptext = get_cmp_func(infoPtr);
1096
/* find match from edit against those in Combobox */
1097
GetWindowTextW (infoPtr->hwndEdit, wintext, 520);
1098
n = SendMessageW (infoPtr->hwndCombo, CB_GETCOUNT, 0, 0);
1099
for (cursel = 0; cursel < n; cursel++){
1100
item = get_item_data(infoPtr, cursel);
1101
if ((INT_PTR)item == CB_ERR) break;
1102
if (!cmptext(COMBOEX_GetText(infoPtr, item), wintext)) break;
1103
}
1104
if ((cursel == n) || ((INT_PTR)item == CB_ERR)) {
1105
TRACE("failed to find match??? item=%p cursel=%d\n",
1106
item, cursel);
1107
if (infoPtr->hwndEdit) SetFocus(infoPtr->hwndEdit);
1108
return 0;
1109
}
1110
}
1111
else {
1112
item = get_item_data(infoPtr, cursel);
1113
if ((INT_PTR)item == CB_ERR) {
1114
TRACE("failed to find match??? item=%p cursel=%d\n",
1115
item, cursel);
1116
if (infoPtr->hwndEdit) SetFocus(infoPtr->hwndEdit);
1117
return 0;
1118
}
1119
}
1120
1121
/* Save flags for testing and reset them */
1122
oldflags = infoPtr->flags;
1123
infoPtr->flags &= ~(WCBE_ACTEDIT | WCBE_EDITCHG);
1124
1125
if (oldflags & WCBE_ACTEDIT) {
1126
cbeend.fChanged = (oldflags & WCBE_EDITCHG);
1127
cbeend.iNewSelection = SendMessageW (infoPtr->hwndCombo,
1128
CB_GETCURSEL, 0, 0);
1129
cbeend.iWhy = CBENF_DROPDOWN;
1130
1131
if (COMBOEX_NotifyEndEdit (infoPtr, &cbeend, COMBOEX_GetText(infoPtr, item))) return 0;
1132
}
1133
1134
/* if selection has changed the set the new current selection */
1135
cursel = SendMessageW (infoPtr->hwndCombo, CB_GETCURSEL, 0, 0);
1136
if ((oldflags & WCBE_EDITCHG) || (cursel != infoPtr->selected)) {
1137
infoPtr->selected = cursel;
1138
SendMessageW (infoPtr->hwndSelf, CB_SETCURSEL, cursel, 0);
1139
SetFocus(infoPtr->hwndCombo);
1140
}
1141
return 0;
1142
1143
case CBN_SELCHANGE:
1144
/*
1145
* CB_GETCURSEL(Combo)
1146
* CB_GETITEMDATA(Combo) < simulated by COMBOEX_FindItem
1147
* lstrlenA
1148
* WM_SETTEXT(Edit)
1149
* WM_GETTEXTLENGTH(Edit)
1150
* WM_GETTEXT(Edit)
1151
* EM_SETSEL(Edit, 0,0)
1152
* WM_GETTEXTLENGTH(Edit)
1153
* WM_GETTEXT(Edit)
1154
* EM_SETSEL(Edit, 0,len)
1155
* return WM_COMMAND to parent
1156
*/
1157
oldItem = SendMessageW (infoPtr->hwndCombo, CB_GETCURSEL, 0, 0);
1158
if (!(item = COMBOEX_FindItem(infoPtr, oldItem))) {
1159
ERR("item %Id not found. Problem!\n", oldItem);
1160
break;
1161
}
1162
infoPtr->selected = oldItem;
1163
COMBOEX_SetEditText (infoPtr, item);
1164
return SendMessageW (parent, WM_COMMAND, wParam, (LPARAM)infoPtr->hwndSelf);
1165
1166
case CBN_SELENDOK:
1167
case CBN_SELENDCANCEL:
1168
/*
1169
* We have to change the handle since we are the control
1170
* issuing the message. IE4 depends on this.
1171
*/
1172
return SendMessageW (parent, WM_COMMAND, wParam, (LPARAM)infoPtr->hwndSelf);
1173
1174
case CBN_KILLFOCUS:
1175
SendMessageW (parent, WM_COMMAND, wParam, (LPARAM)infoPtr->hwndSelf);
1176
if (infoPtr->flags & WCBE_ACTEDIT) {
1177
GetWindowTextW (infoPtr->hwndEdit, wintext, 260);
1178
cbeend.fChanged = (infoPtr->flags & WCBE_EDITCHG);
1179
cbeend.iNewSelection = SendMessageW (infoPtr->hwndCombo,
1180
CB_GETCURSEL, 0, 0);
1181
cbeend.iWhy = CBENF_KILLFOCUS;
1182
1183
infoPtr->flags &= ~(WCBE_ACTEDIT | WCBE_EDITCHG);
1184
if (COMBOEX_NotifyEndEdit (infoPtr, &cbeend, wintext)) return 0;
1185
}
1186
/* possible CB_GETCURSEL */
1187
InvalidateRect (infoPtr->hwndCombo, 0, 0);
1188
return 0;
1189
1190
case CBN_SETFOCUS:
1191
return SendMessageW (parent, WM_COMMAND, wParam, (LPARAM)infoPtr->hwndSelf);
1192
1193
default:
1194
/*
1195
* We have to change the handle since we are the control
1196
* issuing the message. IE4 depends on this.
1197
* We also need to set the focus back to the Edit control
1198
* after passing the command to the parent of the ComboEx.
1199
*/
1200
lret = SendMessageW (parent, WM_COMMAND, wParam, (LPARAM)infoPtr->hwndSelf);
1201
if (infoPtr->hwndEdit) SetFocus(infoPtr->hwndEdit);
1202
return lret;
1203
}
1204
return 0;
1205
}
1206
1207
1208
static BOOL COMBOEX_WM_DeleteItem (COMBOEX_INFO *infoPtr, DELETEITEMSTRUCT const *dis)
1209
{
1210
CBE_ITEMDATA *item, *olditem;
1211
NMCOMBOBOXEXW nmcit;
1212
UINT i;
1213
1214
TRACE("CtlType=%08x, CtlID=%08x, itemID=%08x, hwnd=%p, data=%Ix\n",
1215
dis->CtlType, dis->CtlID, dis->itemID, dis->hwndItem, dis->itemData);
1216
1217
if (dis->itemID >= infoPtr->nb_items) return FALSE;
1218
1219
olditem = infoPtr->items;
1220
i = infoPtr->nb_items - 1;
1221
1222
if (i == dis->itemID) {
1223
infoPtr->items = infoPtr->items->next;
1224
}
1225
else {
1226
item = olditem;
1227
i--;
1228
1229
/* find the prior item in the list */
1230
while (item->next && (i > dis->itemID)) {
1231
item = item->next;
1232
i--;
1233
}
1234
if (!item->next || (i != dis->itemID)) {
1235
ERR("COMBOBOXEX item structures broken. Please report!\n");
1236
return FALSE;
1237
}
1238
olditem = item->next;
1239
item->next = item->next->next;
1240
}
1241
infoPtr->nb_items--;
1242
1243
memset (&nmcit.ceItem, 0, sizeof(nmcit.ceItem));
1244
nmcit.ceItem.mask=~0;
1245
COMBOEX_CopyItem (olditem, &nmcit.ceItem);
1246
COMBOEX_NotifyItem (infoPtr, CBEN_DELETEITEM, &nmcit);
1247
1248
COMBOEX_FreeText(olditem);
1249
Free(olditem);
1250
1251
return TRUE;
1252
}
1253
1254
1255
static LRESULT COMBOEX_DrawItem (COMBOEX_INFO *infoPtr, DRAWITEMSTRUCT const *dis)
1256
{
1257
CBE_ITEMDATA *item = NULL;
1258
SIZE txtsize;
1259
RECT rect;
1260
LPCWSTR str = L"";
1261
UINT xbase, x, y;
1262
INT len;
1263
COLORREF nbkc, ntxc, bkc, txc;
1264
int drawimage, drawstate, xioff, selected;
1265
1266
TRACE("DRAWITEMSTRUCT: CtlType=0x%08x CtlID=0x%08x\n",
1267
dis->CtlType, dis->CtlID);
1268
TRACE("itemID=0x%08x itemAction=0x%08x itemState=0x%08x\n",
1269
dis->itemID, dis->itemAction, dis->itemState);
1270
TRACE("hWnd=%p hDC=%p (%s) itemData=%#Ix\n",
1271
dis->hwndItem, dis->hDC, wine_dbgstr_rect(&dis->rcItem), dis->itemData);
1272
1273
/* MSDN says: */
1274
/* "itemID - Specifies the menu item identifier for a menu */
1275
/* item or the index of the item in a list box or combo box. */
1276
/* For an empty list box or combo box, this member can be -1. */
1277
/* This allows the application to draw only the focus */
1278
/* rectangle at the coordinates specified by the rcItem */
1279
/* member even though there are no items in the control. */
1280
/* This indicates to the user whether the list box or combo */
1281
/* box has the focus. How the bits are set in the itemAction */
1282
/* member determines whether the rectangle is to be drawn as */
1283
/* though the list box or combo box has the focus. */
1284
if (dis->itemID == 0xffffffff) {
1285
if ( ( (dis->itemAction & ODA_FOCUS) && (dis->itemState & ODS_SELECTED)) ||
1286
( (dis->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)) && (dis->itemState & ODS_FOCUS) ) ) {
1287
1288
TRACE("drawing item -1 special focus, rect=(%s)\n",
1289
wine_dbgstr_rect(&dis->rcItem));
1290
}
1291
else if ((dis->CtlType == ODT_COMBOBOX) &&
1292
(dis->itemAction == ODA_DRAWENTIRE)) {
1293
/* draw of edit control data */
1294
1295
if (TRACE_ON(comboex)) {
1296
RECT exrc, cbrc, edrc;
1297
GetWindowRect (infoPtr->hwndSelf, &exrc);
1298
GetWindowRect (infoPtr->hwndCombo, &cbrc);
1299
SetRect(&edrc, -1, -1, -1, -1);
1300
if (infoPtr->hwndEdit) GetWindowRect (infoPtr->hwndEdit, &edrc);
1301
TRACE("window rects ex=(%s), cb=(%s), ed=(%s)\n",
1302
wine_dbgstr_rect(&exrc), wine_dbgstr_rect(&cbrc),
1303
wine_dbgstr_rect(&edrc));
1304
}
1305
}
1306
else {
1307
ERR("NOT drawing item -1 special focus, rect=(%s), action=%08x, state=%08x\n",
1308
wine_dbgstr_rect(&dis->rcItem),
1309
dis->itemAction, dis->itemState);
1310
return 0;
1311
}
1312
}
1313
1314
/* If draw item is -1 (edit control) setup the item pointer */
1315
if (dis->itemID == 0xffffffff) {
1316
item = &infoPtr->edit;
1317
1318
if (infoPtr->hwndEdit) {
1319
/* free previous text of edit item */
1320
COMBOEX_FreeText(item);
1321
item->mask &= ~CBEIF_TEXT;
1322
if( (len = GetWindowTextLengthW(infoPtr->hwndEdit)) ) {
1323
item->mask |= CBEIF_TEXT;
1324
item->pszText = Alloc ((len + 1)*sizeof(WCHAR));
1325
if (item->pszText)
1326
GetWindowTextW(infoPtr->hwndEdit, item->pszText, len+1);
1327
1328
TRACE("edit control hwndEdit=%p, text len=%d str=%s\n",
1329
infoPtr->hwndEdit, len, debugstr_txt(item->pszText));
1330
}
1331
}
1332
}
1333
1334
1335
/* if the item pointer is not set, then get the data and locate it */
1336
if (!item) {
1337
item = get_item_data(infoPtr, dis->itemID);
1338
if (item == (CBE_ITEMDATA *)CB_ERR) {
1339
ERR("invalid item for id %d\n", dis->itemID);
1340
return 0;
1341
}
1342
}
1343
1344
if (TRACE_ON(comboex)) COMBOEX_DumpItem (item);
1345
1346
xbase = CBE_STARTOFFSET;
1347
if ((item->mask & CBEIF_INDENT) && (dis->itemState & ODS_COMBOEXLBOX)) {
1348
INT indent = item->iIndent;
1349
if (indent == I_INDENTCALLBACK) {
1350
NMCOMBOBOXEXW nmce;
1351
ZeroMemory(&nmce, sizeof(nmce));
1352
nmce.ceItem.mask = CBEIF_INDENT;
1353
nmce.ceItem.lParam = item->lParam;
1354
nmce.ceItem.iItem = dis->itemID;
1355
COMBOEX_NotifyItem(infoPtr, CBEN_GETDISPINFOW, &nmce);
1356
if (nmce.ceItem.mask & CBEIF_DI_SETITEM)
1357
item->iIndent = nmce.ceItem.iIndent;
1358
indent = nmce.ceItem.iIndent;
1359
}
1360
xbase += (indent * CBE_INDENT);
1361
}
1362
1363
drawimage = -2;
1364
drawstate = ILD_NORMAL;
1365
selected = infoPtr->selected == dis->itemID;
1366
1367
if (item->mask & CBEIF_IMAGE)
1368
drawimage = item->iImage;
1369
if (item->mask & CBEIF_SELECTEDIMAGE && selected)
1370
drawimage = item->iSelectedImage;
1371
if (dis->itemState & ODS_COMBOEXLBOX) {
1372
/* drawing listbox entry */
1373
if (dis->itemState & ODS_SELECTED)
1374
drawstate = ILD_SELECTED;
1375
} else {
1376
/* drawing combo/edit entry */
1377
if (IsWindowVisible(infoPtr->hwndEdit)) {
1378
/* if we have an edit control, set the selection state from the edit focus state */
1379
if (infoPtr->flags & WCBE_EDITFOCUSED)
1380
drawstate = ILD_SELECTED;
1381
} else
1382
/* if we don't have an edit control, use
1383
* the requested state.
1384
*/
1385
if (dis->itemState & ODS_SELECTED)
1386
drawstate = ILD_SELECTED;
1387
}
1388
1389
if (infoPtr->himl && !(infoPtr->dwExtStyle & CBES_EX_NOEDITIMAGEINDENT)) {
1390
IMAGEINFO iinfo;
1391
iinfo.rcImage.left = iinfo.rcImage.right = 0;
1392
ImageList_GetImageInfo(infoPtr->himl, 0, &iinfo);
1393
xioff = iinfo.rcImage.right - iinfo.rcImage.left + CBE_SEP;
1394
} else xioff = 0;
1395
1396
/* setup pointer to text to be drawn */
1397
str = COMBOEX_GetText(infoPtr, item);
1398
if (!str) str = L"";
1399
1400
len = lstrlenW (str);
1401
GetTextExtentPoint32W (dis->hDC, str, len, &txtsize);
1402
1403
if (dis->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)) {
1404
int overlay = item->iOverlay;
1405
1406
if (drawimage == I_IMAGECALLBACK) {
1407
NMCOMBOBOXEXW nmce;
1408
ZeroMemory(&nmce, sizeof(nmce));
1409
nmce.ceItem.mask = selected ? CBEIF_SELECTEDIMAGE : CBEIF_IMAGE;
1410
nmce.ceItem.lParam = item->lParam;
1411
nmce.ceItem.iItem = dis->itemID;
1412
COMBOEX_NotifyItem(infoPtr, CBEN_GETDISPINFOW, &nmce);
1413
if (!selected) {
1414
if (nmce.ceItem.mask & CBEIF_DI_SETITEM) item->iImage = nmce.ceItem.iImage;
1415
drawimage = nmce.ceItem.iImage;
1416
} else {
1417
if (nmce.ceItem.mask & CBEIF_DI_SETITEM) item->iSelectedImage = nmce.ceItem.iSelectedImage;
1418
drawimage = nmce.ceItem.iSelectedImage;
1419
}
1420
}
1421
1422
if (overlay == I_IMAGECALLBACK) {
1423
NMCOMBOBOXEXW nmce;
1424
ZeroMemory(&nmce, sizeof(nmce));
1425
nmce.ceItem.mask = CBEIF_OVERLAY;
1426
nmce.ceItem.lParam = item->lParam;
1427
nmce.ceItem.iItem = dis->itemID;
1428
COMBOEX_NotifyItem(infoPtr, CBEN_GETDISPINFOW, &nmce);
1429
if (nmce.ceItem.mask & CBEIF_DI_SETITEM)
1430
item->iOverlay = nmce.ceItem.iOverlay;
1431
overlay = nmce.ceItem.iOverlay;
1432
}
1433
1434
if (drawimage >= 0 &&
1435
!(infoPtr->dwExtStyle & (CBES_EX_NOEDITIMAGE | CBES_EX_NOEDITIMAGEINDENT))) {
1436
if (overlay > 0) ImageList_SetOverlayImage (infoPtr->himl, overlay, 1);
1437
ImageList_Draw (infoPtr->himl, drawimage, dis->hDC, xbase, dis->rcItem.top,
1438
drawstate | (overlay > 0 ? INDEXTOOVERLAYMASK(1) : 0));
1439
}
1440
1441
/* now draw the text */
1442
if (!IsWindowVisible (infoPtr->hwndEdit)) {
1443
nbkc = (dis->itemState & ODS_SELECTED) ?
1444
comctl32_color.clrHighlight : comctl32_color.clrWindow;
1445
bkc = SetBkColor (dis->hDC, nbkc);
1446
ntxc = (dis->itemState & ODS_SELECTED) ?
1447
comctl32_color.clrHighlightText : comctl32_color.clrWindowText;
1448
txc = SetTextColor (dis->hDC, ntxc);
1449
x = xbase + xioff;
1450
y = dis->rcItem.top +
1451
(dis->rcItem.bottom - dis->rcItem.top - txtsize.cy) / 2;
1452
SetRect(&rect, x, dis->rcItem.top + 1, x + txtsize.cx, dis->rcItem.bottom - 1);
1453
TRACE("drawing item %d text, rect=(%s)\n",
1454
dis->itemID, wine_dbgstr_rect(&rect));
1455
ExtTextOutW (dis->hDC, x, y, ETO_OPAQUE | ETO_CLIPPED,
1456
&rect, str, len, 0);
1457
SetBkColor (dis->hDC, bkc);
1458
SetTextColor (dis->hDC, txc);
1459
}
1460
}
1461
1462
if (dis->itemAction & ODA_FOCUS) {
1463
rect.left = xbase + xioff - 1;
1464
rect.right = rect.left + txtsize.cx + 2;
1465
rect.top = dis->rcItem.top;
1466
rect.bottom = dis->rcItem.bottom;
1467
DrawFocusRect(dis->hDC, &rect);
1468
}
1469
1470
return 0;
1471
}
1472
1473
1474
static void COMBOEX_ResetContent (COMBOEX_INFO *infoPtr)
1475
{
1476
if (infoPtr->items)
1477
{
1478
CBE_ITEMDATA *item, *next;
1479
1480
item = infoPtr->items;
1481
while (item) {
1482
next = item->next;
1483
COMBOEX_FreeText (item);
1484
Free (item);
1485
item = next;
1486
}
1487
infoPtr->items = 0;
1488
}
1489
1490
infoPtr->selected = -1;
1491
infoPtr->nb_items = 0;
1492
}
1493
1494
1495
static LRESULT COMBOEX_Destroy (COMBOEX_INFO *infoPtr)
1496
{
1497
if (infoPtr->hwndCombo)
1498
SetWindowSubclass(infoPtr->hwndCombo, COMBOEX_ComboWndProc, COMBO_SUBCLASSID, 0);
1499
1500
if (infoPtr->hwndEdit)
1501
SetWindowSubclass(infoPtr->hwndEdit, COMBOEX_EditWndProc, EDIT_SUBCLASSID, 0);
1502
1503
COMBOEX_FreeText (&infoPtr->edit);
1504
COMBOEX_ResetContent (infoPtr);
1505
1506
if (infoPtr->defaultFont)
1507
DeleteObject (infoPtr->defaultFont);
1508
1509
SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
1510
1511
/* free comboex info data */
1512
Free (infoPtr);
1513
1514
return 0;
1515
}
1516
1517
1518
static LRESULT COMBOEX_Enable (COMBOEX_INFO *infoPtr, BOOL enable)
1519
{
1520
TRACE("hwnd=%p, enable=%s\n", infoPtr->hwndSelf, enable ? "TRUE":"FALSE");
1521
1522
if (infoPtr->hwndEdit)
1523
EnableWindow(infoPtr->hwndEdit, enable);
1524
1525
EnableWindow(infoPtr->hwndCombo, enable);
1526
1527
/* Force the control to repaint when the enabled state changes. */
1528
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
1529
1530
return 1;
1531
}
1532
1533
1534
static LRESULT COMBOEX_MeasureItem (COMBOEX_INFO const *infoPtr, MEASUREITEMSTRUCT *mis)
1535
{
1536
SIZE mysize;
1537
HDC hdc;
1538
1539
hdc = GetDC (0);
1540
GetTextExtentPointW (hdc, L"W", 1, &mysize);
1541
ReleaseDC (0, hdc);
1542
mis->itemHeight = mysize.cy + CBE_EXTRA;
1543
1544
TRACE("adjusted height hwnd=%p, height=%d\n",
1545
infoPtr->hwndSelf, mis->itemHeight);
1546
1547
return 0;
1548
}
1549
1550
1551
static LRESULT COMBOEX_NCCreate (HWND hwnd)
1552
{
1553
/* WARNING: The COMBOEX_INFO structure is not yet created */
1554
DWORD oldstyle, newstyle;
1555
1556
oldstyle = (DWORD)GetWindowLongW (hwnd, GWL_STYLE);
1557
newstyle = oldstyle & ~(WS_VSCROLL | WS_HSCROLL | WS_BORDER);
1558
if (newstyle != oldstyle)
1559
{
1560
TRACE("req style %#lx, resetting style %#lx\n", oldstyle, newstyle);
1561
SetWindowLongW (hwnd, GWL_STYLE, newstyle);
1562
}
1563
return 1;
1564
}
1565
1566
1567
static LRESULT COMBOEX_NotifyFormat (COMBOEX_INFO *infoPtr, LPARAM lParam)
1568
{
1569
if (lParam == NF_REQUERY) {
1570
INT i = SendMessageW(infoPtr->hwndNotify,
1571
WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
1572
infoPtr->NtfUnicode = (i == NFR_UNICODE);
1573
}
1574
return infoPtr->NtfUnicode ? NFR_UNICODE : NFR_ANSI;
1575
}
1576
1577
1578
static LRESULT COMBOEX_Size (COMBOEX_INFO *infoPtr, INT width, INT height)
1579
{
1580
TRACE("(width=%d, height=%d)\n", width, height);
1581
1582
MoveWindow (infoPtr->hwndCombo, 0, 0, width, height, TRUE);
1583
1584
COMBOEX_AdjustEditPos (infoPtr);
1585
1586
return 0;
1587
}
1588
1589
static LRESULT COMBOEX_SetFont( COMBOEX_INFO *infoPtr, HFONT font, BOOL redraw )
1590
{
1591
infoPtr->font = font;
1592
SendMessageW( infoPtr->hwndCombo, WM_SETFONT, (WPARAM)font, 0 );
1593
if (infoPtr->hwndEdit) SendMessageW( infoPtr->hwndEdit, WM_SETFONT, (WPARAM)font, 0 );
1594
COMBOEX_ReSize( infoPtr );
1595
if (redraw) InvalidateRect( infoPtr->hwndCombo, NULL, TRUE );
1596
return 0;
1597
}
1598
1599
static LRESULT COMBOEX_SetRedraw(const COMBOEX_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
1600
{
1601
LRESULT ret = DefWindowProcW( infoPtr->hwndSelf, WM_SETREDRAW, wParam, lParam );
1602
if (wParam) RedrawWindow( infoPtr->hwndSelf, NULL, 0, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN );
1603
return ret;
1604
}
1605
1606
1607
static LRESULT COMBOEX_WindowPosChanging (const COMBOEX_INFO *infoPtr, WINDOWPOS *wp)
1608
{
1609
RECT cbx_wrect, cbx_crect, cb_wrect;
1610
INT width, height;
1611
1612
GetWindowRect (infoPtr->hwndSelf, &cbx_wrect);
1613
GetClientRect (infoPtr->hwndSelf, &cbx_crect);
1614
GetWindowRect (infoPtr->hwndCombo, &cb_wrect);
1615
1616
/* width is winpos value + border width of comboex */
1617
width = wp->cx
1618
+ (cbx_wrect.right-cbx_wrect.left)
1619
- (cbx_crect.right-cbx_crect.left);
1620
1621
TRACE("winpos=(%d,%d %dx%d) flags=0x%08x\n",
1622
wp->x, wp->y, wp->cx, wp->cy, wp->flags);
1623
TRACE("EX window=(%s), client=(%s)\n",
1624
wine_dbgstr_rect(&cbx_wrect), wine_dbgstr_rect(&cbx_crect));
1625
TRACE("CB window=(%s), EX setting=(0,0)-(%d,%ld)\n",
1626
wine_dbgstr_rect(&cbx_wrect), width, cb_wrect.bottom-cb_wrect.top);
1627
1628
if (width) SetWindowPos (infoPtr->hwndCombo, HWND_TOP, 0, 0,
1629
width,
1630
cb_wrect.bottom-cb_wrect.top,
1631
SWP_NOACTIVATE);
1632
1633
GetWindowRect (infoPtr->hwndCombo, &cb_wrect);
1634
1635
/* height is combo window height plus border width of comboex */
1636
height = (cb_wrect.bottom-cb_wrect.top)
1637
+ (cbx_wrect.bottom-cbx_wrect.top)
1638
- (cbx_crect.bottom-cbx_crect.top);
1639
wp->cy = height;
1640
if (infoPtr->hwndEdit) {
1641
COMBOEX_AdjustEditPos (infoPtr);
1642
InvalidateRect (infoPtr->hwndCombo, 0, TRUE);
1643
}
1644
1645
return 0;
1646
}
1647
1648
static LRESULT CALLBACK
1649
COMBOEX_EditWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
1650
UINT_PTR uId, DWORD_PTR ref_data)
1651
{
1652
COMBOEX_INFO *infoPtr = COMBOEX_GetInfoPtr ((HWND)ref_data);
1653
NMCBEENDEDITW cbeend;
1654
WCHAR edit_text[260];
1655
COLORREF obkc;
1656
HDC hDC;
1657
RECT rect;
1658
LRESULT lret;
1659
1660
TRACE("hwnd %p, msg %x, wparam %Ix, lParam %Ix, info_ptr=%p\n",
1661
hwnd, uMsg, wParam, lParam, infoPtr);
1662
1663
if (uMsg == WM_NCDESTROY)
1664
RemoveWindowSubclass(hwnd, COMBOEX_EditWndProc, EDIT_SUBCLASSID);
1665
1666
if (!infoPtr)
1667
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
1668
1669
switch (uMsg)
1670
{
1671
1672
case WM_CHAR:
1673
/* handle (ignore) the return character */
1674
if (wParam == VK_RETURN) return 0;
1675
/* all other characters pass into the real Edit */
1676
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
1677
1678
case WM_ERASEBKGND:
1679
hDC = (HDC) wParam;
1680
obkc = SetBkColor (hDC, comctl32_color.clrWindow);
1681
GetClientRect (hwnd, &rect);
1682
TRACE("erasing (%s)\n", wine_dbgstr_rect(&rect));
1683
ExtTextOutW (hDC, 0, 0, ETO_OPAQUE, &rect, 0, 0, 0);
1684
SetBkColor (hDC, obkc);
1685
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
1686
1687
case WM_KEYDOWN: {
1688
INT_PTR oldItem, selected;
1689
CBE_ITEMDATA *item;
1690
1691
switch ((INT)wParam)
1692
{
1693
case VK_ESCAPE:
1694
TRACE("special code for VK_ESCAPE\n");
1695
1696
GetWindowTextW (infoPtr->hwndEdit, edit_text, 260);
1697
1698
infoPtr->flags &= ~(WCBE_ACTEDIT | WCBE_EDITCHG);
1699
cbeend.fChanged = FALSE;
1700
cbeend.iNewSelection = SendMessageW (infoPtr->hwndCombo,
1701
CB_GETCURSEL, 0, 0);
1702
cbeend.iWhy = CBENF_ESCAPE;
1703
1704
if (COMBOEX_NotifyEndEdit (infoPtr, &cbeend, edit_text)) return 0;
1705
oldItem = SendMessageW (infoPtr->hwndCombo, CB_GETCURSEL, 0, 0);
1706
InvalidateRect (infoPtr->hwndCombo, 0, 0);
1707
if (!(item = COMBOEX_FindItem(infoPtr, oldItem))) {
1708
ERR("item %Id not found. Problem!\n", oldItem);
1709
break;
1710
}
1711
infoPtr->selected = oldItem;
1712
COMBOEX_SetEditText (infoPtr, item);
1713
RedrawWindow (infoPtr->hwndCombo, 0, 0, RDW_ERASE |
1714
RDW_INVALIDATE);
1715
break;
1716
1717
case VK_RETURN:
1718
TRACE("special code for VK_RETURN\n");
1719
1720
GetWindowTextW (infoPtr->hwndEdit, edit_text, 260);
1721
1722
infoPtr->flags &= ~(WCBE_ACTEDIT | WCBE_EDITCHG);
1723
selected = SendMessageW (infoPtr->hwndCombo,
1724
CB_GETCURSEL, 0, 0);
1725
1726
if (selected != -1) {
1727
cmp_func_t cmptext = get_cmp_func(infoPtr);
1728
item = COMBOEX_FindItem (infoPtr, selected);
1729
TRACE("handling VK_RETURN, selected = %Id, selected_text=%s\n", selected, debugstr_txt(item->pszText));
1730
TRACE("handling VK_RETURN, edittext=%s\n", debugstr_w(edit_text));
1731
if (cmptext (COMBOEX_GetText(infoPtr, item), edit_text)) {
1732
/* strings not equal -- indicate edit has changed */
1733
selected = -1;
1734
}
1735
}
1736
1737
cbeend.iNewSelection = selected;
1738
cbeend.fChanged = TRUE;
1739
cbeend.iWhy = CBENF_RETURN;
1740
if (COMBOEX_NotifyEndEdit (infoPtr, &cbeend, edit_text)) {
1741
/* abort the change, restore previous */
1742
TRACE("Notify requested abort of change\n");
1743
COMBOEX_SetEditText (infoPtr, &infoPtr->edit);
1744
RedrawWindow (infoPtr->hwndCombo, 0, 0, RDW_ERASE |
1745
RDW_INVALIDATE);
1746
return 0;
1747
}
1748
oldItem = SendMessageW (infoPtr->hwndCombo,CB_GETCURSEL, 0, 0);
1749
if (oldItem != -1) {
1750
/* if something is selected, then deselect it */
1751
SendMessageW (infoPtr->hwndCombo, CB_SETCURSEL, -1, 0);
1752
}
1753
InvalidateRect (infoPtr->hwndCombo, 0, 0);
1754
SetFocus(infoPtr->hwndEdit);
1755
break;
1756
1757
case VK_UP:
1758
case VK_DOWN:
1759
{
1760
INT step = wParam == VK_DOWN ? 1 : -1;
1761
1762
oldItem = SendMessageW (infoPtr->hwndSelf, CB_GETCURSEL, 0, 0);
1763
if (oldItem >= 0 && oldItem + step >= 0)
1764
SendMessageW (infoPtr->hwndSelf, CB_SETCURSEL, oldItem + step, 0);
1765
return 0;
1766
}
1767
default:
1768
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
1769
}
1770
return 0;
1771
}
1772
1773
case WM_SETFOCUS:
1774
/* remember the focus to set state of icon */
1775
lret = DefSubclassProc(hwnd, uMsg, wParam, lParam);
1776
infoPtr->flags |= WCBE_EDITFOCUSED;
1777
return lret;
1778
1779
case WM_KILLFOCUS:
1780
/*
1781
* do NOTIFY CBEN_ENDEDIT with CBENF_KILLFOCUS
1782
*/
1783
infoPtr->flags &= ~WCBE_EDITFOCUSED;
1784
if (infoPtr->flags & WCBE_ACTEDIT) {
1785
infoPtr->flags &= ~(WCBE_ACTEDIT | WCBE_EDITCHG);
1786
1787
GetWindowTextW (infoPtr->hwndEdit, edit_text, 260);
1788
cbeend.fChanged = FALSE;
1789
cbeend.iNewSelection = SendMessageW (infoPtr->hwndCombo,
1790
CB_GETCURSEL, 0, 0);
1791
cbeend.iWhy = CBENF_KILLFOCUS;
1792
1793
COMBOEX_NotifyEndEdit (infoPtr, &cbeend, edit_text);
1794
}
1795
/* fall through */
1796
1797
default:
1798
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
1799
}
1800
}
1801
1802
1803
static LRESULT CALLBACK
1804
COMBOEX_ComboWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
1805
UINT_PTR uId, DWORD_PTR ref_data)
1806
{
1807
COMBOEX_INFO *infoPtr = COMBOEX_GetInfoPtr ((HWND)ref_data);
1808
NMCBEENDEDITW cbeend;
1809
NMMOUSE nmmse;
1810
COLORREF obkc;
1811
HDC hDC;
1812
HWND focusedhwnd;
1813
RECT rect;
1814
POINT pt;
1815
WCHAR edit_text[260];
1816
1817
TRACE("hwnd %p, msg %x, wparam %Ix, lParam %Ix, info_ptr %p\n",
1818
hwnd, uMsg, wParam, lParam, infoPtr);
1819
1820
if (uMsg == WM_NCDESTROY)
1821
RemoveWindowSubclass(hwnd, COMBOEX_ComboWndProc, COMBO_SUBCLASSID);
1822
1823
if (!infoPtr)
1824
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
1825
1826
switch (uMsg)
1827
{
1828
case WM_DRAWITEM:
1829
/*
1830
* The only way this message should come is from the
1831
* child Listbox issuing the message. Flag this so
1832
* that ComboEx knows this is listbox.
1833
*/
1834
((DRAWITEMSTRUCT *)lParam)->itemState |= ODS_COMBOEXLBOX;
1835
break;
1836
1837
case WM_ERASEBKGND:
1838
hDC = (HDC) wParam;
1839
obkc = SetBkColor (hDC, comctl32_color.clrWindow);
1840
GetClientRect (hwnd, &rect);
1841
TRACE("erasing (%s)\n", wine_dbgstr_rect(&rect));
1842
ExtTextOutW (hDC, 0, 0, ETO_OPAQUE, &rect, 0, 0, 0);
1843
SetBkColor (hDC, obkc);
1844
break;
1845
1846
case WM_SETCURSOR:
1847
/*
1848
* WM_NOTIFY to comboex parent (rebar)
1849
* with NM_SETCURSOR with extra words of 0,0,0,0,0x02010001
1850
* CallWindowProc (previous)
1851
*/
1852
nmmse.dwItemSpec = 0;
1853
nmmse.dwItemData = 0;
1854
nmmse.pt.x = 0;
1855
nmmse.pt.y = 0;
1856
nmmse.dwHitInfo = lParam;
1857
COMBOEX_Notify (infoPtr, NM_SETCURSOR, (NMHDR *)&nmmse);
1858
break;
1859
1860
case WM_LBUTTONDOWN:
1861
GetClientRect (hwnd, &rect);
1862
rect.bottom = rect.top + SendMessageW(infoPtr->hwndSelf,
1863
CB_GETITEMHEIGHT, -1, 0);
1864
rect.left = rect.right - GetSystemMetrics(SM_CXVSCROLL);
1865
pt.x = (short)LOWORD(lParam);
1866
pt.y = (short)HIWORD(lParam);
1867
if (PtInRect(&rect, pt))
1868
break;
1869
1870
infoPtr->flags |= WCBE_MOUSECAPTURED;
1871
SetCapture(hwnd);
1872
return 0;
1873
1874
case WM_LBUTTONUP:
1875
if (!(infoPtr->flags & WCBE_MOUSECAPTURED))
1876
break;
1877
1878
ReleaseCapture();
1879
infoPtr->flags &= ~WCBE_MOUSECAPTURED;
1880
if (infoPtr->flags & WCBE_MOUSEDRAGGED) {
1881
infoPtr->flags &= ~WCBE_MOUSEDRAGGED;
1882
} else {
1883
SendMessageW(hwnd, CB_SHOWDROPDOWN, TRUE, 0);
1884
}
1885
return 0;
1886
1887
case WM_MOUSEMOVE:
1888
if ( (infoPtr->flags & WCBE_MOUSECAPTURED) &&
1889
!(infoPtr->flags & WCBE_MOUSEDRAGGED)) {
1890
GetWindowTextW (infoPtr->hwndEdit, edit_text, 260);
1891
COMBOEX_NotifyDragBegin(infoPtr, edit_text);
1892
infoPtr->flags |= WCBE_MOUSEDRAGGED;
1893
}
1894
break;
1895
1896
case WM_COMMAND:
1897
switch (HIWORD(wParam)) {
1898
1899
case EN_UPDATE:
1900
/* traces show that COMBOEX does not issue CBN_EDITUPDATE
1901
* on the EN_UPDATE
1902
*/
1903
return 0;
1904
1905
case EN_KILLFOCUS:
1906
focusedhwnd = GetFocus();
1907
if (infoPtr->flags & WCBE_ACTEDIT) {
1908
GetWindowTextW (infoPtr->hwndEdit, edit_text, 260);
1909
cbeend.fChanged = (infoPtr->flags & WCBE_EDITCHG);
1910
cbeend.iNewSelection = SendMessageW (infoPtr->hwndCombo,
1911
CB_GETCURSEL, 0, 0);
1912
cbeend.iWhy = CBENF_KILLFOCUS;
1913
1914
infoPtr->flags &= ~(WCBE_ACTEDIT | WCBE_EDITCHG);
1915
if (COMBOEX_NotifyEndEdit (infoPtr, &cbeend, edit_text)) return 0;
1916
}
1917
/* possible CB_GETCURSEL */
1918
InvalidateRect (infoPtr->hwndCombo, 0, 0);
1919
if (focusedhwnd)
1920
SendMessageW (infoPtr->hwndCombo, WM_KILLFOCUS,
1921
(WPARAM)focusedhwnd, 0);
1922
return 0;
1923
1924
case EN_SETFOCUS: {
1925
NMHDR hdr;
1926
1927
SendMessageW (infoPtr->hwndEdit, EM_SETSEL, 0, 0);
1928
SendMessageW (infoPtr->hwndEdit, EM_SETSEL, 0, -1);
1929
COMBOEX_Notify (infoPtr, CBEN_BEGINEDIT, &hdr);
1930
infoPtr->flags |= WCBE_ACTEDIT;
1931
infoPtr->flags &= ~WCBE_EDITCHG; /* no change yet */
1932
return 0;
1933
}
1934
1935
case EN_CHANGE: {
1936
LPCWSTR lastwrk;
1937
cmp_func_t cmptext = get_cmp_func(infoPtr);
1938
1939
INT_PTR selected = SendMessageW (infoPtr->hwndCombo,
1940
CB_GETCURSEL, 0, 0);
1941
1942
/* lstrlenW( lastworkingURL ) */
1943
1944
GetWindowTextW (infoPtr->hwndEdit, edit_text, 260);
1945
if (selected == -1) {
1946
lastwrk = infoPtr->edit.pszText;
1947
}
1948
else {
1949
CBE_ITEMDATA *item = COMBOEX_FindItem (infoPtr, selected);
1950
lastwrk = COMBOEX_GetText(infoPtr, item);
1951
}
1952
1953
TRACE("handling EN_CHANGE, selected = %Id, selected_text=%s\n", selected, debugstr_w(lastwrk));
1954
TRACE("handling EN_CHANGE, edittext=%s\n",
1955
debugstr_w(edit_text));
1956
1957
/* cmptext is between lastworkingURL and GetWindowText */
1958
if (cmptext (lastwrk, edit_text)) {
1959
/* strings not equal -- indicate edit has changed */
1960
infoPtr->flags |= WCBE_EDITCHG;
1961
}
1962
SendMessageW ( infoPtr->hwndNotify, WM_COMMAND,
1963
MAKEWPARAM(GetDlgCtrlID (infoPtr->hwndSelf),
1964
CBN_EDITCHANGE),
1965
(LPARAM)infoPtr->hwndSelf);
1966
return 0;
1967
}
1968
1969
case LBN_SELCHANGE:
1970
default:
1971
break;
1972
}/* fall through */
1973
default:
1974
;
1975
}
1976
1977
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
1978
}
1979
1980
1981
static LRESULT WINAPI
1982
COMBOEX_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1983
{
1984
COMBOEX_INFO *infoPtr = COMBOEX_GetInfoPtr (hwnd);
1985
1986
TRACE("hwnd %p, msg %x, wparam %Ix, lParam %Ix\n", hwnd, uMsg, wParam, lParam);
1987
1988
if (!infoPtr) {
1989
if (uMsg == WM_CREATE)
1990
return COMBOEX_Create (hwnd, (LPCREATESTRUCTA)lParam);
1991
if (uMsg == WM_NCCREATE)
1992
COMBOEX_NCCreate (hwnd);
1993
return DefWindowProcW (hwnd, uMsg, wParam, lParam);
1994
}
1995
1996
switch (uMsg)
1997
{
1998
case CBEM_DELETEITEM:
1999
return COMBOEX_DeleteItem (infoPtr, wParam);
2000
2001
case CBEM_GETCOMBOCONTROL:
2002
return (LRESULT)infoPtr->hwndCombo;
2003
2004
case CBEM_GETEDITCONTROL:
2005
return (LRESULT)infoPtr->hwndEdit;
2006
2007
case CBEM_GETEXTENDEDSTYLE:
2008
return infoPtr->dwExtStyle;
2009
2010
case CBEM_GETIMAGELIST:
2011
return (LRESULT)infoPtr->himl;
2012
2013
case CBEM_GETITEMA:
2014
return (LRESULT)COMBOEX_GetItemA (infoPtr, (COMBOBOXEXITEMA *)lParam);
2015
2016
case CBEM_GETITEMW:
2017
return (LRESULT)COMBOEX_GetItemW (infoPtr, (COMBOBOXEXITEMW *)lParam);
2018
2019
case CBEM_GETUNICODEFORMAT:
2020
return infoPtr->unicode;
2021
2022
case CBEM_HASEDITCHANGED:
2023
return COMBOEX_HasEditChanged (infoPtr);
2024
2025
case CBEM_INSERTITEMA:
2026
return COMBOEX_InsertItemA (infoPtr, (COMBOBOXEXITEMA *)lParam);
2027
2028
case CBEM_INSERTITEMW:
2029
return COMBOEX_InsertItemW (infoPtr, (COMBOBOXEXITEMW *)lParam);
2030
2031
case CBEM_SETEXSTYLE:
2032
case CBEM_SETEXTENDEDSTYLE:
2033
return COMBOEX_SetExtendedStyle (infoPtr, (DWORD)wParam, (DWORD)lParam);
2034
2035
case CBEM_SETIMAGELIST:
2036
return (LRESULT)COMBOEX_SetImageList (infoPtr, (HIMAGELIST)lParam);
2037
2038
case CBEM_SETITEMA:
2039
return COMBOEX_SetItemA (infoPtr, (COMBOBOXEXITEMA *)lParam);
2040
2041
case CBEM_SETITEMW:
2042
return COMBOEX_SetItemW (infoPtr, (COMBOBOXEXITEMW *)lParam);
2043
2044
case CBEM_SETUNICODEFORMAT:
2045
return COMBOEX_SetUnicodeFormat (infoPtr, wParam);
2046
2047
/*case CBEM_SETWINDOWTHEME:
2048
FIXME("CBEM_SETWINDOWTHEME: stub\n");*/
2049
2050
case WM_SETTEXT:
2051
case WM_GETTEXT:
2052
case WM_GETTEXTLENGTH:
2053
return SendMessageW(infoPtr->hwndEdit, uMsg, wParam, lParam);
2054
2055
case CB_GETLBTEXT:
2056
return COMBOEX_GetListboxText(infoPtr, wParam, (LPWSTR)lParam);
2057
2058
case CB_GETLBTEXTLEN:
2059
return COMBOEX_GetListboxText(infoPtr, wParam, NULL);
2060
2061
case CB_RESETCONTENT:
2062
COMBOEX_ResetContent(infoPtr);
2063
/* fall through */
2064
2065
/* Combo messages we are not sure if we need to process or just forward */
2066
case CB_GETDROPPEDCONTROLRECT:
2067
case CB_GETITEMHEIGHT:
2068
case CB_GETEXTENDEDUI:
2069
case CB_LIMITTEXT:
2070
case CB_SELECTSTRING:
2071
2072
/* Combo messages OK to just forward to the regular COMBO */
2073
case CB_GETCOUNT:
2074
case CB_GETCURSEL:
2075
case CB_GETDROPPEDSTATE:
2076
case CB_SETDROPPEDWIDTH:
2077
case CB_SETEXTENDEDUI:
2078
case CB_SHOWDROPDOWN:
2079
return SendMessageW (infoPtr->hwndCombo, uMsg, wParam, lParam);
2080
2081
/* Combo messages we need to process specially */
2082
case CB_FINDSTRINGEXACT:
2083
return COMBOEX_FindStringExact (infoPtr, (INT)wParam, (LPCWSTR)lParam);
2084
2085
case CB_GETITEMDATA:
2086
return COMBOEX_GetItemData (infoPtr, (INT)wParam);
2087
2088
case CB_SETCURSEL:
2089
return COMBOEX_SetCursel (infoPtr, (INT)wParam);
2090
2091
case CB_SETITEMDATA:
2092
return COMBOEX_SetItemData (infoPtr, (INT)wParam, (DWORD_PTR)lParam);
2093
2094
case CB_SETITEMHEIGHT:
2095
return COMBOEX_SetItemHeight (infoPtr, (INT)wParam, (UINT)lParam);
2096
2097
2098
2099
/* Window messages passed to parent */
2100
case WM_COMMAND:
2101
return COMBOEX_Command (infoPtr, wParam);
2102
2103
case WM_NOTIFY:
2104
if (infoPtr->NtfUnicode)
2105
return SendMessageW (infoPtr->hwndNotify, uMsg, wParam, lParam);
2106
else
2107
return SendMessageA (infoPtr->hwndNotify, uMsg, wParam, lParam);
2108
2109
2110
/* Window messages we need to process */
2111
case WM_DELETEITEM:
2112
return COMBOEX_WM_DeleteItem (infoPtr, (DELETEITEMSTRUCT *)lParam);
2113
2114
case WM_DRAWITEM:
2115
return COMBOEX_DrawItem (infoPtr, (DRAWITEMSTRUCT *)lParam);
2116
2117
case WM_DESTROY:
2118
return COMBOEX_Destroy (infoPtr);
2119
2120
case WM_ENABLE:
2121
return COMBOEX_Enable (infoPtr, (BOOL)wParam);
2122
2123
case WM_MEASUREITEM:
2124
return COMBOEX_MeasureItem (infoPtr, (MEASUREITEMSTRUCT *)lParam);
2125
2126
case WM_NOTIFYFORMAT:
2127
return COMBOEX_NotifyFormat (infoPtr, lParam);
2128
2129
case WM_SIZE:
2130
return COMBOEX_Size (infoPtr, LOWORD(lParam), HIWORD(lParam));
2131
2132
case WM_GETFONT:
2133
return (LRESULT)infoPtr->font;
2134
2135
case WM_SETFONT:
2136
return COMBOEX_SetFont( infoPtr, (HFONT)wParam, LOWORD(lParam) != 0 );
2137
2138
case WM_SETREDRAW:
2139
return COMBOEX_SetRedraw(infoPtr, wParam, lParam);
2140
2141
case WM_WINDOWPOSCHANGING:
2142
return COMBOEX_WindowPosChanging (infoPtr, (WINDOWPOS *)lParam);
2143
2144
case WM_SETFOCUS:
2145
if (infoPtr->hwndEdit) SetFocus( infoPtr->hwndEdit );
2146
else SetFocus( infoPtr->hwndCombo );
2147
return 0;
2148
2149
case WM_SYSCOLORCHANGE:
2150
COMCTL32_RefreshSysColors();
2151
return 0;
2152
2153
default:
2154
if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
2155
ERR("unknown msg %04x, wp %#Ix, lp %#Ix\n",uMsg,wParam,lParam);
2156
return DefWindowProcW (hwnd, uMsg, wParam, lParam);
2157
}
2158
}
2159
2160
2161
void COMBOEX_Register (void)
2162
{
2163
WNDCLASSW wndClass;
2164
2165
ZeroMemory (&wndClass, sizeof(WNDCLASSW));
2166
wndClass.style = CS_GLOBALCLASS;
2167
wndClass.lpfnWndProc = COMBOEX_WindowProc;
2168
wndClass.cbClsExtra = 0;
2169
wndClass.cbWndExtra = sizeof(COMBOEX_INFO *);
2170
wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
2171
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
2172
wndClass.lpszClassName = WC_COMBOBOXEXW;
2173
2174
RegisterClassW (&wndClass);
2175
}
2176
2177
2178
void COMBOEX_Unregister (void)
2179
{
2180
UnregisterClassW (WC_COMBOBOXEXW, NULL);
2181
}
2182
2183