Better implementation of GetCalendarInfo{A,W}, not perfect.
[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 INT __cdecl _wtoi(LPWSTR string);
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     INT 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     INT 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     INT 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("(%08x, %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("(%08x, %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("(%08x, %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("(%08x, %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("(%08x, %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     INT 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("(%08x, %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("(%08x, %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->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     INT   nNewItems, nSize, nIndex = 0;
1881     LPVOID  *lpTemp, *lpDest;
1882
1883     TRACE("(%p %d %p)\n", hdpa, i, p);
1884
1885     if ((!hdpa) || (i < 0))
1886         return -1;
1887
1888     if (!hdpa->ptrs) {
1889         hdpa->ptrs =
1890             (LPVOID*)HeapAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
1891                                 2 * hdpa->nGrow * sizeof(LPVOID));
1892         if (!hdpa->ptrs)
1893             return -1;
1894         hdpa->nMaxCount = hdpa->nGrow * 2;
1895         nIndex = 0;
1896     }
1897     else {
1898         if (hdpa->nItemCount >= hdpa->nMaxCount) {
1899             TRACE("-- resizing\n");
1900             nNewItems = hdpa->nMaxCount + hdpa->nGrow;
1901             nSize = nNewItems * sizeof(LPVOID);
1902
1903             lpTemp = (LPVOID*)HeapReAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
1904                                            hdpa->ptrs, nSize);
1905             if (!lpTemp)
1906                 return -1;
1907             hdpa->nMaxCount = nNewItems;
1908             hdpa->ptrs = lpTemp;
1909         }
1910
1911         if (i >= hdpa->nItemCount) {
1912             nIndex = hdpa->nItemCount;
1913             TRACE("-- appending at %d\n", nIndex);
1914         }
1915         else {
1916             TRACE("-- inserting at %d\n", i);
1917             lpTemp = hdpa->ptrs + i;
1918             lpDest = lpTemp + 1;
1919             nSize  = (hdpa->nItemCount - i) * sizeof(LPVOID);
1920             TRACE("-- move dest=%p src=%p size=%x\n",
1921                    lpDest, lpTemp, nSize);
1922             memmove (lpDest, lpTemp, nSize);
1923             nIndex = i;
1924         }
1925     }
1926
1927     /* insert item */
1928     hdpa->nItemCount++;
1929     hdpa->ptrs[nIndex] = p;
1930
1931     return nIndex;
1932 }
1933
1934
1935 /**************************************************************************
1936  * DPA_SetPtr [COMCTL32.335]
1937  *
1938  * Sets a pointer in the pointer array
1939  *
1940  * PARAMS
1941  *     hdpa [I] handle (pointer) to the pointer array
1942  *     i    [I] index of the pointer that will be set
1943  *     p    [I] pointer to be set
1944  *
1945  * RETURNS
1946  *     Success: TRUE
1947  *     Failure: FALSE
1948  */
1949
1950 BOOL WINAPI
1951 DPA_SetPtr (const HDPA hdpa, INT i, LPVOID p)
1952 {
1953     LPVOID *lpTemp;
1954     
1955     TRACE("(%p %d %p)\n", hdpa, i, p);
1956
1957     if ((!hdpa) || i < 0)
1958         return FALSE;
1959       
1960     if (hdpa->nItemCount <= i) {
1961         /* within the old array */
1962         if (hdpa->nMaxCount > i) {
1963             /* within the allocated space, set a new boundary */
1964             hdpa->nItemCount = i+1;
1965         }
1966         else {
1967             /* resize the block of memory */
1968             INT nNewItems =
1969                 hdpa->nGrow * ((INT)(((i+1) - 1) / hdpa->nGrow) + 1);
1970             INT nSize = nNewItems * sizeof(LPVOID);
1971
1972             lpTemp = (LPVOID*)HeapReAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
1973                                            hdpa->ptrs, nSize);
1974             if (!lpTemp)
1975                 return FALSE;
1976
1977             hdpa->nItemCount = nNewItems;
1978             hdpa->ptrs = lpTemp;        
1979         }    
1980     }
1981
1982     /* put the new entry in */
1983     hdpa->ptrs[i] = p;
1984
1985     return TRUE;
1986 }
1987
1988
1989 /**************************************************************************
1990  * DPA_DeletePtr [COMCTL32.336]
1991  *
1992  * Removes a pointer from the pointer array.
1993  *
1994  * PARAMS
1995  *     hdpa [I] handle (pointer) to the pointer array
1996  *     i    [I] index of the pointer that will be deleted
1997  *
1998  * RETURNS
1999  *     Success: deleted pointer
2000  *     Failure: NULL
2001  */
2002
2003 LPVOID WINAPI
2004 DPA_DeletePtr (const HDPA hdpa, INT i)
2005 {
2006     LPVOID *lpDest, *lpSrc, lpTemp = NULL;
2007     INT  nSize;
2008     
2009     TRACE("(%p %d)\n", hdpa, i);
2010
2011     if ((!hdpa) || i < 0 || i >= hdpa->nItemCount)
2012         return NULL;
2013
2014     lpTemp = hdpa->ptrs[i];
2015
2016     /* do we need to move ?*/
2017     if (i < hdpa->nItemCount - 1) {
2018         lpDest = hdpa->ptrs + i;
2019         lpSrc = lpDest + 1;
2020         nSize = (hdpa->nItemCount - i - 1) * sizeof(LPVOID);
2021         TRACE("-- move dest=%p src=%p size=%x\n",
2022                lpDest, lpSrc, nSize);
2023         memmove (lpDest, lpSrc, nSize);
2024     }
2025     
2026     hdpa->nItemCount --;
2027     
2028     /* free memory ?*/
2029     if ((hdpa->nMaxCount - hdpa->nItemCount) >= hdpa->nGrow) {
2030         INT nNewItems = max(hdpa->nGrow * 2, hdpa->nItemCount);
2031         nSize = nNewItems * sizeof(LPVOID);
2032         lpDest = (LPVOID)HeapReAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
2033                                       hdpa->ptrs, nSize);
2034         if (!lpDest)
2035             return NULL;
2036
2037         hdpa->nMaxCount = nNewItems;
2038         hdpa->ptrs = (LPVOID*)lpDest;         
2039     }
2040
2041     return lpTemp;
2042 }
2043
2044
2045 /**************************************************************************
2046  * DPA_DeleteAllPtrs [COMCTL32.337]
2047  *
2048  * Removes all pointers and reinitializes the array.
2049  *
2050  * PARAMS
2051  *     hdpa [I] handle (pointer) to the pointer array
2052  *
2053  * RETURNS
2054  *     Success: TRUE
2055  *     Failure: FALSE
2056  */
2057
2058 BOOL WINAPI
2059 DPA_DeleteAllPtrs (const HDPA hdpa)
2060 {
2061     TRACE("(%p)\n", hdpa);
2062
2063     if (!hdpa) 
2064         return FALSE;
2065
2066     if (hdpa->ptrs && (!HeapFree (hdpa->hHeap, 0, hdpa->ptrs)))
2067         return FALSE;
2068
2069     hdpa->nItemCount = 0;
2070     hdpa->nMaxCount = hdpa->nGrow * 2;
2071     hdpa->ptrs = (LPVOID*)HeapAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
2072                                      hdpa->nMaxCount * sizeof(LPVOID));
2073
2074     return TRUE;
2075 }
2076
2077
2078 /**************************************************************************
2079  * DPA_QuickSort [Internal]
2080  *
2081  * Ordinary quicksort (used by DPA_Sort).
2082  *
2083  * PARAMS
2084  *     lpPtrs     [I] pointer to the pointer array
2085  *     l          [I] index of the "left border" of the partition
2086  *     r          [I] index of the "right border" of the partition
2087  *     pfnCompare [I] pointer to the compare function
2088  *     lParam     [I] user defined value (3rd parameter in compare function)
2089  *
2090  * RETURNS
2091  *     NONE
2092  */
2093
2094 static VOID
2095 DPA_QuickSort (LPVOID *lpPtrs, INT l, INT r,
2096                PFNDPACOMPARE pfnCompare, LPARAM lParam)
2097 {
2098     INT m;
2099     LPVOID t;
2100
2101     TRACE("l=%i r=%i\n", l, r);
2102  
2103     if (l==r)    /* one element is always sorted */
2104         return;
2105     if (r<l)     /* oops, got it in the wrong order */
2106         {
2107         DPA_QuickSort(lpPtrs, r, l, pfnCompare, lParam);
2108         return;
2109         }
2110     m = (l+r)/2; /* divide by two */
2111     DPA_QuickSort(lpPtrs, l, m, pfnCompare, lParam);
2112     DPA_QuickSort(lpPtrs, m+1, r, pfnCompare, lParam);
2113
2114     /* join the two sides */
2115     while( (l<=m) && (m<r) ) 
2116     {
2117         if(pfnCompare(lpPtrs[l],lpPtrs[m+1],lParam)>0)
2118         {
2119             t = lpPtrs[m+1];
2120             memmove(&lpPtrs[l+1],&lpPtrs[l],(m-l+1)*sizeof lpPtrs[l]);
2121             lpPtrs[l] = t;
2122
2123             m++;
2124         }
2125         l++;
2126     }
2127 }
2128
2129
2130 /**************************************************************************
2131  * DPA_Sort [COMCTL32.338]
2132  *
2133  * Sorts a pointer array using a user defined compare function
2134  *
2135  * PARAMS
2136  *     hdpa       [I] handle (pointer) to the pointer array
2137  *     pfnCompare [I] pointer to the compare function
2138  *     lParam     [I] user defined value (3rd parameter of compare function)
2139  *
2140  * RETURNS
2141  *     Success: TRUE
2142  *     Failure: FALSE
2143  */
2144
2145 BOOL WINAPI
2146 DPA_Sort (const HDPA hdpa, PFNDPACOMPARE pfnCompare, LPARAM lParam)
2147 {
2148     if (!hdpa || !pfnCompare)
2149         return FALSE;
2150
2151     TRACE("(%p %p 0x%lx)\n", hdpa, pfnCompare, lParam);
2152
2153     if ((hdpa->nItemCount > 1) && (hdpa->ptrs))
2154         DPA_QuickSort (hdpa->ptrs, 0, hdpa->nItemCount - 1,
2155                        pfnCompare, lParam);
2156
2157     return TRUE;
2158 }
2159
2160
2161 /**************************************************************************
2162  * DPA_Search [COMCTL32.339]
2163  *
2164  * Searches a pointer array for a specified pointer
2165  *
2166  * PARAMS
2167  *     hdpa       [I] handle (pointer) to the pointer array
2168  *     pFind      [I] pointer to search for
2169  *     nStart     [I] start index
2170  *     pfnCompare [I] pointer to the compare function
2171  *     lParam     [I] user defined value (3rd parameter of compare function)
2172  *     uOptions   [I] search options
2173  *
2174  * RETURNS
2175  *     Success: index of the pointer in the array.
2176  *     Failure: -1
2177  *
2178  * NOTES
2179  *     Binary search taken from R.Sedgewick "Algorithms in C"!
2180  *     Function is NOT tested!
2181  *     If something goes wrong, blame HIM not ME! (Eric Kohl)
2182  */
2183
2184 INT WINAPI
2185 DPA_Search (const HDPA hdpa, LPVOID pFind, INT nStart,
2186             PFNDPACOMPARE pfnCompare, LPARAM lParam, UINT uOptions)
2187 {
2188     if (!hdpa || !pfnCompare || !pFind)
2189         return -1;
2190
2191     TRACE("(%p %p %d %p 0x%08lx 0x%08x)\n",
2192            hdpa, pFind, nStart, pfnCompare, lParam, uOptions);
2193
2194     if (uOptions & DPAS_SORTED) {
2195         /* array is sorted --> use binary search */
2196         INT l, r, x, n;
2197         LPVOID *lpPtr;
2198
2199         TRACE("binary search\n");
2200
2201         l = (nStart == -1) ? 0 : nStart;
2202         r = hdpa->nItemCount - 1;
2203         lpPtr = hdpa->ptrs;
2204         while (r >= l) {
2205             x = (l + r) / 2;
2206             n = (pfnCompare)(pFind, lpPtr[x], lParam);
2207             if (n < 0)
2208                 r = x - 1;
2209             else
2210                 l = x + 1;
2211             if (n == 0) {
2212                 TRACE("-- ret=%d\n", n);
2213                 return n;
2214             }
2215         }
2216
2217         if (uOptions & DPAS_INSERTBEFORE) {
2218             TRACE("-- ret=%d\n", r);
2219             return r;
2220         }
2221
2222         if (uOptions & DPAS_INSERTAFTER) {
2223             TRACE("-- ret=%d\n", l);
2224             return l;
2225         }
2226     }
2227     else {
2228         /* array is not sorted --> use linear search */
2229         LPVOID *lpPtr;
2230         INT  nIndex;
2231
2232         TRACE("linear search\n");
2233         
2234         nIndex = (nStart == -1)? 0 : nStart;
2235         lpPtr = hdpa->ptrs;
2236         for (; nIndex < hdpa->nItemCount; nIndex++) {
2237             if ((pfnCompare)(pFind, lpPtr[nIndex], lParam) == 0) {
2238                 TRACE("-- ret=%d\n", nIndex);
2239                 return nIndex;
2240             }
2241         }
2242     }
2243
2244     TRACE("-- not found: ret=-1\n");
2245     return -1;
2246 }
2247
2248
2249 /**************************************************************************
2250  * DPA_CreateEx [COMCTL32.340]
2251  *
2252  * Creates a dynamic pointer array using the specified size and heap.
2253  *
2254  * PARAMS
2255  *     nGrow [I] number of items by which the array grows when it is filled
2256  *     hHeap [I] handle to the heap where the array is stored
2257  *
2258  * RETURNS
2259  *     Success: handle (pointer) to the pointer array.
2260  *     Failure: NULL
2261  */
2262
2263 HDPA WINAPI
2264 DPA_CreateEx (INT nGrow, HANDLE hHeap)
2265 {
2266     HDPA hdpa;
2267
2268     TRACE("(%d 0x%x)\n", nGrow, hHeap);
2269
2270     if (hHeap)
2271         hdpa = (HDPA)HeapAlloc (hHeap, HEAP_ZERO_MEMORY, sizeof(DPA));
2272     else
2273         hdpa = (HDPA)COMCTL32_Alloc (sizeof(DPA));
2274
2275     if (hdpa) {
2276         hdpa->nGrow = min(8, nGrow);
2277         hdpa->hHeap = hHeap ? hHeap : COMCTL32_hHeap;
2278         hdpa->nMaxCount = hdpa->nGrow * 2;
2279         hdpa->ptrs =
2280             (LPVOID*)HeapAlloc (hHeap, HEAP_ZERO_MEMORY,
2281                                 hdpa->nMaxCount * sizeof(LPVOID));
2282     }
2283
2284     TRACE("-- %p\n", hdpa);
2285
2286     return hdpa;
2287 }
2288
2289
2290 /**************************************************************************
2291  * Notification functions
2292  */
2293
2294 typedef struct tagNOTIFYDATA
2295 {
2296     HWND hwndFrom;
2297     HWND hwndTo;
2298     DWORD  dwParam3;
2299     DWORD  dwParam4;
2300     DWORD  dwParam5;
2301     DWORD  dwParam6;
2302 } NOTIFYDATA, *LPNOTIFYDATA;
2303
2304
2305 /**************************************************************************
2306  * DoNotify [Internal]
2307  */
2308
2309 static LRESULT
2310 DoNotify (LPNOTIFYDATA lpNotify, UINT uCode, LPNMHDR lpHdr)
2311 {
2312     NMHDR nmhdr;
2313     LPNMHDR lpNmh = NULL;
2314     UINT idFrom = 0;
2315
2316     TRACE("(0x%04x 0x%04x %d %p 0x%08lx)\n",
2317            lpNotify->hwndFrom, lpNotify->hwndTo, uCode, lpHdr,
2318            lpNotify->dwParam5);
2319
2320     if (!lpNotify->hwndTo)
2321         return 0;
2322
2323     if (lpNotify->hwndFrom == -1) {
2324         lpNmh = lpHdr;
2325         idFrom = lpHdr->idFrom;
2326     }
2327     else {
2328         if (lpNotify->hwndFrom) {
2329             HWND hwndParent = GetParent (lpNotify->hwndFrom);
2330             if (hwndParent) {
2331                 hwndParent = GetWindow (lpNotify->hwndFrom, GW_OWNER);
2332                 /* the following is done even if the return from above
2333                  * is zero.  GLA 12/2001 */
2334                 idFrom = GetDlgCtrlID (lpNotify->hwndFrom);
2335             }
2336         }
2337
2338         lpNmh = (lpHdr) ? lpHdr : &nmhdr;
2339
2340         lpNmh->hwndFrom = lpNotify->hwndFrom;
2341         lpNmh->idFrom = idFrom;
2342         lpNmh->code = uCode;
2343     }
2344
2345     return SendMessageA (lpNotify->hwndTo, WM_NOTIFY, idFrom, (LPARAM)lpNmh);
2346 }
2347
2348
2349 /**************************************************************************
2350  * SendNotify [COMCTL32.341]
2351  *
2352  * PARAMS
2353  *     hwndTo   [I]
2354  *     hwndFrom [I]
2355  *     uCode    [I]
2356  *     lpHdr    [I]
2357  *
2358  * RETURNS
2359  *     Success: return value from notification
2360  *     Failure: 0
2361  */
2362
2363 LRESULT WINAPI
2364 COMCTL32_SendNotify (HWND hwndTo, HWND hwndFrom,
2365                      UINT uCode, LPNMHDR lpHdr)
2366 {
2367     NOTIFYDATA notify;
2368
2369     TRACE("(0x%04x 0x%04x %d %p)\n",
2370            hwndTo, hwndFrom, uCode, lpHdr);
2371
2372     notify.hwndFrom = hwndFrom;
2373     notify.hwndTo   = hwndTo;
2374     notify.dwParam5 = 0;
2375     notify.dwParam6 = 0;
2376
2377     return DoNotify (&notify, uCode, lpHdr);
2378 }
2379
2380
2381 /**************************************************************************
2382  * SendNotifyEx [COMCTL32.342]
2383  *
2384  * PARAMS
2385  *     hwndFrom [I]
2386  *     hwndTo   [I]
2387  *     uCode    [I]
2388  *     lpHdr    [I]
2389  *     dwParam5 [I]
2390  *
2391  * RETURNS
2392  *     Success: return value from notification
2393  *     Failure: 0
2394  */
2395
2396 LRESULT WINAPI
2397 COMCTL32_SendNotifyEx (HWND hwndTo, HWND hwndFrom, UINT uCode,
2398                        LPNMHDR lpHdr, DWORD dwParam5)
2399 {
2400     NOTIFYDATA notify;
2401     HWND hwndNotify;
2402
2403     TRACE("(0x%04x 0x%04x %d %p 0x%08lx)\n",
2404            hwndFrom, hwndTo, uCode, lpHdr, dwParam5);
2405
2406     hwndNotify = hwndTo;
2407     if (!hwndTo) {
2408         if (IsWindow (hwndFrom)) {
2409             hwndNotify = GetParent (hwndFrom);
2410             if (!hwndNotify)
2411                 return 0;
2412         }
2413     }
2414
2415     notify.hwndFrom = hwndFrom;
2416     notify.hwndTo   = hwndNotify;
2417     notify.dwParam5 = dwParam5;
2418     notify.dwParam6 = 0;
2419
2420     return DoNotify (&notify, uCode, lpHdr);
2421 }
2422
2423
2424 /**************************************************************************
2425  * StrChrA [COMCTL32.350]
2426  *
2427  */
2428
2429 LPSTR WINAPI
2430 COMCTL32_StrChrA (LPCSTR lpString, CHAR cChar)
2431 {
2432     return strchr (lpString, cChar);
2433 }
2434
2435
2436 /**************************************************************************
2437  * StrStrIA [COMCTL32.355]
2438  */
2439
2440 LPSTR WINAPI
2441 COMCTL32_StrStrIA (LPCSTR lpStr1, LPCSTR lpStr2)
2442 {
2443     INT len1, len2, i;
2444     CHAR  first;
2445
2446     if (*lpStr2 == 0)
2447         return ((LPSTR)lpStr1);
2448     len1 = 0;
2449     while (lpStr1[len1] != 0) ++len1;
2450     len2 = 0;
2451     while (lpStr2[len2] != 0) ++len2;
2452     if (len2 == 0)
2453         return ((LPSTR)(lpStr1 + len1));
2454     first = tolower (*lpStr2);
2455     while (len1 >= len2) {
2456         if (tolower(*lpStr1) == first) {
2457             for (i = 1; i < len2; ++i)
2458                 if (tolower (lpStr1[i]) != tolower(lpStr2[i]))
2459                     break;
2460             if (i >= len2)
2461                 return ((LPSTR)lpStr1);
2462         }
2463         ++lpStr1; --len1;
2464     }
2465     return (NULL);
2466 }
2467
2468
2469 /**************************************************************************
2470  * StrToIntA [COMCTL32.357] Converts a string to a signed integer.
2471  */
2472
2473 INT WINAPI
2474 COMCTL32_StrToIntA (LPSTR lpString)
2475 {
2476     return atoi(lpString);
2477 }
2478
2479 /**************************************************************************
2480  * StrToIntW [COMCTL32.365] Converts a wide char string to a signed integer.
2481  */
2482
2483 INT WINAPI
2484 COMCTL32_StrToIntW (LPWSTR lpString)
2485 {
2486     return _wtoi(lpString);
2487 }
2488
2489
2490 /**************************************************************************
2491  * DPA_EnumCallback [COMCTL32.385]
2492  *
2493  * Enumerates all items in a dynamic pointer array.
2494  *
2495  * PARAMS
2496  *     hdpa     [I] handle to the dynamic pointer array
2497  *     enumProc [I]
2498  *     lParam   [I] 
2499  *
2500  * RETURNS
2501  *     none
2502  */
2503
2504 VOID WINAPI
2505 DPA_EnumCallback (const HDPA hdpa, DPAENUMPROC enumProc, LPARAM lParam)
2506 {
2507     INT i;
2508
2509     TRACE("(%p %p %08lx)\n", hdpa, enumProc, lParam);
2510
2511     if (!hdpa)
2512         return;
2513     if (hdpa->nItemCount <= 0)
2514         return;
2515
2516     for (i = 0; i < hdpa->nItemCount; i++) {
2517         if ((enumProc)(hdpa->ptrs[i], lParam) == 0)
2518             return;
2519     }
2520
2521     return;
2522 }
2523
2524
2525 /**************************************************************************
2526  * DPA_DestroyCallback [COMCTL32.386]
2527  *
2528  * Enumerates all items in a dynamic pointer array and destroys it.
2529  *
2530  * PARAMS
2531  *     hdpa     [I] handle to the dynamic pointer array
2532  *     enumProc [I]
2533  *     lParam   [I]
2534  *
2535  * RETURNS
2536  *     Success: TRUE
2537  *     Failure: FALSE
2538  */
2539
2540 BOOL WINAPI
2541 DPA_DestroyCallback (const HDPA hdpa, DPAENUMPROC enumProc, LPARAM lParam)
2542 {
2543     TRACE("(%p %p %08lx)\n", hdpa, enumProc, lParam);
2544
2545     DPA_EnumCallback (hdpa, enumProc, lParam);
2546
2547     return DPA_Destroy (hdpa);
2548 }
2549
2550
2551 /**************************************************************************
2552  * DSA_EnumCallback [COMCTL32.387]
2553  *
2554  * Enumerates all items in a dynamic storage array.
2555  *
2556  * PARAMS
2557  *     hdsa     [I] handle to the dynamic storage array
2558  *     enumProc [I]
2559  *     lParam   [I]
2560  *
2561  * RETURNS
2562  *     none
2563  */
2564
2565 VOID WINAPI
2566 DSA_EnumCallback (const HDSA hdsa, DSAENUMPROC enumProc, LPARAM lParam)
2567 {
2568     INT i;
2569
2570     TRACE("(%p %p %08lx)\n", hdsa, enumProc, lParam);
2571
2572     if (!hdsa)
2573         return;
2574     if (hdsa->nItemCount <= 0)
2575         return;
2576
2577     for (i = 0; i < hdsa->nItemCount; i++) {
2578         LPVOID lpItem = DSA_GetItemPtr (hdsa, i);
2579         if ((enumProc)(lpItem, lParam) == 0)
2580             return;
2581     }
2582
2583     return;
2584 }
2585
2586
2587 /**************************************************************************
2588  * DSA_DestroyCallback [COMCTL32.388]
2589  *
2590  * Enumerates all items in a dynamic storage array and destroys it.
2591  *
2592  * PARAMS
2593  *     hdsa     [I] handle to the dynamic storage array
2594  *     enumProc [I]
2595  *     lParam   [I]
2596  *
2597  * RETURNS
2598  *     Success: TRUE
2599  *     Failure: FALSE
2600  */
2601
2602 BOOL WINAPI
2603 DSA_DestroyCallback (const HDSA hdsa, DSAENUMPROC enumProc, LPARAM lParam)
2604 {
2605     TRACE("(%p %p %08lx)\n", hdsa, enumProc, lParam);
2606
2607     DSA_EnumCallback (hdsa, enumProc, lParam);
2608
2609     return DSA_Destroy (hdsa);
2610 }
2611
2612 /**************************************************************************
2613  * StrCSpnA [COMCTL32.356]
2614  *
2615  */
2616 INT WINAPI COMCTL32_StrCSpnA( LPCSTR lpStr, LPCSTR lpSet) {
2617   return strcspn(lpStr, lpSet);
2618 }
2619
2620 /**************************************************************************
2621  * StrChrW [COMCTL32.358]
2622  *
2623  */
2624 LPWSTR WINAPI COMCTL32_StrChrW( LPCWSTR lpStart, WORD wMatch) {
2625   return strchrW(lpStart, wMatch);
2626 }
2627
2628 /**************************************************************************
2629  * StrCmpNA [COMCTL32.352]
2630  *
2631  */
2632 INT WINAPI COMCTL32_StrCmpNA( LPCSTR lpStr1, LPCSTR lpStr2, int nChar) {
2633   return strncmp(lpStr1, lpStr2, nChar);
2634 }
2635
2636 /**************************************************************************
2637  * StrCmpNIA [COMCTL32.353]
2638  *
2639  */
2640 INT WINAPI COMCTL32_StrCmpNIA( LPCSTR lpStr1, LPCSTR lpStr2, int nChar) {
2641   return strncasecmp(lpStr1, lpStr2, nChar);
2642 }
2643
2644 /**************************************************************************
2645  * StrCmpNW [COMCTL32.360]
2646  *
2647  */
2648 INT WINAPI COMCTL32_StrCmpNW( LPCWSTR lpStr1, LPCWSTR lpStr2, int nChar) {
2649   return strncmpW(lpStr1, lpStr2, nChar);
2650 }
2651
2652 /**************************************************************************
2653  * StrCmpNIW [COMCTL32.361]
2654  *
2655  */
2656 INT WINAPI COMCTL32_StrCmpNIW( LPCWSTR lpStr1, LPCWSTR lpStr2, int nChar) {
2657   FIXME("(%s, %s, %i): stub\n", debugstr_w(lpStr1), debugstr_w(lpStr2), nChar);
2658   return 0;
2659 }
2660
2661 /**************************************************************************
2662  * StrRChrA [COMCTL32.351]
2663  *
2664  */
2665 LPSTR WINAPI COMCTL32_StrRChrA( LPCSTR lpStart, LPCSTR lpEnd, WORD wMatch )
2666 {
2667     LPCSTR lpGotIt = NULL;
2668     BOOL dbcs = IsDBCSLeadByte( LOBYTE(wMatch) );
2669
2670     TRACE("(%p, %p, %x)\n", lpStart, lpEnd, wMatch);
2671
2672     if (!lpEnd) lpEnd = lpStart + strlen(lpStart);
2673
2674     for(; lpStart < lpEnd; lpStart = CharNextA(lpStart))
2675     {
2676         if (*lpStart != LOBYTE(wMatch)) continue;
2677         if (dbcs && lpStart[1] != HIBYTE(wMatch)) continue;
2678         lpGotIt = lpStart;
2679     }    
2680     return (LPSTR)lpGotIt;
2681 }
2682
2683
2684 /**************************************************************************
2685  * StrRChrW [COMCTL32.359]
2686  *
2687  */
2688 LPWSTR WINAPI COMCTL32_StrRChrW( LPCWSTR lpStart, LPCWSTR lpEnd, WORD wMatch)
2689 {
2690     LPCWSTR lpGotIt = NULL;
2691
2692     TRACE("(%p, %p, %x)\n", lpStart, lpEnd, wMatch);
2693     if (!lpEnd) lpEnd = lpStart + strlenW(lpStart);
2694
2695     for(; lpStart < lpEnd; lpStart = CharNextW(lpStart))
2696         if (*lpStart == wMatch) lpGotIt = lpStart;
2697
2698     return (LPWSTR)lpGotIt;
2699 }
2700
2701
2702 /**************************************************************************
2703  * StrStrA [COMCTL32.354]
2704  *
2705  */
2706 LPSTR WINAPI COMCTL32_StrStrA( LPCSTR lpFirst, LPCSTR lpSrch) {
2707   return strstr(lpFirst, lpSrch);
2708 }
2709
2710 /**************************************************************************
2711  * StrStrW [COMCTL32.362]
2712  *
2713  */
2714 LPWSTR WINAPI COMCTL32_StrStrW( LPCWSTR lpFirst, LPCWSTR lpSrch) {
2715   return strstrW(lpFirst, lpSrch);
2716 }
2717
2718 /**************************************************************************
2719  * StrSpnW [COMCTL32.364]
2720  *
2721  */
2722 INT WINAPI COMCTL32_StrSpnW( LPWSTR lpStr, LPWSTR lpSet) {
2723   LPWSTR lpLoop = lpStr;
2724
2725   /* validate ptr */
2726   if ((lpStr == 0) || (lpSet == 0)) return 0;
2727
2728 /* while(*lpLoop) { if lpLoop++; } */
2729
2730   for(; (*lpLoop != 0); lpLoop++)
2731     if( strchrW(lpSet, *(WORD*)lpLoop))
2732       return (INT)(lpLoop-lpStr);
2733   
2734   return (INT)(lpLoop-lpStr);
2735 }
2736
2737 /**************************************************************************
2738  * @ [COMCTL32.410]
2739  *
2740  * FIXME: What's this supposed to do?
2741  *        Parameter 1 is an HWND, you're on your own for the rest.
2742  */
2743
2744 BOOL WINAPI COMCTL32_410( HWND hw, DWORD b, DWORD c, DWORD d) {
2745
2746    FIXME("(%x, %lx, %lx, %lx): stub!\n", hw, b, c, d);
2747
2748    return TRUE;
2749 }
2750
2751 /**************************************************************************
2752  * @ [COMCTL32.411]
2753  *
2754  * FIXME: What's this supposed to do?
2755  *        Parameter 1 is an HWND, you're on your own for the rest.
2756  */
2757
2758 BOOL WINAPI COMCTL32_411( HWND hw, DWORD b, DWORD c) {
2759
2760    FIXME("(%x, %lx, %lx): stub!\n", hw, b, c);
2761
2762    return TRUE;
2763 }
2764
2765 /**************************************************************************
2766  * @ [COMCTL32.412]
2767  *
2768  * FIXME: What's this supposed to do?
2769  *        Parameter 1 is an HWND, you're on your own for the rest.
2770  */
2771
2772 BOOL WINAPI COMCTL32_412( HWND hwnd, DWORD b, DWORD c)
2773 {
2774     FIXME("(%x, %lx, %lx): stub!\n", hwnd, b, c);
2775
2776     if (IsWindow (hwnd) == FALSE)
2777         return FALSE;
2778
2779     if (b == 0)
2780         return FALSE;
2781
2782
2783     return TRUE;
2784 }
2785
2786 /**************************************************************************
2787  * @ [COMCTL32.413]
2788  *
2789  * FIXME: What's this supposed to do?
2790  *        Parameter 1 is an HWND, you're on your own for the rest.
2791  */
2792
2793 BOOL WINAPI COMCTL32_413( HWND hw, DWORD b, DWORD c, DWORD d) {
2794
2795    FIXME("(%x, %lx, %lx, %lx): stub!\n", hw, b, c, d);
2796
2797    return TRUE;
2798 }
2799
2800
2801 /**************************************************************************
2802  * @ [COMCTL32.415]
2803  *
2804  * FIXME: What's this supposed to do?
2805  *        Parameter 1 is an HWND, you're on your own for the rest.
2806  */
2807
2808 BOOL WINAPI COMCTL32_415( HWND hwnd, DWORD b, DWORD c, DWORD d, DWORD e)
2809 {
2810
2811    FIXME("(%x, %lx, %lx, %lx, %lx): stub!\n", hwnd, b, c, d, e);
2812
2813    return TRUE;
2814 }
2815
2816 /**************************************************************************
2817  * @ [COMCTL32.419]
2818  *
2819  * FIXME: What's this supposed to do?
2820  */
2821
2822 BOOL WINAPI COMCTL32_419( DWORD a, DWORD b, DWORD c, DWORD d)
2823 {
2824
2825    FIXME("(%lx, %lx, %lx, %lx): stub!\n", a, b, c, d);
2826
2827    return TRUE;
2828 }