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