fdopen: don't rewind the file after creating the FILE* handle. Added
[wine] / dlls / comctl32 / comctl32undoc.c
1 /*
2  * Undocumented functions from COMCTL32.DLL
3  *
4  * Copyright 1998 Eric Kohl
5  *           1998 Juergen Schmied <j.schmied@metronet.de>
6  *           2000 Eric Kohl for CodeWeavers
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  * NOTES
23  *     All of these functions are UNDOCUMENTED!! And I mean UNDOCUMENTED!!!!
24  *     Do NOT rely on names or contents of undocumented structures and types!!!
25  *     These functions are used by EXPLORER.EXE, IEXPLORE.EXE and
26  *     COMCTL32.DLL (internally).
27  *
28  * TODO
29  *     - Add more functions.
30  *     - Write some documentation.
31  */
32 #include "config.h"
33 #include "wine/port.h"
34
35 #include <string.h>
36 #include <stdlib.h> /* atoi */
37 #include <ctype.h>
38 #include <limits.h>
39
40 #include "commctrl.h"
41 #include "objbase.h"
42 #include "winbase.h"
43 #include "winerror.h"
44 #include "winreg.h"
45
46 #include "wine/unicode.h"
47 #include "comctl32.h"
48
49 #include "wine/debug.h"
50
51 WINE_DEFAULT_DEBUG_CHANNEL(commctrl);
52
53
54 extern HANDLE COMCTL32_hHeap; /* handle to the private heap */
55
56
57 typedef struct _STREAMDATA
58 {
59     DWORD dwSize;
60     DWORD dwData2;
61     DWORD dwItems;
62 } STREAMDATA, *PSTREAMDATA;
63
64 typedef struct _LOADDATA
65 {
66     INT   nCount;
67     PVOID ptr;
68 } LOADDATA, *LPLOADDATA;
69
70 typedef HRESULT (CALLBACK *DPALOADPROC)(LPLOADDATA,IStream*,LPARAM);
71
72 /**************************************************************************
73  * DPA_LoadStream [COMCTL32.9]
74  *
75  * Loads a dynamic pointer array from a stream
76  *
77  * PARAMS
78  *     phDpa    [O] pointer to a handle to a dynamic pointer array
79  *     loadProc [I] pointer to a callback function
80  *     pStream  [I] pointer to a stream
81  *     lParam   [I] application specific value
82  *
83  * NOTES
84  *     No more information available yet!
85  */
86
87 HRESULT WINAPI
88 DPA_LoadStream (HDPA *phDpa, DPALOADPROC loadProc, IStream *pStream, LPARAM lParam)
89 {
90     HRESULT errCode;
91     LARGE_INTEGER position;
92     ULARGE_INTEGER newPosition;
93     STREAMDATA  streamData;
94     LOADDATA loadData;
95     ULONG ulRead;
96     HDPA hDpa;
97     PVOID *ptr;
98
99     FIXME ("phDpa=%p loadProc=%p pStream=%p lParam=%lx\n",
100            phDpa, loadProc, pStream, lParam);
101
102     if (!phDpa || !loadProc || !pStream)
103         return E_INVALIDARG;
104
105     *phDpa = (HDPA)NULL;
106
107     position.s.LowPart = 0;
108     position.s.HighPart = 0;
109
110     /*
111      * Zero out our streamData
112      */
113     memset(&streamData,0,sizeof(STREAMDATA));
114
115     errCode = IStream_Seek (pStream, position, STREAM_SEEK_CUR, &newPosition);
116     if (errCode != S_OK)
117         return errCode;
118
119     errCode = IStream_Read (pStream, &streamData, sizeof(STREAMDATA), &ulRead);
120     if (errCode != S_OK)
121         return errCode;
122
123     FIXME ("dwSize=%lu dwData2=%lu dwItems=%lu\n",
124            streamData.dwSize, streamData.dwData2, streamData.dwItems);
125
126     if ( ulRead < sizeof(STREAMDATA) ||
127     lParam < sizeof(STREAMDATA) ||
128         streamData.dwSize < sizeof(STREAMDATA) ||
129         streamData.dwData2 < 1) {
130         errCode = E_FAIL;
131     }
132
133     if (streamData.dwItems > (UINT_MAX / 2 / sizeof(VOID*))) /* 536870911 */
134         return E_OUTOFMEMORY;
135
136     /* create the dpa */
137     hDpa = DPA_Create (streamData.dwItems);
138     if (!hDpa)
139         return E_OUTOFMEMORY;
140
141     if (!DPA_Grow (hDpa, streamData.dwItems))
142         return E_OUTOFMEMORY;
143
144     /* load data from the stream into the dpa */
145     ptr = hDpa->ptrs;
146     for (loadData.nCount = 0; loadData.nCount < streamData.dwItems; loadData.nCount++) {
147         errCode = (loadProc)(&loadData, pStream, lParam);
148         if (errCode != S_OK) {
149             errCode = S_FALSE;
150             break;
151         }
152
153         *ptr = loadData.ptr;
154         ptr++;
155     }
156
157     /* set the number of items */
158     hDpa->nItemCount = loadData.nCount;
159
160     /* store the handle to the dpa */
161     *phDpa = hDpa;
162     FIXME ("new hDpa=%p\n", hDpa);
163
164     return errCode;
165 }
166
167
168 /**************************************************************************
169  * DPA_SaveStream [COMCTL32.10]
170  *
171  * Saves a dynamic pointer array to a stream
172  *
173  * PARAMS
174  *     hDpa     [I] handle to a dynamic pointer array
175  *     loadProc [I] pointer to a callback function
176  *     pStream  [I] pointer to a stream
177  *     lParam   [I] application specific value
178  *
179  * NOTES
180  *     No more information available yet!
181  */
182
183 HRESULT WINAPI
184 DPA_SaveStream (const HDPA hDpa, DPALOADPROC loadProc, IStream *pStream, LPARAM lParam)
185 {
186
187     FIXME ("hDpa=%p loadProc=%p pStream=%p lParam=%lx\n",
188            hDpa, loadProc, pStream, lParam);
189
190     return E_FAIL;
191 }
192
193
194 /**************************************************************************
195  * DPA_Merge [COMCTL32.11]
196  *
197  * PARAMS
198  *     hdpa1       [I] handle to a dynamic pointer array
199  *     hdpa2       [I] handle to a dynamic pointer array
200  *     dwFlags     [I] flags
201  *     pfnCompare  [I] pointer to sort function
202  *     pfnMerge    [I] pointer to merge function
203  *     lParam      [I] application specific value
204  *
205  * NOTES
206  *     No more information available yet!
207  */
208
209 BOOL WINAPI
210 DPA_Merge (const HDPA hdpa1, const HDPA hdpa2, DWORD dwFlags,
211            PFNDPACOMPARE pfnCompare, PFNDPAMERGE pfnMerge, LPARAM lParam)
212 {
213     INT nCount;
214     LPVOID *pWork1, *pWork2;
215     INT nResult, i;
216     INT nIndex;
217
218     TRACE("%p %p %08lx %p %p %08lx)\n",
219            hdpa1, hdpa2, dwFlags, pfnCompare, pfnMerge, lParam);
220
221     if (IsBadWritePtr (hdpa1, sizeof(DPA)))
222         return FALSE;
223
224     if (IsBadWritePtr (hdpa2, sizeof(DPA)))
225         return FALSE;
226
227     if (IsBadCodePtr ((FARPROC)pfnCompare))
228         return FALSE;
229
230     if (IsBadCodePtr ((FARPROC)pfnMerge))
231         return FALSE;
232
233     if (!(dwFlags & DPAM_NOSORT)) {
234         TRACE("sorting dpa's!\n");
235         if (hdpa1->nItemCount > 0)
236         DPA_Sort (hdpa1, pfnCompare, lParam);
237         TRACE ("dpa 1 sorted!\n");
238         if (hdpa2->nItemCount > 0)
239         DPA_Sort (hdpa2, pfnCompare, lParam);
240         TRACE ("dpa 2 sorted!\n");
241     }
242
243     if (hdpa2->nItemCount < 1)
244         return TRUE;
245
246     TRACE("hdpa1->nItemCount=%d hdpa2->nItemCount=%d\n",
247            hdpa1->nItemCount, hdpa2->nItemCount);
248
249
250     /* working but untrusted implementation */
251
252     pWork1 = &(hdpa1->ptrs[hdpa1->nItemCount - 1]);
253     pWork2 = &(hdpa2->ptrs[hdpa2->nItemCount - 1]);
254
255     nIndex = hdpa1->nItemCount - 1;
256     nCount = hdpa2->nItemCount - 1;
257
258     do
259     {
260         if (nIndex < 0) {
261             if ((nCount >= 0) && (dwFlags & DPAM_INSERT)) {
262                 /* Now insert the remaining new items into DPA 1 */
263                 TRACE("%d items to be inserted at start of DPA 1\n",
264                       nCount+1);
265                 for (i=nCount; i>=0; i--) {
266                     PVOID ptr;
267
268                     ptr = (pfnMerge)(3, *pWork2, NULL, lParam);
269                     if (!ptr)
270                         return FALSE;
271                     DPA_InsertPtr (hdpa1, 0, ptr);
272                     pWork2--;
273                 }
274             }
275             break;
276         }
277         nResult = (pfnCompare)(*pWork1, *pWork2, lParam);
278         TRACE("compare result=%d, dpa1.cnt=%d, dpa2.cnt=%d\n",
279               nResult, nIndex, nCount);
280
281         if (nResult == 0)
282         {
283             PVOID ptr;
284
285             ptr = (pfnMerge)(1, *pWork1, *pWork2, lParam);
286             if (!ptr)
287                 return FALSE;
288
289             nCount--;
290             pWork2--;
291             *pWork1 = ptr;
292             nIndex--;
293             pWork1--;
294         }
295         else if (nResult > 0)
296         {
297             /* item in DPA 1 missing from DPA 2 */
298             if (dwFlags & DPAM_DELETE)
299             {
300                 /* Now delete the extra item in DPA1 */
301                 PVOID ptr;
302
303                 ptr = DPA_DeletePtr (hdpa1, hdpa1->nItemCount - 1);
304
305                 (pfnMerge)(2, ptr, NULL, lParam);
306             }
307             nIndex--;
308             pWork1--;
309         }
310         else
311         {
312             /* new item in DPA 2 */
313             if (dwFlags & DPAM_INSERT)
314             {
315                 /* Now insert the new item in DPA 1 */
316                 PVOID ptr;
317
318                 ptr = (pfnMerge)(3, *pWork2, NULL, lParam);
319                 if (!ptr)
320                     return FALSE;
321                 DPA_InsertPtr (hdpa1, nIndex+1, ptr);
322             }
323             nCount--;
324             pWork2--;
325         }
326
327     }
328     while (nCount >= 0);
329
330     return TRUE;
331 }
332
333
334 /**************************************************************************
335  * Alloc [COMCTL32.71]
336  *
337  * Allocates memory block from the dll's private heap
338  *
339  * PARAMS
340  *     dwSize [I] size of the allocated memory block
341  *
342  * RETURNS
343  *     Success: pointer to allocated memory block
344  *     Failure: NULL
345  */
346
347 LPVOID WINAPI
348 COMCTL32_Alloc (DWORD dwSize)
349 {
350     LPVOID lpPtr;
351
352     TRACE("(0x%lx)\n", dwSize);
353
354     lpPtr = HeapAlloc (COMCTL32_hHeap, HEAP_ZERO_MEMORY, dwSize);
355
356     TRACE("-- ret=%p\n", lpPtr);
357
358     return lpPtr;
359 }
360
361
362 /**************************************************************************
363  * ReAlloc [COMCTL32.72]
364  *
365  * Changes the size of an allocated memory block or allocates a memory
366  * block using the dll's private heap.
367  *
368  * PARAMS
369  *     lpSrc  [I] pointer to memory block which will be resized
370  *     dwSize [I] new size of the memory block.
371  *
372  * RETURNS
373  *     Success: pointer to the resized memory block
374  *     Failure: NULL
375  *
376  * NOTES
377  *     If lpSrc is a NULL-pointer, then COMCTL32_ReAlloc allocates a memory
378  *     block like COMCTL32_Alloc.
379  */
380
381 LPVOID WINAPI
382 COMCTL32_ReAlloc (LPVOID lpSrc, DWORD dwSize)
383 {
384     LPVOID lpDest;
385
386     TRACE("(%p 0x%08lx)\n", lpSrc, dwSize);
387
388     if (lpSrc)
389         lpDest = HeapReAlloc (COMCTL32_hHeap, HEAP_ZERO_MEMORY, lpSrc, dwSize);
390     else
391         lpDest = HeapAlloc (COMCTL32_hHeap, HEAP_ZERO_MEMORY, dwSize);
392
393     TRACE("-- ret=%p\n", lpDest);
394
395     return lpDest;
396 }
397
398
399 /**************************************************************************
400  * Free [COMCTL32.73]
401  *
402  * Frees an allocated memory block from the dll's private heap.
403  *
404  * PARAMS
405  *     lpMem [I] pointer to memory block which will be freed
406  *
407  * RETURNS
408  *     Success: TRUE
409  *     Failure: FALSE
410  */
411
412 BOOL WINAPI
413 COMCTL32_Free (LPVOID lpMem)
414 {
415     TRACE("(%p)\n", lpMem);
416
417     return HeapFree (COMCTL32_hHeap, 0, lpMem);
418 }
419
420
421 /**************************************************************************
422  * GetSize [COMCTL32.74]
423  *
424  * Retrieves the size of the specified memory block from the dll's
425  * private heap.
426  *
427  * PARAMS
428  *     lpMem [I] pointer to an allocated memory block
429  *
430  * RETURNS
431  *     Success: size of the specified memory block
432  *     Failure: 0
433  */
434
435 DWORD WINAPI
436 COMCTL32_GetSize (LPVOID lpMem)
437 {
438     TRACE("(%p)\n", lpMem);
439
440     return HeapSize (COMCTL32_hHeap, 0, lpMem);
441 }
442
443
444 /**************************************************************************
445  * The MRU-API is a set of functions to manipulate MRU(Most Recently Used)
446  * lists.
447  *
448  * Stored in the reg. as a set of values under a single key.  Each item in the
449  * list has a value name that is a single char. 'a' - 'z', '{', '|' or '}'.
450  * The order of the list is stored with value name 'MRUList' which is a string
451  * containing the value names (i.e. 'a', 'b', etc.) in the relevant order.
452  */
453
454 typedef struct tagCREATEMRULISTA
455 {
456     DWORD  cbSize;        /* size of struct */
457     DWORD  nMaxItems;     /* max no. of items in list */
458     DWORD  dwFlags;       /* see below */
459     HKEY   hKey;          /* root reg. key under which list is saved */
460     LPCSTR lpszSubKey;    /* reg. subkey */
461     PROC   lpfnCompare;   /* item compare proc */
462 } CREATEMRULISTA, *LPCREATEMRULISTA;
463
464 typedef struct tagCREATEMRULISTW
465 {
466     DWORD   cbSize;        /* size of struct */
467     DWORD   nMaxItems;     /* max no. of items in list */
468     DWORD   dwFlags;       /* see below */
469     HKEY    hKey;          /* root reg. key under which list is saved */
470     LPCWSTR lpszSubKey;    /* reg. subkey */
471     PROC    lpfnCompare;   /* item compare proc */
472 } CREATEMRULISTW, *LPCREATEMRULISTW;
473
474 /* dwFlags */
475 #define MRUF_STRING_LIST  0 /* list will contain strings */
476 #define MRUF_BINARY_LIST  1 /* list will contain binary data */
477 #define MRUF_DELAYED_SAVE 2 /* only save list order to reg. is FreeMRUList */
478
479 /* If list is a string list lpfnCompare has the following prototype
480  * int CALLBACK MRUCompareString(LPCSTR s1, LPCSTR s2)
481  * for binary lists the prototype is
482  * int CALLBACK MRUCompareBinary(LPCVOID data1, LPCVOID data2, DWORD cbData)
483  * where cbData is the no. of bytes to compare.
484  * Need to check what return value means identical - 0?
485  */
486
487 typedef struct tagWINEMRUITEM
488 {
489     DWORD          size;        /* size of data stored               */
490     DWORD          itemFlag;    /* flags                             */
491     BYTE           datastart;
492 } WINEMRUITEM, *LPWINEMRUITEM;
493
494 /* itemFlag */
495 #define WMRUIF_CHANGED   0x0001 /* this dataitem changed             */
496
497 typedef struct tagWINEMRULIST
498 {
499     CREATEMRULISTW extview;     /* original create information       */
500     BOOL           isUnicode;   /* is compare fn Unicode */
501     DWORD          wineFlags;   /* internal flags                    */
502     DWORD          cursize;     /* current size of realMRU           */
503     LPSTR          realMRU;     /* pointer to string of index names  */
504     LPWINEMRUITEM  *array;      /* array of pointers to data         */
505                                 /* in 'a' to 'z' order               */
506 } WINEMRULIST, *LPWINEMRULIST;
507
508 /* wineFlags */
509 #define WMRUF_CHANGED  0x0001   /* MRU list has changed              */
510
511 /**************************************************************************
512  *              MRU_SaveChanged - Localize MRU saving code
513  *
514  */
515 VOID MRU_SaveChanged( LPWINEMRULIST mp )
516 {
517     UINT i, err;
518     HKEY newkey;
519     WCHAR realname[2];
520     LPWINEMRUITEM witem;
521     WCHAR emptyW[] = {'\0'};
522
523     /* or should we do the following instead of RegOpenKeyEx:
524      */
525
526     /* open the sub key */
527     if ((err = RegOpenKeyExW( mp->extview.hKey, mp->extview.lpszSubKey,
528                               0, KEY_WRITE, &newkey))) {
529         /* not present - what to do ??? */
530         ERR("Can not open key, error=%d, attempting to create\n",
531             err);
532         if ((err = RegCreateKeyExW( mp->extview.hKey, mp->extview.lpszSubKey,
533                                     0,
534                                     emptyW,
535                                     REG_OPTION_NON_VOLATILE,
536                                     KEY_READ | KEY_WRITE,
537                                     0,
538                                     &newkey,
539                                     0))) {
540             ERR("failed to create key /%s/, err=%d\n",
541                 debugstr_w(mp->extview.lpszSubKey), err);
542             return;
543         }
544     }
545     if (mp->wineFlags & WMRUF_CHANGED) {
546         mp->wineFlags &= ~WMRUF_CHANGED;
547         err = RegSetValueExA(newkey, "MRUList", 0, REG_SZ,
548                              mp->realMRU, strlen(mp->realMRU) + 1);
549         if (err) {
550             ERR("error saving MRUList, err=%d\n", err);
551         }
552         TRACE("saving MRUList=/%s/\n", mp->realMRU);
553     }
554     realname[1] = 0;
555     for(i=0; i<mp->cursize; i++) {
556         witem = mp->array[i];
557         if (witem->itemFlag & WMRUIF_CHANGED) {
558             witem->itemFlag &= ~WMRUIF_CHANGED;
559             realname[0] = 'a' + i;
560             err = RegSetValueExW(newkey, realname, 0,
561                                  (mp->extview.dwFlags & MRUF_BINARY_LIST) ?
562                                  REG_BINARY : REG_SZ,
563                                  &witem->datastart, witem->size);
564             if (err) {
565                 ERR("error saving /%s/, err=%d\n", debugstr_w(realname), err);
566             }
567             TRACE("saving value for name /%s/ size=%ld\n",
568                   debugstr_w(realname), witem->size);
569         }
570     }
571     RegCloseKey( newkey );
572 }
573
574 /**************************************************************************
575  *              FreeMRUList [COMCTL32.152]
576  *
577  * PARAMS
578  *     hMRUList [I] Handle to list.
579  *
580  */
581 DWORD WINAPI
582 FreeMRUList (HANDLE hMRUList)
583 {
584     LPWINEMRULIST mp = (LPWINEMRULIST)hMRUList;
585     UINT i;
586
587     TRACE("\n");
588     if (mp->wineFlags & WMRUF_CHANGED) {
589         /* need to open key and then save the info */
590         MRU_SaveChanged( mp );
591     }
592
593     for(i=0; i<mp->extview.nMaxItems; i++) {
594         if (mp->array[i])
595             COMCTL32_Free(mp->array[i]);
596     }
597     COMCTL32_Free(mp->realMRU);
598     COMCTL32_Free(mp->array);
599     COMCTL32_Free((LPWSTR)mp->extview.lpszSubKey);
600     return COMCTL32_Free(mp);
601 }
602
603
604 /**************************************************************************
605  *                  FindMRUData [COMCTL32.169]
606  *
607  * Searches binary list for item that matches lpData of length cbData.
608  * Returns position in list order 0 -> MRU and if lpRegNum != NULL then value
609  * corresponding to item's reg. name will be stored in it ('a' -> 0).
610  *
611  * PARAMS
612  *    hList [I] list handle
613  *    lpData [I] data to find
614  *    cbData [I] length of data
615  *    lpRegNum [O] position in registry (maybe NULL)
616  *
617  * RETURNS
618  *    Position in list 0 -> MRU.  -1 if item not found.
619  */
620 INT WINAPI
621 FindMRUData (HANDLE hList, LPCVOID lpData, DWORD cbData, LPINT lpRegNum)
622 {
623     LPWINEMRULIST mp = (LPWINEMRULIST)hList;
624     UINT i, ret;
625     LPSTR dataA = NULL;
626
627     if (!mp->extview.lpfnCompare) {
628         ERR("MRU list not properly created. No compare procedure.\n");
629         return -1;
630     }
631
632     if(!(mp->extview.dwFlags & MRUF_BINARY_LIST) && !mp->isUnicode) {
633         DWORD len = WideCharToMultiByte(CP_ACP, 0, lpData, -1,
634                                         NULL, 0, NULL, NULL);
635         dataA = COMCTL32_Alloc(len);
636         WideCharToMultiByte(CP_ACP, 0, lpData, -1, dataA, len, NULL, NULL);
637     }
638
639     for(i=0; i<mp->cursize; i++) {
640         if (mp->extview.dwFlags & MRUF_BINARY_LIST) {
641             if (!mp->extview.lpfnCompare(lpData, &mp->array[i]->datastart,
642                                          cbData))
643                 break;
644         }
645         else {
646             if(mp->isUnicode) {
647                 if (!mp->extview.lpfnCompare(lpData, &mp->array[i]->datastart))
648                     break;
649             } else {
650                 DWORD len = WideCharToMultiByte(CP_ACP, 0,
651                                                 (LPWSTR)&mp->array[i]->datastart, -1,
652                                                 NULL, 0, NULL, NULL);
653                 LPSTR itemA = COMCTL32_Alloc(len);
654                 INT cmp;
655                 WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&mp->array[i]->datastart, -1,
656                                     itemA, len, NULL, NULL);
657
658                 cmp = mp->extview.lpfnCompare(dataA, itemA);
659                 COMCTL32_Free(itemA);
660                 if(!cmp)
661                     break;
662             }
663         }
664     }
665     if(dataA)
666         COMCTL32_Free(dataA);
667     if (i < mp->cursize)
668         ret = i;
669     else
670         ret = -1;
671     if (lpRegNum && (ret != -1))
672         *lpRegNum = 'a' + i;
673
674     TRACE("(%p, %p, %ld, %p) returning %d\n",
675            hList, lpData, cbData, lpRegNum, ret);
676
677     return ret;
678 }
679
680
681 /**************************************************************************
682  *              AddMRUData [COMCTL32.167]
683  *
684  * Add item to MRU binary list.  If item already exists in list then it is
685  * simply moved up to the top of the list and not added again.  If list is
686  * full then the least recently used item is removed to make room.
687  *
688  * PARAMS
689  *     hList [I] Handle to list.
690  *     lpData [I] ptr to data to add.
691  *     cbData [I] no. of bytes of data.
692  *
693  * RETURNS
694  *     No. corresponding to registry name where value is stored 'a' -> 0 etc.
695  *     -1 on error.
696  */
697 INT WINAPI
698 AddMRUData (HANDLE hList, LPCVOID lpData, DWORD cbData)
699 {
700     LPWINEMRULIST mp = (LPWINEMRULIST)hList;
701     LPWINEMRUITEM witem;
702     INT i, replace, ret;
703
704     if ((replace = FindMRUData (hList, lpData, cbData, NULL)) < 0) {
705         /* either add a new entry or replace oldest */
706         if (mp->cursize < mp->extview.nMaxItems) {
707             /* Add in a new item */
708             replace = mp->cursize;
709             mp->cursize++;
710         }
711         else {
712             /* get the oldest entry and replace data */
713             replace = mp->realMRU[mp->cursize - 1] - 'a';
714             COMCTL32_Free(mp->array[replace]);
715         }
716     }
717     else {
718         /* free up the old data */
719         COMCTL32_Free(mp->array[replace]);
720     }
721
722     /* Allocate space for new item and move in the data */
723     mp->array[replace] = witem = (LPWINEMRUITEM)COMCTL32_Alloc(cbData +
724                                                                sizeof(WINEMRUITEM));
725     witem->itemFlag |= WMRUIF_CHANGED;
726     witem->size = cbData;
727     memcpy( &witem->datastart, lpData, cbData);
728
729     /* now rotate MRU list */
730     mp->wineFlags |= WMRUF_CHANGED;
731     for(i=mp->cursize-1; i>=1; i--) {
732         mp->realMRU[i] = mp->realMRU[i-1];
733     }
734     mp->realMRU[0] = replace + 'a';
735     TRACE("(%p, %p, %ld) adding data, /%c/ now most current\n",
736           hList, lpData, cbData, replace+'a');
737     ret = replace;
738
739     if (!(mp->extview.dwFlags & MRUF_DELAYED_SAVE)) {
740         /* save changed stuff right now */
741         MRU_SaveChanged( mp );
742     }
743
744     return ret;
745 }
746
747 /**************************************************************************
748  *              AddMRUStringW [COMCTL32.401]
749  *
750  * Add item to MRU string list.  If item already exists in list them it is
751  * simply moved up to the top of the list and not added again.  If list is
752  * full then the least recently used item is removed to make room.
753  *
754  * PARAMS
755  *     hList [I] Handle to list.
756  *     lpszString [I] ptr to string to add.
757  *
758  * RETURNS
759  *     No. corresponding to registry name where value is stored 'a' -> 0 etc.
760  *     -1 on error.
761  */
762 INT WINAPI
763 AddMRUStringW(HANDLE hList, LPCWSTR lpszString)
764 {
765     FIXME("(%p, %s) empty stub!\n", hList, debugstr_w(lpszString));
766
767     return 0;
768 }
769
770 /**************************************************************************
771  *              AddMRUStringA [COMCTL32.153]
772  */
773 INT WINAPI
774 AddMRUStringA(HANDLE hList, LPCSTR lpszString)
775 {
776     FIXME("(%p, %s) empty stub!\n", hList, debugstr_a(lpszString));
777
778     return 0;
779 }
780
781 /**************************************************************************
782  *              DelMRUString [COMCTL32.156]
783  *
784  * Removes item from either string or binary list (despite its name)
785  *
786  * PARAMS
787  *    hList [I] list handle
788  *    nItemPos [I] item position to remove 0 -> MRU
789  *
790  * RETURNS
791  *    TRUE if successful, FALSE if nItemPos is out of range.
792  */
793 BOOL WINAPI
794 DelMRUString(HANDLE hList, INT nItemPos)
795 {
796     FIXME("(%p, %d): stub\n", hList, nItemPos);
797     return TRUE;
798 }
799
800 /**************************************************************************
801  *                  FindMRUStringW [COMCTL32.402]
802  */
803 INT WINAPI
804 FindMRUStringW (HANDLE hList, LPCWSTR lpszString, LPINT lpRegNum)
805 {
806   FIXME("stub\n");
807   return -1;
808 }
809
810 /**************************************************************************
811  *                  FindMRUStringA [COMCTL32.155]
812  *
813  * Searches string list for item that matches lpszString.
814  * Returns position in list order 0 -> MRU and if lpRegNum != NULL then value
815  * corresponding to item's reg. name will be stored in it ('a' -> 0).
816  *
817  * PARAMS
818  *    hList [I] list handle
819  *    lpszString [I] string to find
820  *    lpRegNum [O] position in registry (maybe NULL)
821  *
822  * RETURNS
823  *    Position in list 0 -> MRU.  -1 if item not found.
824  */
825 INT WINAPI
826 FindMRUStringA (HANDLE hList, LPCSTR lpszString, LPINT lpRegNum)
827 {
828     DWORD len = MultiByteToWideChar(CP_ACP, 0, lpszString, -1, NULL, 0);
829     LPWSTR stringW = COMCTL32_Alloc(len * sizeof(WCHAR));
830     INT ret;
831
832     MultiByteToWideChar(CP_ACP, 0, lpszString, -1, stringW, len);
833     ret = FindMRUData(hList, stringW, len * sizeof(WCHAR), lpRegNum);
834     COMCTL32_Free(stringW);
835     return ret;
836 }
837
838 /*************************************************************************
839  *                 CreateMRUListLazy_common
840  */
841 HANDLE CreateMRUListLazy_common(LPWINEMRULIST mp)
842 {
843     UINT i, err;
844     HKEY newkey;
845     DWORD datasize, dwdisp;
846     WCHAR realname[2];
847     LPWINEMRUITEM witem;
848     DWORD type;
849     WCHAR emptyW[] = {'\0'};
850
851     /* get space to save indices that will turn into names
852      * but in order of most to least recently used
853      */
854     mp->realMRU = (LPSTR) COMCTL32_Alloc(mp->extview.nMaxItems + 2);
855
856     /* get space to save pointers to actual data in order of
857      * 'a' to 'z' (0 to n).
858      */
859     mp->array = (LPVOID) COMCTL32_Alloc(mp->extview.nMaxItems *
860                                         sizeof(LPVOID));
861
862     /* open the sub key */
863     if ((err = RegCreateKeyExW( mp->extview.hKey, mp->extview.lpszSubKey,
864                                 0,
865                                 emptyW,
866                                 REG_OPTION_NON_VOLATILE,
867                                 KEY_READ | KEY_WRITE,
868                                 0,
869                                 &newkey,
870                                 &dwdisp))) {
871         /* error - what to do ??? */
872         ERR("(%lu %lu %lx %lx \"%s\" %p): Can not open key, error=%d\n",
873             mp->extview.cbSize, mp->extview.nMaxItems, mp->extview.dwFlags,
874             (DWORD)mp->extview.hKey, debugstr_w(mp->extview.lpszSubKey),
875                                  mp->extview.lpfnCompare, err);
876         return 0;
877     }
878
879     /* get values from key 'MRUList' */
880     if (newkey) {
881         datasize = mp->extview.nMaxItems + 1;
882         if((err=RegQueryValueExA( newkey, "MRUList", 0, &type, mp->realMRU,
883                                   &datasize))) {
884             /* not present - set size to 1 (will become 0 later) */
885             datasize = 1;
886             *mp->realMRU = 0;
887         }
888
889         TRACE("MRU list = %s\n", mp->realMRU);
890
891         mp->cursize = datasize - 1;
892         /* datasize now has number of items in the MRUList */
893
894         /* get actual values for each entry */
895         realname[1] = 0;
896         for(i=0; i<mp->cursize; i++) {
897             realname[0] = 'a' + i;
898             if(RegQueryValueExW( newkey, realname, 0, &type, 0, &datasize)) {
899                 /* not present - what to do ??? */
900                 ERR("Key %s not found 1\n", debugstr_w(realname));
901             }
902             mp->array[i] = witem = (LPWINEMRUITEM)COMCTL32_Alloc(datasize +
903                                                                  sizeof(WINEMRUITEM));
904             witem->size = datasize;
905             if(RegQueryValueExW( newkey, realname, 0, &type,
906                                  &witem->datastart, &datasize)) {
907                 /* not present - what to do ??? */
908                 ERR("Key %s not found 2\n", debugstr_w(realname));
909             }
910         }
911         RegCloseKey( newkey );
912     }
913     else
914         mp->cursize = 0;
915
916     TRACE("(%lu %lu %lx %lx \"%s\" %p): Current Size = %ld\n",
917           mp->extview.cbSize, mp->extview.nMaxItems, mp->extview.dwFlags,
918           (DWORD)mp->extview.hKey, debugstr_w(mp->extview.lpszSubKey),
919           mp->extview.lpfnCompare, mp->cursize);
920     return (HANDLE)mp;
921 }
922
923 /**************************************************************************
924  *                  CreateMRUListLazyW [COMCTL32.404]
925  */
926 HANDLE WINAPI
927 CreateMRUListLazyW (LPCREATEMRULISTW lpcml, DWORD dwParam2, DWORD dwParam3, DWORD dwParam4)
928 {
929     LPWINEMRULIST mp;
930
931     if (lpcml == NULL)
932         return 0;
933
934     if (lpcml->cbSize < sizeof(CREATEMRULISTW))
935         return 0;
936
937     mp = (LPWINEMRULIST) COMCTL32_Alloc(sizeof(WINEMRULIST));
938     memcpy(&mp->extview, lpcml, sizeof(CREATEMRULISTW));
939     mp->extview.lpszSubKey = COMCTL32_Alloc((strlenW(lpcml->lpszSubKey) + 1) *
940                                             sizeof(WCHAR));
941     strcpyW((LPWSTR)mp->extview.lpszSubKey, lpcml->lpszSubKey);
942     mp->isUnicode = TRUE;
943
944     return CreateMRUListLazy_common(mp);
945 }
946
947 /**************************************************************************
948  *                  CreateMRUListLazyA [COMCTL32.157]
949  */
950 HANDLE WINAPI
951 CreateMRUListLazyA (LPCREATEMRULISTA lpcml, DWORD dwParam2, DWORD dwParam3, DWORD dwParam4)
952 {
953     LPWINEMRULIST mp;
954     DWORD len;
955
956     if (lpcml == NULL)
957         return 0;
958
959     if (lpcml->cbSize < sizeof(CREATEMRULISTA))
960         return 0;
961
962     mp = (LPWINEMRULIST) COMCTL32_Alloc(sizeof(WINEMRULIST));
963     memcpy(&mp->extview, lpcml, sizeof(CREATEMRULISTW));
964     len = MultiByteToWideChar(CP_ACP, 0, lpcml->lpszSubKey, -1, NULL, 0);
965     mp->extview.lpszSubKey = COMCTL32_Alloc(len * sizeof(WCHAR));
966     MultiByteToWideChar(CP_ACP, 0, lpcml->lpszSubKey, -1,
967                         (LPWSTR)mp->extview.lpszSubKey, len);
968     mp->isUnicode = FALSE;
969     return CreateMRUListLazy_common(mp);
970 }
971
972 /**************************************************************************
973  *              CreateMRUListW [COMCTL32.400]
974  *
975  * PARAMS
976  *     lpcml [I] ptr to CREATEMRULIST structure.
977  *
978  * RETURNS
979  *     Handle to MRU list.
980  */
981 HANDLE WINAPI
982 CreateMRUListW (LPCREATEMRULISTW lpcml)
983 {
984     return CreateMRUListLazyW(lpcml, 0, 0, 0);
985 }
986
987 /**************************************************************************
988  *              CreateMRUListA [COMCTL32.151]
989  */
990 HANDLE WINAPI
991 CreateMRUListA (LPCREATEMRULISTA lpcml)
992 {
993      return CreateMRUListLazyA (lpcml, 0, 0, 0);
994 }
995
996
997 /**************************************************************************
998  *                EnumMRUListW [COMCTL32.403]
999  *
1000  * Enumerate item in a list
1001  *
1002  * PARAMS
1003  *    hList [I] list handle
1004  *    nItemPos [I] item position to enumerate
1005  *    lpBuffer [O] buffer to receive item
1006  *    nBufferSize [I] size of buffer
1007  *
1008  * RETURNS
1009  *    For binary lists specifies how many bytes were copied to buffer, for
1010  *    string lists specifies full length of string.  Enumerating past the end
1011  *    of list returns -1.
1012  *    If lpBuffer == NULL or nItemPos is -ve return value is no. of items in
1013  *    the list.
1014  */
1015 INT WINAPI EnumMRUListW(HANDLE hList, INT nItemPos, LPVOID lpBuffer,
1016 DWORD nBufferSize)
1017 {
1018     LPWINEMRULIST mp = (LPWINEMRULIST) hList;
1019     LPWINEMRUITEM witem;
1020     INT desired, datasize;
1021
1022     if (nItemPos >= mp->cursize) return -1;
1023     if ((nItemPos < 0) || !lpBuffer) return mp->cursize;
1024     desired = mp->realMRU[nItemPos];
1025     desired -= 'a';
1026     TRACE("nItemPos=%d, desired=%d\n", nItemPos, desired);
1027     witem = mp->array[desired];
1028     datasize = min( witem->size, nBufferSize );
1029     memcpy( lpBuffer, &witem->datastart, datasize);
1030     TRACE("(%p, %d, %p, %ld): returning len=%d\n",
1031           hList, nItemPos, lpBuffer, nBufferSize, datasize);
1032     return datasize;
1033 }
1034
1035 /**************************************************************************
1036  *                EnumMRUListA [COMCTL32.154]
1037  *
1038  */
1039 INT WINAPI EnumMRUListA(HANDLE hList, INT nItemPos, LPVOID lpBuffer,
1040 DWORD nBufferSize)
1041 {
1042     LPWINEMRULIST mp = (LPWINEMRULIST) hList;
1043     LPWINEMRUITEM witem;
1044     INT desired, datasize;
1045     DWORD lenA;
1046
1047     if (nItemPos >= mp->cursize) return -1;
1048     if ((nItemPos < 0) || !lpBuffer) return mp->cursize;
1049     desired = mp->realMRU[nItemPos];
1050     desired -= 'a';
1051     TRACE("nItemPos=%d, desired=%d\n", nItemPos, desired);
1052     witem = mp->array[desired];
1053     if(mp->extview.dwFlags & MRUF_BINARY_LIST) {
1054         datasize = min( witem->size, nBufferSize );
1055         memcpy( lpBuffer, &witem->datastart, datasize);
1056     } else {
1057         lenA = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&witem->datastart, -1,
1058                                    NULL, 0, NULL, NULL);
1059         datasize = min( witem->size, nBufferSize );
1060         WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&witem->datastart, -1,
1061                             lpBuffer, datasize, NULL, NULL);
1062     }
1063     TRACE("(%p, %d, %p, %ld): returning len=%d\n",
1064           hList, nItemPos, lpBuffer, nBufferSize, datasize);
1065     return datasize;
1066 }
1067
1068
1069 /**************************************************************************
1070  * Str_GetPtrA [COMCTL32.233]
1071  *
1072  * PARAMS
1073  *     lpSrc   [I]
1074  *     lpDest  [O]
1075  *     nMaxLen [I]
1076  *
1077  * RETURNS
1078  */
1079
1080 INT WINAPI
1081 Str_GetPtrA (LPCSTR lpSrc, LPSTR lpDest, INT nMaxLen)
1082 {
1083     INT len;
1084
1085     TRACE("(%p %p %d)\n", lpSrc, lpDest, nMaxLen);
1086
1087     if (!lpDest && lpSrc)
1088         return strlen (lpSrc);
1089
1090     if (nMaxLen == 0)
1091         return 0;
1092
1093     if (lpSrc == NULL) {
1094         lpDest[0] = '\0';
1095         return 0;
1096     }
1097
1098     len = strlen (lpSrc);
1099     if (len >= nMaxLen)
1100         len = nMaxLen - 1;
1101
1102     RtlMoveMemory (lpDest, lpSrc, len);
1103     lpDest[len] = '\0';
1104
1105     return len;
1106 }
1107
1108
1109 /**************************************************************************
1110  * Str_SetPtrA [COMCTL32.234]
1111  *
1112  * PARAMS
1113  *     lppDest [O]
1114  *     lpSrc   [I]
1115  *
1116  * RETURNS
1117  */
1118
1119 BOOL WINAPI
1120 Str_SetPtrA (LPSTR *lppDest, LPCSTR lpSrc)
1121 {
1122     TRACE("(%p %p)\n", lppDest, lpSrc);
1123
1124     if (lpSrc) {
1125         LPSTR ptr = COMCTL32_ReAlloc (*lppDest, strlen (lpSrc) + 1);
1126         if (!ptr)
1127             return FALSE;
1128         strcpy (ptr, lpSrc);
1129         *lppDest = ptr;
1130     }
1131     else {
1132         if (*lppDest) {
1133             COMCTL32_Free (*lppDest);
1134             *lppDest = NULL;
1135         }
1136     }
1137
1138     return TRUE;
1139 }
1140
1141
1142 /**************************************************************************
1143  * Str_GetPtrW [COMCTL32.235]
1144  *
1145  * PARAMS
1146  *     lpSrc   [I]
1147  *     lpDest  [O]
1148  *     nMaxLen [I]
1149  *
1150  * RETURNS
1151  */
1152
1153 INT WINAPI
1154 Str_GetPtrW (LPCWSTR lpSrc, LPWSTR lpDest, INT nMaxLen)
1155 {
1156     INT len;
1157
1158     TRACE("(%p %p %d)\n", lpSrc, lpDest, nMaxLen);
1159
1160     if (!lpDest && lpSrc)
1161         return strlenW (lpSrc);
1162
1163     if (nMaxLen == 0)
1164         return 0;
1165
1166     if (lpSrc == NULL) {
1167         lpDest[0] = L'\0';
1168         return 0;
1169     }
1170
1171     len = strlenW (lpSrc);
1172     if (len >= nMaxLen)
1173         len = nMaxLen - 1;
1174
1175     RtlMoveMemory (lpDest, lpSrc, len*sizeof(WCHAR));
1176     lpDest[len] = L'\0';
1177
1178     return len;
1179 }
1180
1181
1182 /**************************************************************************
1183  * Str_SetPtrW [COMCTL32.236]
1184  *
1185  * PARAMS
1186  *     lpDest [O]
1187  *     lpSrc  [I]
1188  *
1189  * RETURNS
1190  */
1191
1192 BOOL WINAPI
1193 Str_SetPtrW (LPWSTR *lppDest, LPCWSTR lpSrc)
1194 {
1195     TRACE("(%p %p)\n", lppDest, lpSrc);
1196
1197     if (lpSrc) {
1198         INT len = strlenW (lpSrc) + 1;
1199         LPWSTR ptr = COMCTL32_ReAlloc (*lppDest, len * sizeof(WCHAR));
1200         if (!ptr)
1201             return FALSE;
1202         strcpyW (ptr, lpSrc);
1203         *lppDest = ptr;
1204     }
1205     else {
1206         if (*lppDest) {
1207             COMCTL32_Free (*lppDest);
1208             *lppDest = NULL;
1209         }
1210     }
1211
1212     return TRUE;
1213 }
1214
1215
1216 /**************************************************************************
1217  * Str_GetPtrWtoA [internal]
1218  *
1219  * Converts a unicode string into a multi byte string
1220  *
1221  * PARAMS
1222  *     lpSrc   [I] Pointer to the unicode source string
1223  *     lpDest  [O] Pointer to caller supplied storage for the multi byte string
1224  *     nMaxLen [I] Size, in bytes, of the destination buffer
1225  *
1226  * RETURNS
1227  *     Length, in bytes, of the converted string.
1228  */
1229
1230 INT
1231 Str_GetPtrWtoA (LPCWSTR lpSrc, LPSTR lpDest, INT nMaxLen)
1232 {
1233     INT len;
1234
1235     TRACE("(%s %p %d)\n", debugstr_w(lpSrc), lpDest, nMaxLen);
1236
1237     if (!lpDest && lpSrc)
1238         return WideCharToMultiByte(CP_ACP, 0, lpSrc, -1, 0, 0, NULL, NULL);
1239
1240     if (nMaxLen == 0)
1241         return 0;
1242
1243     if (lpSrc == NULL) {
1244         lpDest[0] = '\0';
1245         return 0;
1246     }
1247
1248     len = WideCharToMultiByte(CP_ACP, 0, lpSrc, -1, 0, 0, NULL, NULL);
1249     if (len >= nMaxLen)
1250         len = nMaxLen - 1;
1251
1252     WideCharToMultiByte(CP_ACP, 0, lpSrc, -1, lpDest, len, NULL, NULL);
1253     lpDest[len] = '\0';
1254
1255     return len;
1256 }
1257
1258
1259 /**************************************************************************
1260  * Str_SetPtrAtoW [internal]
1261  *
1262  * Converts a multi byte string to a unicode string.
1263  * If the pointer to the destination buffer is NULL a buffer is allocated.
1264  * If the destination buffer is too small to keep the converted multi byte
1265  * string the destination buffer is reallocated. If the source pointer is
1266  * NULL, the destination buffer is freed.
1267  *
1268  * PARAMS
1269  *     lppDest [I/O] pointer to a pointer to the destination buffer
1270  *     lpSrc   [I] pointer to a multi byte string
1271  *
1272  * RETURNS
1273  *     TRUE: conversion successful
1274  *     FALSE: error
1275  */
1276
1277 BOOL
1278 Str_SetPtrAtoW (LPWSTR *lppDest, LPCSTR lpSrc)
1279 {
1280     TRACE("(%p %s)\n", lppDest, lpSrc);
1281
1282     if (lpSrc) {
1283         INT len = MultiByteToWideChar(CP_ACP,0,lpSrc,-1,NULL,0);
1284         LPWSTR ptr = COMCTL32_ReAlloc (*lppDest, len*sizeof(WCHAR));
1285
1286         if (!ptr)
1287             return FALSE;
1288         MultiByteToWideChar(CP_ACP,0,lpSrc,-1,ptr,len);
1289         *lppDest = ptr;
1290     }
1291     else {
1292         if (*lppDest) {
1293             COMCTL32_Free (*lppDest);
1294             *lppDest = NULL;
1295         }
1296     }
1297
1298     return TRUE;
1299 }
1300
1301
1302 /**************************************************************************
1303  * The DSA-API is a set of functions to create and manipulate arrays of
1304  * fixed-size memory blocks. These arrays can store any kind of data
1305  * (strings, icons...).
1306  */
1307
1308 /**************************************************************************
1309  * DSA_Create [COMCTL32.320] Creates a dynamic storage array
1310  *
1311  * PARAMS
1312  *     nSize [I] size of the array elements
1313  *     nGrow [I] number of elements by which the array grows when it is filled
1314  *
1315  * RETURNS
1316  *     Success: pointer to an array control structure. Use this like a handle.
1317  *     Failure: NULL
1318  */
1319
1320 HDSA WINAPI
1321 DSA_Create (INT nSize, INT nGrow)
1322 {
1323     HDSA hdsa;
1324
1325     TRACE("(size=%d grow=%d)\n", nSize, nGrow);
1326
1327     hdsa = (HDSA)COMCTL32_Alloc (sizeof(DSA));
1328     if (hdsa)
1329     {
1330         hdsa->nItemCount = 0;
1331         hdsa->pData = NULL;
1332         hdsa->nMaxCount = 0;
1333         hdsa->nItemSize = nSize;
1334         hdsa->nGrow = max(1, nGrow);
1335     }
1336
1337     return hdsa;
1338 }
1339
1340
1341 /**************************************************************************
1342  * DSA_Destroy [COMCTL32.321] Destroys a dynamic storage array
1343  *
1344  * PARAMS
1345  *     hdsa [I] pointer to the array control structure
1346  *
1347  * RETURNS
1348  *     Success: TRUE
1349  *     Failure: FALSE
1350  */
1351
1352 BOOL WINAPI
1353 DSA_Destroy (const HDSA hdsa)
1354 {
1355     TRACE("(%p)\n", hdsa);
1356
1357     if (!hdsa)
1358         return FALSE;
1359
1360     if (hdsa->pData && (!COMCTL32_Free (hdsa->pData)))
1361         return FALSE;
1362
1363     return COMCTL32_Free (hdsa);
1364 }
1365
1366
1367 /**************************************************************************
1368  * DSA_GetItem [COMCTL32.322]
1369  *
1370  * PARAMS
1371  *     hdsa   [I] pointer to the array control structure
1372  *     nIndex [I] number of the Item to get
1373  *     pDest  [O] destination buffer. Has to be >= dwElementSize.
1374  *
1375  * RETURNS
1376  *     Success: TRUE
1377  *     Failure: FALSE
1378  */
1379
1380 BOOL WINAPI
1381 DSA_GetItem (const HDSA hdsa, INT nIndex, LPVOID pDest)
1382 {
1383     LPVOID pSrc;
1384
1385     TRACE("(%p %d %p)\n", hdsa, nIndex, pDest);
1386
1387     if (!hdsa)
1388         return FALSE;
1389     if ((nIndex < 0) || (nIndex >= hdsa->nItemCount))
1390         return FALSE;
1391
1392     pSrc = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
1393     memmove (pDest, pSrc, hdsa->nItemSize);
1394
1395     return TRUE;
1396 }
1397
1398
1399 /**************************************************************************
1400  * DSA_GetItemPtr [COMCTL32.323]
1401  *
1402  * Retrieves a pointer to the specified item.
1403  *
1404  * PARAMS
1405  *     hdsa   [I] pointer to the array control structure
1406  *     nIndex [I] index of the desired item
1407  *
1408  * RETURNS
1409  *     Success: pointer to an item
1410  *     Failure: NULL
1411  */
1412
1413 LPVOID WINAPI
1414 DSA_GetItemPtr (const HDSA hdsa, INT nIndex)
1415 {
1416     LPVOID pSrc;
1417
1418     TRACE("(%p %d)\n", hdsa, nIndex);
1419
1420     if (!hdsa)
1421         return NULL;
1422     if ((nIndex < 0) || (nIndex >= hdsa->nItemCount))
1423         return NULL;
1424
1425     pSrc = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
1426
1427     TRACE("-- ret=%p\n", pSrc);
1428
1429     return pSrc;
1430 }
1431
1432
1433 /**************************************************************************
1434  * DSA_SetItem [COMCTL32.325]
1435  *
1436  * Sets the contents of an item in the array.
1437  *
1438  * PARAMS
1439  *     hdsa   [I] pointer to the array control structure
1440  *     nIndex [I] index for the item
1441  *     pSrc   [I] pointer to the new item data
1442  *
1443  * RETURNS
1444  *     Success: TRUE
1445  *     Failure: FALSE
1446  */
1447
1448 BOOL WINAPI
1449 DSA_SetItem (const HDSA hdsa, INT nIndex, LPVOID pSrc)
1450 {
1451     INT  nSize, nNewItems;
1452     LPVOID pDest, lpTemp;
1453
1454     TRACE("(%p %d %p)\n", hdsa, nIndex, pSrc);
1455
1456     if ((!hdsa) || nIndex < 0)
1457         return FALSE;
1458
1459     if (hdsa->nItemCount <= nIndex) {
1460         /* within the old array */
1461         if (hdsa->nMaxCount > nIndex) {
1462             /* within the allocated space, set a new boundary */
1463             hdsa->nItemCount = nIndex + 1;
1464         }
1465         else {
1466             /* resize the block of memory */
1467             nNewItems =
1468                 hdsa->nGrow * ((INT)(((nIndex + 1) - 1) / hdsa->nGrow) + 1);
1469             nSize = hdsa->nItemSize * nNewItems;
1470
1471             lpTemp = (LPVOID)COMCTL32_ReAlloc (hdsa->pData, nSize);
1472             if (!lpTemp)
1473                 return FALSE;
1474
1475             hdsa->nMaxCount = nNewItems;
1476             hdsa->nItemCount = nIndex + 1;
1477             hdsa->pData = lpTemp;
1478         }
1479     }
1480
1481     /* put the new entry in */
1482     pDest = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
1483     TRACE("-- move dest=%p src=%p size=%d\n",
1484            pDest, pSrc, hdsa->nItemSize);
1485     memmove (pDest, pSrc, hdsa->nItemSize);
1486
1487     return TRUE;
1488 }
1489
1490
1491 /**************************************************************************
1492  * DSA_InsertItem [COMCTL32.324]
1493  *
1494  * PARAMS
1495  *     hdsa   [I] pointer to the array control structure
1496  *     nIndex [I] index for the new item
1497  *     pSrc   [I] pointer to the element
1498  *
1499  * RETURNS
1500  *     Success: position of the new item
1501  *     Failure: -1
1502  */
1503
1504 INT WINAPI
1505 DSA_InsertItem (const HDSA hdsa, INT nIndex, LPVOID pSrc)
1506 {
1507     INT   nNewItems, nSize;
1508     LPVOID  lpTemp, lpDest;
1509
1510     TRACE("(%p %d %p)\n", hdsa, nIndex, pSrc);
1511
1512     if ((!hdsa) || nIndex < 0)
1513         return -1;
1514
1515     /* when nIndex >= nItemCount then append */
1516     if (nIndex >= hdsa->nItemCount)
1517         nIndex = hdsa->nItemCount;
1518
1519     /* do we need to resize ? */
1520     if (hdsa->nItemCount >= hdsa->nMaxCount) {
1521         nNewItems = hdsa->nMaxCount + hdsa->nGrow;
1522         nSize = hdsa->nItemSize * nNewItems;
1523
1524         lpTemp = (LPVOID)COMCTL32_ReAlloc (hdsa->pData, nSize);
1525         if (!lpTemp)
1526             return -1;
1527
1528         hdsa->nMaxCount = nNewItems;
1529         hdsa->pData = lpTemp;
1530     }
1531
1532     /* do we need to move elements ? */
1533     if (nIndex < hdsa->nItemCount) {
1534         lpTemp = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
1535         lpDest = (char *) lpTemp + hdsa->nItemSize;
1536         nSize = (hdsa->nItemCount - nIndex) * hdsa->nItemSize;
1537         TRACE("-- move dest=%p src=%p size=%d\n",
1538                lpDest, lpTemp, nSize);
1539         memmove (lpDest, lpTemp, nSize);
1540     }
1541
1542     /* ok, we can put the new Item in */
1543     hdsa->nItemCount++;
1544     lpDest = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
1545     TRACE("-- move dest=%p src=%p size=%d\n",
1546            lpDest, pSrc, hdsa->nItemSize);
1547     memmove (lpDest, pSrc, hdsa->nItemSize);
1548
1549     return nIndex;
1550 }
1551
1552
1553 /**************************************************************************
1554  * DSA_DeleteItem [COMCTL32.326]
1555  *
1556  * PARAMS
1557  *     hdsa   [I] pointer to the array control structure
1558  *     nIndex [I] index for the element to delete
1559  *
1560  * RETURNS
1561  *     Success: number of the deleted element
1562  *     Failure: -1
1563  */
1564
1565 INT WINAPI
1566 DSA_DeleteItem (const HDSA hdsa, INT nIndex)
1567 {
1568     LPVOID lpDest,lpSrc;
1569     INT  nSize;
1570
1571     TRACE("(%p %d)\n", hdsa, nIndex);
1572
1573     if (!hdsa)
1574         return -1;
1575     if (nIndex < 0 || nIndex >= hdsa->nItemCount)
1576         return -1;
1577
1578     /* do we need to move ? */
1579     if (nIndex < hdsa->nItemCount - 1) {
1580         lpDest = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
1581         lpSrc = (char *) lpDest + hdsa->nItemSize;
1582         nSize = hdsa->nItemSize * (hdsa->nItemCount - nIndex - 1);
1583         TRACE("-- move dest=%p src=%p size=%d\n",
1584                lpDest, lpSrc, nSize);
1585         memmove (lpDest, lpSrc, nSize);
1586     }
1587
1588     hdsa->nItemCount--;
1589
1590     /* free memory ? */
1591     if ((hdsa->nMaxCount - hdsa->nItemCount) >= hdsa->nGrow) {
1592         nSize = hdsa->nItemSize * hdsa->nItemCount;
1593
1594         lpDest = (LPVOID)COMCTL32_ReAlloc (hdsa->pData, nSize);
1595         if (!lpDest)
1596             return -1;
1597
1598         hdsa->nMaxCount = hdsa->nItemCount;
1599         hdsa->pData = lpDest;
1600     }
1601
1602     return nIndex;
1603 }
1604
1605
1606 /**************************************************************************
1607  * DSA_DeleteAllItems [COMCTL32.327]
1608  *
1609  * Removes all items and reinitializes the array.
1610  *
1611  * PARAMS
1612  *     hdsa [I] pointer to the array control structure
1613  *
1614  * RETURNS
1615  *     Success: TRUE
1616  *     Failure: FALSE
1617  */
1618
1619 BOOL WINAPI
1620 DSA_DeleteAllItems (const HDSA hdsa)
1621 {
1622     TRACE("(%p)\n", hdsa);
1623
1624     if (!hdsa)
1625         return FALSE;
1626     if (hdsa->pData && (!COMCTL32_Free (hdsa->pData)))
1627         return FALSE;
1628
1629     hdsa->nItemCount = 0;
1630     hdsa->pData = NULL;
1631     hdsa->nMaxCount = 0;
1632
1633     return TRUE;
1634 }
1635
1636
1637 /**************************************************************************
1638  * The DPA-API is a set of functions to create and manipulate arrays of
1639  * pointers.
1640  */
1641
1642 /**************************************************************************
1643  * DPA_Create [COMCTL32.328] Creates a dynamic pointer array
1644  *
1645  * PARAMS
1646  *     nGrow [I] number of items by which the array grows when it is filled
1647  *
1648  * RETURNS
1649  *     Success: handle (pointer) to the pointer array.
1650  *     Failure: NULL
1651  */
1652
1653 HDPA WINAPI
1654 DPA_Create (INT nGrow)
1655 {
1656     HDPA hdpa;
1657
1658     TRACE("(%d)\n", nGrow);
1659
1660     hdpa = (HDPA)COMCTL32_Alloc (sizeof(DPA));
1661     if (hdpa) {
1662         hdpa->nGrow = max(8, nGrow);
1663         hdpa->hHeap = COMCTL32_hHeap;
1664         hdpa->nMaxCount = hdpa->nGrow * 2;
1665         hdpa->ptrs =
1666             (LPVOID*)COMCTL32_Alloc (hdpa->nMaxCount * sizeof(LPVOID));
1667     }
1668
1669     TRACE("-- %p\n", hdpa);
1670
1671     return hdpa;
1672 }
1673
1674
1675 /**************************************************************************
1676  * DPA_Destroy [COMCTL32.329] Destroys a dynamic pointer array
1677  *
1678  * PARAMS
1679  *     hdpa [I] handle (pointer) to the pointer array
1680  *
1681  * RETURNS
1682  *     Success: TRUE
1683  *     Failure: FALSE
1684  */
1685
1686 BOOL WINAPI
1687 DPA_Destroy (const HDPA hdpa)
1688 {
1689     TRACE("(%p)\n", hdpa);
1690
1691     if (!hdpa)
1692         return FALSE;
1693
1694     if (hdpa->ptrs && (!HeapFree (hdpa->hHeap, 0, hdpa->ptrs)))
1695         return FALSE;
1696
1697     return HeapFree (hdpa->hHeap, 0, hdpa);
1698 }
1699
1700
1701 /**************************************************************************
1702  * DPA_Grow [COMCTL32.330]
1703  *
1704  * Sets the growth amount.
1705  *
1706  * PARAMS
1707  *     hdpa  [I] handle (pointer) to the existing (source) pointer array
1708  *     nGrow [I] number of items by which the array grows when it's too small
1709  *
1710  * RETURNS
1711  *     Success: TRUE
1712  *     Failure: FALSE
1713  */
1714
1715 BOOL WINAPI
1716 DPA_Grow (const HDPA hdpa, INT nGrow)
1717 {
1718     TRACE("(%p %d)\n", hdpa, nGrow);
1719
1720     if (!hdpa)
1721         return FALSE;
1722
1723     hdpa->nGrow = max(8, nGrow);
1724
1725     return TRUE;
1726 }
1727
1728
1729 /**************************************************************************
1730  * DPA_Clone [COMCTL32.331]
1731  *
1732  * Copies a pointer array to an other one or creates a copy
1733  *
1734  * PARAMS
1735  *     hdpa    [I] handle (pointer) to the existing (source) pointer array
1736  *     hdpaNew [O] handle (pointer) to the destination pointer array
1737  *
1738  * RETURNS
1739  *     Success: pointer to the destination pointer array.
1740  *     Failure: NULL
1741  *
1742  * NOTES
1743  *     - If the 'hdpaNew' is a NULL-Pointer, a copy of the source pointer
1744  *       array will be created and it's handle (pointer) is returned.
1745  *     - If 'hdpa' is a NULL-Pointer, the original implementation crashes,
1746  *       this implementation just returns NULL.
1747  */
1748
1749 HDPA WINAPI
1750 DPA_Clone (const HDPA hdpa, const HDPA hdpaNew)
1751 {
1752     INT nNewItems, nSize;
1753     HDPA hdpaTemp;
1754
1755     if (!hdpa)
1756         return NULL;
1757
1758     TRACE("(%p %p)\n", hdpa, hdpaNew);
1759
1760     if (!hdpaNew) {
1761         /* create a new DPA */
1762         hdpaTemp = (HDPA)HeapAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
1763                                     sizeof(DPA));
1764         hdpaTemp->hHeap = hdpa->hHeap;
1765         hdpaTemp->nGrow = hdpa->nGrow;
1766     }
1767     else
1768         hdpaTemp = hdpaNew;
1769
1770     if (hdpaTemp->ptrs) {
1771         /* remove old pointer array */
1772         HeapFree (hdpaTemp->hHeap, 0, hdpaTemp->ptrs);
1773         hdpaTemp->ptrs = NULL;
1774         hdpaTemp->nItemCount = 0;
1775         hdpaTemp->nMaxCount = 0;
1776     }
1777
1778     /* create a new pointer array */
1779     nNewItems = hdpaTemp->nGrow *
1780                 ((INT)((hdpa->nItemCount - 1) / hdpaTemp->nGrow) + 1);
1781     nSize = nNewItems * sizeof(LPVOID);
1782     hdpaTemp->ptrs =
1783         (LPVOID*)HeapAlloc (hdpaTemp->hHeap, HEAP_ZERO_MEMORY, nSize);
1784     hdpaTemp->nMaxCount = nNewItems;
1785
1786     /* clone the pointer array */
1787     hdpaTemp->nItemCount = hdpa->nItemCount;
1788     memmove (hdpaTemp->ptrs, hdpa->ptrs,
1789              hdpaTemp->nItemCount * sizeof(LPVOID));
1790
1791     return hdpaTemp;
1792 }
1793
1794
1795 /**************************************************************************
1796  * DPA_GetPtr [COMCTL32.332]
1797  *
1798  * Retrieves a pointer from a dynamic pointer array
1799  *
1800  * PARAMS
1801  *     hdpa   [I] handle (pointer) to the pointer array
1802  *     nIndex [I] array index of the desired pointer
1803  *
1804  * RETURNS
1805  *     Success: pointer
1806  *     Failure: NULL
1807  */
1808
1809 LPVOID WINAPI
1810 DPA_GetPtr (const HDPA hdpa, INT i)
1811 {
1812     TRACE("(%p %d)\n", hdpa, i);
1813
1814     if (!hdpa)
1815         return NULL;
1816     if (!hdpa->ptrs) {
1817         WARN("no pointer array.\n");
1818         return NULL;
1819     }
1820     if ((i < 0) || (i >= hdpa->nItemCount)) {
1821         WARN("not enough pointers in array (%d vs %d).\n",i,hdpa->nItemCount);
1822         return NULL;
1823     }
1824
1825     TRACE("-- %p\n", hdpa->ptrs[i]);
1826
1827     return hdpa->ptrs[i];
1828 }
1829
1830
1831 /**************************************************************************
1832  * DPA_GetPtrIndex [COMCTL32.333]
1833  *
1834  * Retrieves the index of the specified pointer
1835  *
1836  * PARAMS
1837  *     hdpa   [I] handle (pointer) to the pointer array
1838  *     p      [I] pointer
1839  *
1840  * RETURNS
1841  *     Success: index of the specified pointer
1842  *     Failure: -1
1843  */
1844
1845 INT WINAPI
1846 DPA_GetPtrIndex (const HDPA hdpa, LPVOID p)
1847 {
1848     INT i;
1849
1850     if (!hdpa || !hdpa->ptrs)
1851         return -1;
1852
1853     for (i = 0; i < hdpa->nItemCount; i++) {
1854         if (hdpa->ptrs[i] == p)
1855             return i;
1856     }
1857
1858     return -1;
1859 }
1860
1861
1862 /**************************************************************************
1863  * DPA_InsertPtr [COMCTL32.334]
1864  *
1865  * Inserts a pointer into a dynamic pointer array
1866  *
1867  * PARAMS
1868  *     hdpa [I] handle (pointer) to the array
1869  *     i    [I] array index
1870  *     p    [I] pointer to insert
1871  *
1872  * RETURNS
1873  *     Success: index of the inserted pointer
1874  *     Failure: -1
1875  */
1876
1877 INT WINAPI
1878 DPA_InsertPtr (const HDPA hdpa, INT i, LPVOID p)
1879 {
1880     TRACE("(%p %d %p)\n", hdpa, i, p);
1881
1882     if (!hdpa || i < 0) return -1;
1883
1884     if (i >= 0x7fff)
1885         i = hdpa->nItemCount;
1886
1887     if (i >= hdpa->nItemCount)
1888         return DPA_SetPtr(hdpa, i, p) ? i : -1;
1889
1890     /* create empty spot at the end */
1891     if (!DPA_SetPtr(hdpa, hdpa->nItemCount, 0)) return -1;
1892     memmove (hdpa->ptrs + i + 1, hdpa->ptrs + i, (hdpa->nItemCount - i - 1) * sizeof(LPVOID));
1893     hdpa->ptrs[i] = p;
1894     return i;
1895 }
1896
1897 /**************************************************************************
1898  * DPA_SetPtr [COMCTL32.335]
1899  *
1900  * Sets a pointer in the pointer array
1901  *
1902  * PARAMS
1903  *     hdpa [I] handle (pointer) to the pointer array
1904  *     i    [I] index of the pointer that will be set
1905  *     p    [I] pointer to be set
1906  *
1907  * RETURNS
1908  *     Success: TRUE
1909  *     Failure: FALSE
1910  */
1911
1912 BOOL WINAPI
1913 DPA_SetPtr (const HDPA hdpa, INT i, LPVOID p)
1914 {
1915     LPVOID *lpTemp;
1916
1917     TRACE("(%p %d %p)\n", hdpa, i, p);
1918
1919     if (!hdpa || i < 0 || i > 0x7fff)
1920         return FALSE;
1921
1922     if (hdpa->nItemCount <= i) {
1923         /* within the old array */
1924         if (hdpa->nMaxCount <= i) {
1925             /* resize the block of memory */
1926             INT nNewItems =
1927                 hdpa->nGrow * ((INT)(((i+1) - 1) / hdpa->nGrow) + 1);
1928             INT nSize = nNewItems * sizeof(LPVOID);
1929
1930             if (hdpa->ptrs)
1931                 lpTemp = (LPVOID*)HeapReAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY, hdpa->ptrs, nSize);
1932             else
1933                 lpTemp = (LPVOID*)HeapAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY, nSize);
1934             
1935             if (!lpTemp)
1936                 return FALSE;
1937
1938             hdpa->nMaxCount = nNewItems;
1939             hdpa->ptrs = lpTemp;
1940         }
1941         hdpa->nItemCount = i+1;
1942     }
1943
1944     /* put the new entry in */
1945     hdpa->ptrs[i] = p;
1946
1947     return TRUE;
1948 }
1949
1950
1951 /**************************************************************************
1952  * DPA_DeletePtr [COMCTL32.336]
1953  *
1954  * Removes a pointer from the pointer array.
1955  *
1956  * PARAMS
1957  *     hdpa [I] handle (pointer) to the pointer array
1958  *     i    [I] index of the pointer that will be deleted
1959  *
1960  * RETURNS
1961  *     Success: deleted pointer
1962  *     Failure: NULL
1963  */
1964
1965 LPVOID WINAPI
1966 DPA_DeletePtr (const HDPA hdpa, INT i)
1967 {
1968     LPVOID *lpDest, *lpSrc, lpTemp = NULL;
1969     INT  nSize;
1970
1971     TRACE("(%p %d)\n", hdpa, i);
1972
1973     if ((!hdpa) || i < 0 || i >= hdpa->nItemCount)
1974         return NULL;
1975
1976     lpTemp = hdpa->ptrs[i];
1977
1978     /* do we need to move ?*/
1979     if (i < hdpa->nItemCount - 1) {
1980         lpDest = hdpa->ptrs + i;
1981         lpSrc = lpDest + 1;
1982         nSize = (hdpa->nItemCount - i - 1) * sizeof(LPVOID);
1983         TRACE("-- move dest=%p src=%p size=%x\n",
1984                lpDest, lpSrc, nSize);
1985         memmove (lpDest, lpSrc, nSize);
1986     }
1987
1988     hdpa->nItemCount --;
1989
1990     /* free memory ?*/
1991     if ((hdpa->nMaxCount - hdpa->nItemCount) >= hdpa->nGrow) {
1992         INT nNewItems = max(hdpa->nGrow * 2, hdpa->nItemCount);
1993         nSize = nNewItems * sizeof(LPVOID);
1994         lpDest = (LPVOID)HeapReAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
1995                                       hdpa->ptrs, nSize);
1996         if (!lpDest)
1997             return NULL;
1998
1999         hdpa->nMaxCount = nNewItems;
2000         hdpa->ptrs = (LPVOID*)lpDest;
2001     }
2002
2003     return lpTemp;
2004 }
2005
2006
2007 /**************************************************************************
2008  * DPA_DeleteAllPtrs [COMCTL32.337]
2009  *
2010  * Removes all pointers and reinitializes the array.
2011  *
2012  * PARAMS
2013  *     hdpa [I] handle (pointer) to the pointer array
2014  *
2015  * RETURNS
2016  *     Success: TRUE
2017  *     Failure: FALSE
2018  */
2019
2020 BOOL WINAPI
2021 DPA_DeleteAllPtrs (const HDPA hdpa)
2022 {
2023     TRACE("(%p)\n", hdpa);
2024
2025     if (!hdpa)
2026         return FALSE;
2027
2028     if (hdpa->ptrs && (!HeapFree (hdpa->hHeap, 0, hdpa->ptrs)))
2029         return FALSE;
2030
2031     hdpa->nItemCount = 0;
2032     hdpa->nMaxCount = hdpa->nGrow * 2;
2033     hdpa->ptrs = (LPVOID*)HeapAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
2034                                      hdpa->nMaxCount * sizeof(LPVOID));
2035
2036     return TRUE;
2037 }
2038
2039
2040 /**************************************************************************
2041  * DPA_QuickSort [Internal]
2042  *
2043  * Ordinary quicksort (used by DPA_Sort).
2044  *
2045  * PARAMS
2046  *     lpPtrs     [I] pointer to the pointer array
2047  *     l          [I] index of the "left border" of the partition
2048  *     r          [I] index of the "right border" of the partition
2049  *     pfnCompare [I] pointer to the compare function
2050  *     lParam     [I] user defined value (3rd parameter in compare function)
2051  *
2052  * RETURNS
2053  *     NONE
2054  */
2055
2056 static VOID
2057 DPA_QuickSort (LPVOID *lpPtrs, INT l, INT r,
2058                PFNDPACOMPARE pfnCompare, LPARAM lParam)
2059 {
2060     INT m;
2061     LPVOID t;
2062
2063     TRACE("l=%i r=%i\n", l, r);
2064
2065     if (l==r)    /* one element is always sorted */
2066         return;
2067     if (r<l)     /* oops, got it in the wrong order */
2068         {
2069         DPA_QuickSort(lpPtrs, r, l, pfnCompare, lParam);
2070         return;
2071         }
2072     m = (l+r)/2; /* divide by two */
2073     DPA_QuickSort(lpPtrs, l, m, pfnCompare, lParam);
2074     DPA_QuickSort(lpPtrs, m+1, r, pfnCompare, lParam);
2075
2076     /* join the two sides */
2077     while( (l<=m) && (m<r) )
2078     {
2079         if(pfnCompare(lpPtrs[l],lpPtrs[m+1],lParam)>0)
2080         {
2081             t = lpPtrs[m+1];
2082             memmove(&lpPtrs[l+1],&lpPtrs[l],(m-l+1)*sizeof lpPtrs[l]);
2083             lpPtrs[l] = t;
2084
2085             m++;
2086         }
2087         l++;
2088     }
2089 }
2090
2091
2092 /**************************************************************************
2093  * DPA_Sort [COMCTL32.338]
2094  *
2095  * Sorts a pointer array using a user defined compare function
2096  *
2097  * PARAMS
2098  *     hdpa       [I] handle (pointer) to the pointer array
2099  *     pfnCompare [I] pointer to the compare function
2100  *     lParam     [I] user defined value (3rd parameter of compare function)
2101  *
2102  * RETURNS
2103  *     Success: TRUE
2104  *     Failure: FALSE
2105  */
2106
2107 BOOL WINAPI
2108 DPA_Sort (const HDPA hdpa, PFNDPACOMPARE pfnCompare, LPARAM lParam)
2109 {
2110     if (!hdpa || !pfnCompare)
2111         return FALSE;
2112
2113     TRACE("(%p %p 0x%lx)\n", hdpa, pfnCompare, lParam);
2114
2115     if ((hdpa->nItemCount > 1) && (hdpa->ptrs))
2116         DPA_QuickSort (hdpa->ptrs, 0, hdpa->nItemCount - 1,
2117                        pfnCompare, lParam);
2118
2119     return TRUE;
2120 }
2121
2122
2123 /**************************************************************************
2124  * DPA_Search [COMCTL32.339]
2125  *
2126  * Searches a pointer array for a specified pointer
2127  *
2128  * PARAMS
2129  *     hdpa       [I] handle (pointer) to the pointer array
2130  *     pFind      [I] pointer to search for
2131  *     nStart     [I] start index
2132  *     pfnCompare [I] pointer to the compare function
2133  *     lParam     [I] user defined value (3rd parameter of compare function)
2134  *     uOptions   [I] search options
2135  *
2136  * RETURNS
2137  *     Success: index of the pointer in the array.
2138  *     Failure: -1
2139  *
2140  * NOTES
2141  *     Binary search taken from R.Sedgewick "Algorithms in C"!
2142  *     Function is NOT tested!
2143  *     If something goes wrong, blame HIM not ME! (Eric Kohl)
2144  */
2145
2146 INT WINAPI
2147 DPA_Search (const HDPA hdpa, LPVOID pFind, INT nStart,
2148             PFNDPACOMPARE pfnCompare, LPARAM lParam, UINT uOptions)
2149 {
2150     if (!hdpa || !pfnCompare || !pFind)
2151         return -1;
2152
2153     TRACE("(%p %p %d %p 0x%08lx 0x%08x)\n",
2154            hdpa, pFind, nStart, pfnCompare, lParam, uOptions);
2155
2156     if (uOptions & DPAS_SORTED) {
2157         /* array is sorted --> use binary search */
2158         INT l, r, x, n;
2159         LPVOID *lpPtr;
2160
2161         TRACE("binary search\n");
2162
2163         l = (nStart == -1) ? 0 : nStart;
2164         r = hdpa->nItemCount - 1;
2165         lpPtr = hdpa->ptrs;
2166         while (r >= l) {
2167             x = (l + r) / 2;
2168             n = (pfnCompare)(pFind, lpPtr[x], lParam);
2169             if (n < 0)
2170                 r = x - 1;
2171             else
2172                 l = x + 1;
2173             if (n == 0) {
2174                 TRACE("-- ret=%d\n", n);
2175                 return n;
2176             }
2177         }
2178
2179         if (uOptions & DPAS_INSERTBEFORE) {
2180             TRACE("-- ret=%d\n", r);
2181             return r;
2182         }
2183
2184         if (uOptions & DPAS_INSERTAFTER) {
2185             TRACE("-- ret=%d\n", l);
2186             return l;
2187         }
2188     }
2189     else {
2190         /* array is not sorted --> use linear search */
2191         LPVOID *lpPtr;
2192         INT  nIndex;
2193
2194         TRACE("linear search\n");
2195
2196         nIndex = (nStart == -1)? 0 : nStart;
2197         lpPtr = hdpa->ptrs;
2198         for (; nIndex < hdpa->nItemCount; nIndex++) {
2199             if ((pfnCompare)(pFind, lpPtr[nIndex], lParam) == 0) {
2200                 TRACE("-- ret=%d\n", nIndex);
2201                 return nIndex;
2202             }
2203         }
2204     }
2205
2206     TRACE("-- not found: ret=-1\n");
2207     return -1;
2208 }
2209
2210
2211 /**************************************************************************
2212  * DPA_CreateEx [COMCTL32.340]
2213  *
2214  * Creates a dynamic pointer array using the specified size and heap.
2215  *
2216  * PARAMS
2217  *     nGrow [I] number of items by which the array grows when it is filled
2218  *     hHeap [I] handle to the heap where the array is stored
2219  *
2220  * RETURNS
2221  *     Success: handle (pointer) to the pointer array.
2222  *     Failure: NULL
2223  */
2224
2225 HDPA WINAPI
2226 DPA_CreateEx (INT nGrow, HANDLE hHeap)
2227 {
2228     HDPA hdpa;
2229
2230     TRACE("(%d %p)\n", nGrow, hHeap);
2231
2232     if (hHeap)
2233         hdpa = (HDPA)HeapAlloc (hHeap, HEAP_ZERO_MEMORY, sizeof(DPA));
2234     else
2235         hdpa = (HDPA)COMCTL32_Alloc (sizeof(DPA));
2236
2237     if (hdpa) {
2238         hdpa->nGrow = min(8, nGrow);
2239         hdpa->hHeap = hHeap ? hHeap : COMCTL32_hHeap;
2240         hdpa->nMaxCount = hdpa->nGrow * 2;
2241         hdpa->ptrs =
2242             (LPVOID*)HeapAlloc (hHeap, HEAP_ZERO_MEMORY,
2243                                 hdpa->nMaxCount * sizeof(LPVOID));
2244     }
2245
2246     TRACE("-- %p\n", hdpa);
2247
2248     return hdpa;
2249 }
2250
2251
2252 /**************************************************************************
2253  * Notification functions
2254  */
2255
2256 typedef struct tagNOTIFYDATA
2257 {
2258     HWND hwndFrom;
2259     HWND hwndTo;
2260     DWORD  dwParam3;
2261     DWORD  dwParam4;
2262     DWORD  dwParam5;
2263     DWORD  dwParam6;
2264 } NOTIFYDATA, *LPNOTIFYDATA;
2265
2266
2267 /**************************************************************************
2268  * DoNotify [Internal]
2269  */
2270
2271 static LRESULT
2272 DoNotify (LPNOTIFYDATA lpNotify, UINT uCode, LPNMHDR lpHdr)
2273 {
2274     NMHDR nmhdr;
2275     LPNMHDR lpNmh = NULL;
2276     UINT idFrom = 0;
2277
2278     TRACE("(%p %p %d %p 0x%08lx)\n",
2279            lpNotify->hwndFrom, lpNotify->hwndTo, uCode, lpHdr,
2280            lpNotify->dwParam5);
2281
2282     if (!lpNotify->hwndTo)
2283         return 0;
2284
2285     if (lpNotify->hwndFrom == (HWND)-1) {
2286         lpNmh = lpHdr;
2287         idFrom = lpHdr->idFrom;
2288     }
2289     else {
2290         if (lpNotify->hwndFrom) {
2291             HWND hwndParent = GetParent (lpNotify->hwndFrom);
2292             if (hwndParent) {
2293                 hwndParent = GetWindow (lpNotify->hwndFrom, GW_OWNER);
2294                 /* the following is done even if the return from above
2295                  * is zero.  GLA 12/2001 */
2296                 idFrom = GetDlgCtrlID (lpNotify->hwndFrom);
2297             }
2298         }
2299
2300         lpNmh = (lpHdr) ? lpHdr : &nmhdr;
2301
2302         lpNmh->hwndFrom = lpNotify->hwndFrom;
2303         lpNmh->idFrom = idFrom;
2304         lpNmh->code = uCode;
2305     }
2306
2307     return SendMessageA (lpNotify->hwndTo, WM_NOTIFY, idFrom, (LPARAM)lpNmh);
2308 }
2309
2310
2311 /**************************************************************************
2312  * SendNotify [COMCTL32.341]
2313  *
2314  * PARAMS
2315  *     hwndTo   [I]
2316  *     hwndFrom [I]
2317  *     uCode    [I]
2318  *     lpHdr    [I]
2319  *
2320  * RETURNS
2321  *     Success: return value from notification
2322  *     Failure: 0
2323  */
2324
2325 LRESULT WINAPI
2326 COMCTL32_SendNotify (HWND hwndTo, HWND hwndFrom,
2327                      UINT uCode, LPNMHDR lpHdr)
2328 {
2329     NOTIFYDATA notify;
2330
2331     TRACE("(%p %p %d %p)\n",
2332            hwndTo, hwndFrom, uCode, lpHdr);
2333
2334     notify.hwndFrom = hwndFrom;
2335     notify.hwndTo   = hwndTo;
2336     notify.dwParam5 = 0;
2337     notify.dwParam6 = 0;
2338
2339     return DoNotify (&notify, uCode, lpHdr);
2340 }
2341
2342
2343 /**************************************************************************
2344  * SendNotifyEx [COMCTL32.342]
2345  *
2346  * PARAMS
2347  *     hwndFrom [I]
2348  *     hwndTo   [I]
2349  *     uCode    [I]
2350  *     lpHdr    [I]
2351  *     dwParam5 [I]
2352  *
2353  * RETURNS
2354  *     Success: return value from notification
2355  *     Failure: 0
2356  */
2357
2358 LRESULT WINAPI
2359 COMCTL32_SendNotifyEx (HWND hwndTo, HWND hwndFrom, UINT uCode,
2360                        LPNMHDR lpHdr, DWORD dwParam5)
2361 {
2362     NOTIFYDATA notify;
2363     HWND hwndNotify;
2364
2365     TRACE("(%p %p %d %p 0x%08lx)\n",
2366            hwndFrom, hwndTo, uCode, lpHdr, dwParam5);
2367
2368     hwndNotify = hwndTo;
2369     if (!hwndTo) {
2370         if (IsWindow (hwndFrom)) {
2371             hwndNotify = GetParent (hwndFrom);
2372             if (!hwndNotify)
2373                 return 0;
2374         }
2375     }
2376
2377     notify.hwndFrom = hwndFrom;
2378     notify.hwndTo   = hwndNotify;
2379     notify.dwParam5 = dwParam5;
2380     notify.dwParam6 = 0;
2381
2382     return DoNotify (&notify, uCode, lpHdr);
2383 }
2384
2385
2386 /**************************************************************************
2387  * StrChrA [COMCTL32.350]
2388  *
2389  */
2390
2391 LPSTR WINAPI
2392 COMCTL32_StrChrA (LPCSTR lpString, CHAR cChar)
2393 {
2394     return strchr (lpString, cChar);
2395 }
2396
2397
2398 /**************************************************************************
2399  * StrStrIA [COMCTL32.355]
2400  */
2401
2402 LPSTR WINAPI
2403 COMCTL32_StrStrIA (LPCSTR lpStr1, LPCSTR lpStr2)
2404 {
2405     INT len1, len2, i;
2406     CHAR  first;
2407
2408     if (*lpStr2 == 0)
2409         return ((LPSTR)lpStr1);
2410     len1 = 0;
2411     while (lpStr1[len1] != 0) ++len1;
2412     len2 = 0;
2413     while (lpStr2[len2] != 0) ++len2;
2414     if (len2 == 0)
2415         return ((LPSTR)(lpStr1 + len1));
2416     first = tolower (*lpStr2);
2417     while (len1 >= len2) {
2418         if (tolower(*lpStr1) == first) {
2419             for (i = 1; i < len2; ++i)
2420                 if (tolower (lpStr1[i]) != tolower(lpStr2[i]))
2421                     break;
2422             if (i >= len2)
2423                 return ((LPSTR)lpStr1);
2424         }
2425         ++lpStr1; --len1;
2426     }
2427     return (NULL);
2428 }
2429
2430 /**************************************************************************
2431  * StrToIntA [COMCTL32.357] Converts a string to a signed integer.
2432  */
2433
2434 INT WINAPI
2435 COMCTL32_StrToIntA (LPSTR lpString)
2436 {
2437     return atoi(lpString);
2438 }
2439
2440 /**************************************************************************
2441  * StrStrIW [COMCTL32.363]
2442  */
2443
2444 LPWSTR WINAPI
2445 COMCTL32_StrStrIW (LPCWSTR lpStr1, LPCWSTR lpStr2)
2446 {
2447     INT len1, len2, i;
2448     WCHAR  first;
2449
2450     if (*lpStr2 == 0)
2451         return ((LPWSTR)lpStr1);
2452     len1 = 0;
2453     while (lpStr1[len1] != 0) ++len1;
2454     len2 = 0;
2455     while (lpStr2[len2] != 0) ++len2;
2456     if (len2 == 0)
2457         return ((LPWSTR)(lpStr1 + len1));
2458     first = tolowerW (*lpStr2);
2459     while (len1 >= len2) {
2460         if (tolowerW (*lpStr1) == first) {
2461             for (i = 1; i < len2; ++i)
2462                 if (tolowerW (lpStr1[i]) != tolowerW(lpStr2[i]))
2463                     break;
2464             if (i >= len2)
2465                 return ((LPWSTR)lpStr1);
2466         }
2467         ++lpStr1; --len1;
2468     }
2469     return (NULL);
2470 }
2471
2472 /**************************************************************************
2473  * StrToIntW [COMCTL32.365] Converts a wide char string to a signed integer.
2474  */
2475
2476 INT WINAPI
2477 COMCTL32_StrToIntW (LPWSTR lpString)
2478 {
2479     return atoiW(lpString);
2480 }
2481
2482
2483 /**************************************************************************
2484  * DPA_EnumCallback [COMCTL32.385]
2485  *
2486  * Enumerates all items in a dynamic pointer array.
2487  *
2488  * PARAMS
2489  *     hdpa     [I] handle to the dynamic pointer array
2490  *     enumProc [I]
2491  *     lParam   [I]
2492  *
2493  * RETURNS
2494  *     none
2495  */
2496
2497 VOID WINAPI
2498 DPA_EnumCallback (const HDPA hdpa, DPAENUMPROC enumProc, LPARAM lParam)
2499 {
2500     INT i;
2501
2502     TRACE("(%p %p %08lx)\n", hdpa, enumProc, lParam);
2503
2504     if (!hdpa)
2505         return;
2506     if (hdpa->nItemCount <= 0)
2507         return;
2508
2509     for (i = 0; i < hdpa->nItemCount; i++) {
2510         if ((enumProc)(hdpa->ptrs[i], lParam) == 0)
2511             return;
2512     }
2513
2514     return;
2515 }
2516
2517
2518 /**************************************************************************
2519  * DPA_DestroyCallback [COMCTL32.386]
2520  *
2521  * Enumerates all items in a dynamic pointer array and destroys it.
2522  *
2523  * PARAMS
2524  *     hdpa     [I] handle to the dynamic pointer array
2525  *     enumProc [I]
2526  *     lParam   [I]
2527  *
2528  * RETURNS
2529  *     Success: TRUE
2530  *     Failure: FALSE
2531  */
2532
2533 BOOL WINAPI
2534 DPA_DestroyCallback (const HDPA hdpa, DPAENUMPROC enumProc, LPARAM lParam)
2535 {
2536     TRACE("(%p %p %08lx)\n", hdpa, enumProc, lParam);
2537
2538     DPA_EnumCallback (hdpa, enumProc, lParam);
2539
2540     return DPA_Destroy (hdpa);
2541 }
2542
2543
2544 /**************************************************************************
2545  * DSA_EnumCallback [COMCTL32.387]
2546  *
2547  * Enumerates all items in a dynamic storage array.
2548  *
2549  * PARAMS
2550  *     hdsa     [I] handle to the dynamic storage array
2551  *     enumProc [I]
2552  *     lParam   [I]
2553  *
2554  * RETURNS
2555  *     none
2556  */
2557
2558 VOID WINAPI
2559 DSA_EnumCallback (const HDSA hdsa, DSAENUMPROC enumProc, LPARAM lParam)
2560 {
2561     INT i;
2562
2563     TRACE("(%p %p %08lx)\n", hdsa, enumProc, lParam);
2564
2565     if (!hdsa)
2566         return;
2567     if (hdsa->nItemCount <= 0)
2568         return;
2569
2570     for (i = 0; i < hdsa->nItemCount; i++) {
2571         LPVOID lpItem = DSA_GetItemPtr (hdsa, i);
2572         if ((enumProc)(lpItem, lParam) == 0)
2573             return;
2574     }
2575
2576     return;
2577 }
2578
2579
2580 /**************************************************************************
2581  * DSA_DestroyCallback [COMCTL32.388]
2582  *
2583  * Enumerates all items in a dynamic storage array and destroys it.
2584  *
2585  * PARAMS
2586  *     hdsa     [I] handle to the dynamic storage array
2587  *     enumProc [I]
2588  *     lParam   [I]
2589  *
2590  * RETURNS
2591  *     Success: TRUE
2592  *     Failure: FALSE
2593  */
2594
2595 BOOL WINAPI
2596 DSA_DestroyCallback (const HDSA hdsa, DSAENUMPROC enumProc, LPARAM lParam)
2597 {
2598     TRACE("(%p %p %08lx)\n", hdsa, enumProc, lParam);
2599
2600     DSA_EnumCallback (hdsa, enumProc, lParam);
2601
2602     return DSA_Destroy (hdsa);
2603 }
2604
2605 /**************************************************************************
2606  * StrCSpnA [COMCTL32.356]
2607  *
2608  */
2609 INT WINAPI COMCTL32_StrCSpnA( LPCSTR lpStr, LPCSTR lpSet) {
2610   return strcspn(lpStr, lpSet);
2611 }
2612
2613 /**************************************************************************
2614  * StrChrW [COMCTL32.358]
2615  *
2616  */
2617 LPWSTR WINAPI COMCTL32_StrChrW( LPCWSTR lpStart, WORD wMatch) {
2618   return strchrW(lpStart, wMatch);
2619 }
2620
2621 /**************************************************************************
2622  * StrCmpNA [COMCTL32.352]
2623  *
2624  */
2625 INT WINAPI COMCTL32_StrCmpNA( LPCSTR lpStr1, LPCSTR lpStr2, int nChar) {
2626   return strncmp(lpStr1, lpStr2, nChar);
2627 }
2628
2629 /**************************************************************************
2630  * StrCmpNIA [COMCTL32.353]
2631  *
2632  */
2633 INT WINAPI COMCTL32_StrCmpNIA( LPCSTR lpStr1, LPCSTR lpStr2, int nChar) {
2634   return strncasecmp(lpStr1, lpStr2, nChar);
2635 }
2636
2637 /**************************************************************************
2638  * StrCmpNW [COMCTL32.360]
2639  *
2640  */
2641 INT WINAPI COMCTL32_StrCmpNW( LPCWSTR lpStr1, LPCWSTR lpStr2, int nChar) {
2642   return strncmpW(lpStr1, lpStr2, nChar);
2643 }
2644
2645 /**************************************************************************
2646  * StrCmpNIW [COMCTL32.361]
2647  *
2648  */
2649 INT WINAPI COMCTL32_StrCmpNIW( LPCWSTR lpStr1, LPCWSTR lpStr2, int nChar) {
2650   FIXME("(%s, %s, %i): stub\n", debugstr_w(lpStr1), debugstr_w(lpStr2), nChar);
2651   return 0;
2652 }
2653
2654 /**************************************************************************
2655  * StrRChrA [COMCTL32.351]
2656  *
2657  */
2658 LPSTR WINAPI COMCTL32_StrRChrA( LPCSTR lpStart, LPCSTR lpEnd, WORD wMatch )
2659 {
2660     LPCSTR lpGotIt = NULL;
2661     BOOL dbcs = IsDBCSLeadByte( LOBYTE(wMatch) );
2662
2663     TRACE("(%p, %p, %x)\n", lpStart, lpEnd, wMatch);
2664
2665     if (!lpEnd) lpEnd = lpStart + strlen(lpStart);
2666
2667     for(; lpStart < lpEnd; lpStart = CharNextA(lpStart))
2668     {
2669         if (*lpStart != LOBYTE(wMatch)) continue;
2670         if (dbcs && lpStart[1] != HIBYTE(wMatch)) continue;
2671         lpGotIt = lpStart;
2672     }
2673     return (LPSTR)lpGotIt;
2674 }
2675
2676
2677 /**************************************************************************
2678  * StrRChrW [COMCTL32.359]
2679  *
2680  */
2681 LPWSTR WINAPI COMCTL32_StrRChrW( LPCWSTR lpStart, LPCWSTR lpEnd, WORD wMatch)
2682 {
2683     LPCWSTR lpGotIt = NULL;
2684
2685     TRACE("(%p, %p, %x)\n", lpStart, lpEnd, wMatch);
2686     if (!lpEnd) lpEnd = lpStart + strlenW(lpStart);
2687
2688     for(; lpStart < lpEnd; lpStart = CharNextW(lpStart))
2689         if (*lpStart == wMatch) lpGotIt = lpStart;
2690
2691     return (LPWSTR)lpGotIt;
2692 }
2693
2694
2695 /**************************************************************************
2696  * StrStrA [COMCTL32.354]
2697  *
2698  */
2699 LPSTR WINAPI COMCTL32_StrStrA( LPCSTR lpFirst, LPCSTR lpSrch) {
2700   return strstr(lpFirst, lpSrch);
2701 }
2702
2703 /**************************************************************************
2704  * StrStrW [COMCTL32.362]
2705  *
2706  */
2707 LPWSTR WINAPI COMCTL32_StrStrW( LPCWSTR lpFirst, LPCWSTR lpSrch) {
2708   return strstrW(lpFirst, lpSrch);
2709 }
2710
2711 /**************************************************************************
2712  * StrSpnW [COMCTL32.364]
2713  *
2714  */
2715 INT WINAPI COMCTL32_StrSpnW( LPWSTR lpStr, LPWSTR lpSet) {
2716   LPWSTR lpLoop = lpStr;
2717
2718   /* validate ptr */
2719   if ((lpStr == 0) || (lpSet == 0)) return 0;
2720
2721 /* while(*lpLoop) { if lpLoop++; } */
2722
2723   for(; (*lpLoop != 0); lpLoop++)
2724     if( strchrW(lpSet, *(WORD*)lpLoop))
2725       return (INT)(lpLoop-lpStr);
2726
2727   return (INT)(lpLoop-lpStr);
2728 }
2729
2730 /**************************************************************************
2731  * @ [COMCTL32.410]
2732  *
2733  * FIXME: What's this supposed to do?
2734  *        Parameter 1 is an HWND, you're on your own for the rest.
2735  */
2736
2737 BOOL WINAPI COMCTL32_410( HWND hw, DWORD b, DWORD c, DWORD d) {
2738
2739    FIXME("(%p, %lx, %lx, %lx): stub!\n", hw, b, c, d);
2740
2741    return TRUE;
2742 }
2743
2744 /**************************************************************************
2745  * @ [COMCTL32.411]
2746  *
2747  * FIXME: What's this supposed to do?
2748  *        Parameter 1 is an HWND, you're on your own for the rest.
2749  */
2750
2751 BOOL WINAPI COMCTL32_411( HWND hw, DWORD b, DWORD c) {
2752
2753    FIXME("(%p, %lx, %lx): stub!\n", hw, b, c);
2754
2755    return TRUE;
2756 }
2757
2758 /**************************************************************************
2759  * @ [COMCTL32.412]
2760  *
2761  * FIXME: What's this supposed to do?
2762  *        Parameter 1 is an HWND, you're on your own for the rest.
2763  */
2764
2765 BOOL WINAPI COMCTL32_412( HWND hwnd, DWORD b, DWORD c)
2766 {
2767     FIXME("(%p, %lx, %lx): stub!\n", hwnd, b, c);
2768
2769     if (IsWindow (hwnd) == FALSE)
2770         return FALSE;
2771
2772     if (b == 0)
2773         return FALSE;
2774
2775
2776     return TRUE;
2777 }
2778
2779 /**************************************************************************
2780  * @ [COMCTL32.413]
2781  *
2782  * FIXME: What's this supposed to do?
2783  *        Parameter 1 is an HWND, you're on your own for the rest.
2784  */
2785
2786 BOOL WINAPI COMCTL32_413( HWND hw, DWORD b, DWORD c, DWORD d) {
2787
2788    FIXME("(%p, %lx, %lx, %lx): stub!\n", hw, b, c, d);
2789
2790    return TRUE;
2791 }
2792
2793
2794 /**************************************************************************
2795  * @ [COMCTL32.415]
2796  *
2797  * FIXME: What's this supposed to do?
2798  *        Parameter 1 is an HWND, you're on your own for the rest.
2799  */
2800
2801 BOOL WINAPI COMCTL32_415( HWND hwnd, DWORD b, DWORD c, DWORD d, DWORD e)
2802 {
2803
2804    FIXME("(%p, %lx, %lx, %lx, %lx): stub!\n", hwnd, b, c, d, e);
2805
2806    return TRUE;
2807 }
2808
2809 /**************************************************************************
2810  * @ [COMCTL32.417]
2811  *
2812  */
2813 BOOL WINAPI COMCTL32_417(HDC hdc, INT x, INT y, UINT flags, const RECT *lprect,
2814                          LPCWSTR str, UINT count, const INT *lpDx)
2815 {
2816     return ExtTextOutW(hdc, x, y, flags, lprect, str, count, lpDx);
2817 }
2818
2819 /**************************************************************************
2820  * @ [COMCTL32.419]
2821  *
2822  * FIXME: What's this supposed to do?
2823  */
2824
2825 BOOL WINAPI COMCTL32_419( DWORD a, DWORD b, DWORD c, DWORD d)
2826 {
2827
2828    FIXME("(%lx, %lx, %lx, %lx): stub!\n", a, b, c, d);
2829
2830    return TRUE;
2831 }