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