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