- Better implementation for GetTopindex/GetOrigin/SetIconSpacing.
[wine] / dlls / shell32 / shlmenu.c
1 /*
2  * see www.geocities.com/SiliconValley/4942/filemenu.html
3  *
4  * Copyright 1999, 2000 Juergen Schmied
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <string.h>
22
23 #include "winreg.h"
24 #include "shlobj.h"
25 #include "wine/obj_base.h"
26 #include "wine/obj_enumidlist.h"
27 #include "wine/obj_shellfolder.h"
28 #include "undocshell.h"
29 #include "shlwapi.h"
30 #include "heap.h"
31 #include "shell32_main.h"
32 #include "shlguid.h"
33
34 #include "pidl.h"
35 #include "wine/debug.h"
36
37 static BOOL FileMenu_AppendItemA(HMENU hMenu, LPCSTR lpText, UINT uID, int icon,
38                                  HMENU hMenuPopup, int nItemHeight);
39
40 typedef struct
41 {
42         BOOL            bInitialized;
43         BOOL            bFixedItems;
44         /* create */
45         COLORREF        crBorderColor;
46         int             nBorderWidth;
47         HBITMAP         hBorderBmp;
48
49         /* insert using pidl */
50         LPITEMIDLIST    pidl;
51         UINT            uID;
52         UINT            uFlags;
53         UINT            uEnumFlags;
54         LPFNFMCALLBACK lpfnCallback;
55 } FMINFO, *LPFMINFO;
56
57 typedef struct
58 {       int     cchItemText;
59         int     iIconIndex;
60         HMENU   hMenu;
61         char    szItemText[1];
62 } FMITEM, * LPFMITEM;
63
64 static BOOL bAbortInit;
65
66 #define CCH_MAXITEMTEXT 256
67
68 WINE_DEFAULT_DEBUG_CHANNEL(shell);
69
70 LPFMINFO FM_GetMenuInfo(HMENU hmenu)
71 {       MENUINFO        MenuInfo;
72         LPFMINFO        menudata;
73
74         MenuInfo.cbSize = sizeof(MENUINFO);
75         MenuInfo.fMask = MIM_MENUDATA;
76
77         if (! GetMenuInfo(hmenu, &MenuInfo))
78           return NULL;
79
80         menudata = (LPFMINFO)MenuInfo.dwMenuData;
81
82         if ((menudata == 0) || (MenuInfo.cbSize != sizeof(MENUINFO)))
83         {
84           ERR("menudata corrupt: %p %lu\n", menudata, MenuInfo.cbSize);
85           return 0;
86         }
87
88         return menudata;
89
90 }
91 /*************************************************************************
92  * FM_SetMenuParameter                          [internal]
93  *
94  */
95 static LPFMINFO FM_SetMenuParameter(
96         HMENU hmenu,
97         UINT uID,
98         LPCITEMIDLIST pidl,
99         UINT uFlags,
100         UINT uEnumFlags,
101         LPFNFMCALLBACK lpfnCallback)
102 {
103         LPFMINFO        menudata;
104
105         TRACE("\n");
106
107         menudata = FM_GetMenuInfo(hmenu);
108
109         if ( menudata->pidl)
110         { SHFree(menudata->pidl);
111         }
112
113         menudata->uID = uID;
114         menudata->pidl = ILClone(pidl);
115         menudata->uFlags = uFlags;
116         menudata->uEnumFlags = uEnumFlags;
117         menudata->lpfnCallback = lpfnCallback;
118
119         return menudata;
120 }
121
122 /*************************************************************************
123  * FM_InitMenuPopup                             [internal]
124  *
125  */
126 static int FM_InitMenuPopup(HMENU hmenu, LPITEMIDLIST pAlternatePidl)
127 {       IShellFolder    *lpsf, *lpsf2;
128         ULONG           ulItemAttr = SFGAO_FOLDER;
129         UINT            uID, uFlags, uEnumFlags;
130         LPFNFMCALLBACK  lpfnCallback;
131         LPITEMIDLIST    pidl;
132         char            sTemp[MAX_PATH];
133         int             NumberOfItems = 0, iIcon;
134         MENUINFO        MenuInfo;
135         LPFMINFO        menudata;
136
137         TRACE("0x%04x %p\n", hmenu, pAlternatePidl);
138
139         MenuInfo.cbSize = sizeof(MENUINFO);
140         MenuInfo.fMask = MIM_MENUDATA;
141
142         if (! GetMenuInfo(hmenu, &MenuInfo))
143           return FALSE;
144
145         menudata = (LPFMINFO)MenuInfo.dwMenuData;
146
147         if ((menudata == 0) || (MenuInfo.cbSize != sizeof(MENUINFO)))
148         {
149           ERR("menudata corrupt: %p %lu\n", menudata, MenuInfo.cbSize);
150           return 0;
151         }
152
153         if (menudata->bInitialized)
154           return 0;
155
156         pidl = ((pAlternatePidl) ? pAlternatePidl : menudata->pidl);
157         if (!pidl)
158           return 0;
159
160         uID = menudata->uID;
161         uFlags = menudata->uFlags;
162         uEnumFlags = menudata->uEnumFlags;
163         lpfnCallback = menudata->lpfnCallback;
164         menudata->bInitialized = FALSE;
165
166         SetMenuInfo(hmenu, &MenuInfo);
167
168         if (SUCCEEDED (SHGetDesktopFolder(&lpsf)))
169         {
170           if (SUCCEEDED(IShellFolder_BindToObject(lpsf, pidl,0,(REFIID)&IID_IShellFolder,(LPVOID *)&lpsf2)))
171           {
172             IEnumIDList *lpe = NULL;
173
174             if (SUCCEEDED (IShellFolder_EnumObjects(lpsf2, 0, uEnumFlags, &lpe )))
175             {
176
177               LPITEMIDLIST pidlTemp = NULL;
178               ULONG ulFetched;
179
180               while ((!bAbortInit) && (NOERROR == IEnumIDList_Next(lpe,1,&pidlTemp,&ulFetched)))
181               {
182                 if (SUCCEEDED (IShellFolder_GetAttributesOf(lpsf, 1, &pidlTemp, &ulItemAttr)))
183                 {
184                   ILGetDisplayName( pidlTemp, sTemp);
185                   if (! (PidlToSicIndex(lpsf, pidlTemp, FALSE, 0, &iIcon)))
186                     iIcon = FM_BLANK_ICON;
187                   if ( SFGAO_FOLDER & ulItemAttr)
188                   {
189                     LPFMINFO lpFmMi;
190                     MENUINFO MenuInfo;
191                     HMENU hMenuPopup = CreatePopupMenu();
192
193                     lpFmMi = (LPFMINFO) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FMINFO));
194
195                     lpFmMi->pidl = ILCombine(pidl, pidlTemp);
196                     lpFmMi->uEnumFlags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
197
198                     MenuInfo.cbSize = sizeof(MENUINFO);
199                     MenuInfo.fMask = MIM_MENUDATA;
200                     MenuInfo.dwMenuData = (DWORD) lpFmMi;
201                     SetMenuInfo (hMenuPopup, &MenuInfo);
202
203                     FileMenu_AppendItemA (hmenu, sTemp, uID, iIcon, hMenuPopup, FM_DEFAULT_HEIGHT);
204                   }
205                   else
206                   {
207                     ((LPSTR)PathFindExtensionA(sTemp))[0] = 0x00;
208                     FileMenu_AppendItemA (hmenu, sTemp, uID, iIcon, 0, FM_DEFAULT_HEIGHT);
209                   }
210                 }
211
212                 if (lpfnCallback)
213                 {
214                   TRACE("enter callback\n");
215                   lpfnCallback ( pidl, pidlTemp);
216                   TRACE("leave callback\n");
217                 }
218
219                 NumberOfItems++;
220               }
221               IEnumIDList_Release (lpe);
222             }
223             IShellFolder_Release(lpsf2);
224           }
225           IShellFolder_Release(lpsf);
226         }
227
228         if ( GetMenuItemCount (hmenu) == 0 )
229         { FileMenu_AppendItemA (hmenu, "(empty)", uID, FM_BLANK_ICON, 0, FM_DEFAULT_HEIGHT);
230           NumberOfItems++;
231         }
232
233         menudata->bInitialized = TRUE;
234         SetMenuInfo(hmenu, &MenuInfo);
235
236         return NumberOfItems;
237 }
238 /*************************************************************************
239  * FileMenu_Create                              [SHELL32.114]
240  *
241  * NOTES
242  *  for non-root menus values are
243  *  (ffffffff,00000000,00000000,00000000,00000000)
244  */
245 HMENU WINAPI FileMenu_Create (
246         COLORREF crBorderColor,
247         int nBorderWidth,
248         HBITMAP hBorderBmp,
249         int nSelHeight,
250         UINT uFlags)
251 {
252         MENUINFO        MenuInfo;
253         LPFMINFO        menudata;
254
255         HMENU hMenu = CreatePopupMenu();
256
257         TRACE("0x%08lx 0x%08x 0x%08x 0x%08x 0x%08x  hMenu=0x%08x\n",
258         crBorderColor, nBorderWidth, hBorderBmp, nSelHeight, uFlags, hMenu);
259
260         menudata = (LPFMINFO)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FMINFO));
261         menudata->crBorderColor = crBorderColor;
262         menudata->nBorderWidth = nBorderWidth;
263         menudata->hBorderBmp = hBorderBmp;
264
265         MenuInfo.cbSize = sizeof(MENUINFO);
266         MenuInfo.fMask = MIM_MENUDATA;
267         MenuInfo.dwMenuData = (DWORD) menudata;
268         SetMenuInfo (hMenu, &MenuInfo);
269
270         return hMenu;
271 }
272
273 /*************************************************************************
274  * FileMenu_Destroy                             [SHELL32.118]
275  *
276  * NOTES
277  *  exported by name
278  */
279 void WINAPI FileMenu_Destroy (HMENU hmenu)
280 {
281         LPFMINFO        menudata;
282
283         TRACE("0x%08x\n", hmenu);
284
285         FileMenu_DeleteAllItems (hmenu);
286
287         menudata = FM_GetMenuInfo(hmenu);
288
289         if ( menudata->pidl)
290         { SHFree( menudata->pidl);
291         }
292         HeapFree(GetProcessHeap(), 0, menudata);
293
294         DestroyMenu (hmenu);
295 }
296
297 /*************************************************************************
298  * FileMenu_AppendItem                  [SHELL32.115]
299  *
300  */
301 static BOOL FileMenu_AppendItemA(
302         HMENU hMenu,
303         LPCSTR lpText,
304         UINT uID,
305         int icon,
306         HMENU hMenuPopup,
307         int nItemHeight)
308 {
309         LPSTR lpszText = (LPSTR)lpText;
310         MENUITEMINFOA   mii;
311         LPFMITEM        myItem;
312         LPFMINFO        menudata;
313         MENUINFO        MenuInfo;
314
315
316         TRACE("0x%08x %s 0x%08x 0x%08x 0x%08x 0x%08x\n",
317         hMenu, (lpszText!=FM_SEPARATOR) ? lpText: NULL,
318         uID, icon, hMenuPopup, nItemHeight);
319
320         ZeroMemory (&mii, sizeof(MENUITEMINFOA));
321
322         mii.cbSize = sizeof(MENUITEMINFOA);
323
324         if (lpText != FM_SEPARATOR)
325         { int len = strlen (lpText);
326           myItem = (LPFMITEM) SHAlloc( sizeof(FMITEM) + len);
327           strcpy (myItem->szItemText, lpText);
328           myItem->cchItemText = len;
329           myItem->iIconIndex = icon;
330           myItem->hMenu = hMenu;
331           mii.fMask = MIIM_DATA;
332           mii.dwItemData = (DWORD) myItem;
333         }
334
335         if ( hMenuPopup )
336         { /* sub menu */
337           mii.fMask |= MIIM_TYPE | MIIM_SUBMENU;
338           mii.fType = MFT_OWNERDRAW;
339           mii.hSubMenu = hMenuPopup;
340         }
341         else if (lpText == FM_SEPARATOR )
342         { mii.fMask |= MIIM_ID | MIIM_TYPE;
343           mii.fType = MFT_SEPARATOR;
344         }
345         else
346         { /* normal item */
347           mii.fMask |= MIIM_ID | MIIM_TYPE | MIIM_STATE;
348           mii.fState = MFS_ENABLED | MFS_DEFAULT;
349           mii.fType = MFT_OWNERDRAW;
350         }
351         mii.wID = uID;
352
353         InsertMenuItemA (hMenu, (UINT)-1, TRUE, &mii);
354
355         /* set bFixedItems to true */
356         MenuInfo.cbSize = sizeof(MENUINFO);
357         MenuInfo.fMask = MIM_MENUDATA;
358
359         if (! GetMenuInfo(hMenu, &MenuInfo))
360           return FALSE;
361
362         menudata = (LPFMINFO)MenuInfo.dwMenuData;
363         if ((menudata == 0) || (MenuInfo.cbSize != sizeof(MENUINFO)))
364         {
365           ERR("menudata corrupt: %p %lu\n", menudata, MenuInfo.cbSize);
366           return 0;
367         }
368
369         menudata->bFixedItems = TRUE;
370         SetMenuInfo(hMenu, &MenuInfo);
371
372         return TRUE;
373
374 }
375
376 /**********************************************************************/
377
378 BOOL WINAPI FileMenu_AppendItemAW(
379         HMENU hMenu,
380         LPCVOID lpText,
381         UINT uID,
382         int icon,
383         HMENU hMenuPopup,
384         int nItemHeight)
385 {
386         BOOL ret;
387         LPSTR lpszText=NULL;
388
389         if (SHELL_OsIsUnicode() && (lpText!=FM_SEPARATOR))
390           lpszText = HEAP_strdupWtoA ( GetProcessHeap(),0, lpText);
391
392         ret = FileMenu_AppendItemA(hMenu, (lpszText) ? lpszText : lpText, uID, icon, hMenuPopup, nItemHeight);
393
394         if (lpszText)
395           HeapFree( GetProcessHeap(), 0, lpszText );
396
397         return ret;
398 }
399 /*************************************************************************
400  * FileMenu_InsertUsingPidl                     [SHELL32.110]
401  *
402  * NOTES
403  *      uEnumFlags      any SHCONTF flag
404  */
405 int WINAPI FileMenu_InsertUsingPidl (
406         HMENU hmenu,
407         UINT uID,
408         LPCITEMIDLIST pidl,
409         UINT uFlags,
410         UINT uEnumFlags,
411         LPFNFMCALLBACK lpfnCallback)
412 {
413         TRACE("0x%08x 0x%08x %p 0x%08x 0x%08x %p\n",
414         hmenu, uID, pidl, uFlags, uEnumFlags, lpfnCallback);
415
416         pdump (pidl);
417
418         bAbortInit = FALSE;
419
420         FM_SetMenuParameter(hmenu, uID, pidl, uFlags, uEnumFlags, lpfnCallback);
421
422         return FM_InitMenuPopup(hmenu, NULL);
423 }
424
425 /*************************************************************************
426  * FileMenu_ReplaceUsingPidl                    [SHELL32.113]
427  *
428  * FIXME: the static items are deleted but wont be refreshed
429  */
430 int WINAPI FileMenu_ReplaceUsingPidl(
431         HMENU   hmenu,
432         UINT    uID,
433         LPCITEMIDLIST   pidl,
434         UINT    uEnumFlags,
435         LPFNFMCALLBACK lpfnCallback)
436 {
437         TRACE("0x%08x 0x%08x %p 0x%08x %p\n",
438         hmenu, uID, pidl, uEnumFlags, lpfnCallback);
439
440         FileMenu_DeleteAllItems (hmenu);
441
442         FM_SetMenuParameter(hmenu, uID, pidl, 0, uEnumFlags, lpfnCallback);
443
444         return FM_InitMenuPopup(hmenu, NULL);
445 }
446
447 /*************************************************************************
448  * FileMenu_Invalidate                  [SHELL32.111]
449  */
450 void WINAPI FileMenu_Invalidate (HMENU hMenu)
451 {
452         FIXME("0x%08x\n",hMenu);
453 }
454
455 /*************************************************************************
456  * FileMenu_FindSubMenuByPidl                   [SHELL32.106]
457  */
458 HMENU WINAPI FileMenu_FindSubMenuByPidl(
459         HMENU   hMenu,
460         LPCITEMIDLIST   pidl)
461 {
462         FIXME("0x%08x %p\n",hMenu, pidl);
463         return 0;
464 }
465
466 /*************************************************************************
467  * FileMenu_AppendFilesForPidl                  [SHELL32.124]
468  */
469 int WINAPI FileMenu_AppendFilesForPidl(
470         HMENU   hmenu,
471         LPCITEMIDLIST   pidl,
472         BOOL    bAddSeperator)
473 {
474         LPFMINFO        menudata;
475
476         menudata = FM_GetMenuInfo(hmenu);
477
478         menudata->bInitialized = FALSE;
479
480         FM_InitMenuPopup(hmenu, pidl);
481
482         if (bAddSeperator)
483           FileMenu_AppendItemA (hmenu, FM_SEPARATOR, 0, 0, 0, FM_DEFAULT_HEIGHT);
484
485         TRACE("0x%08x %p 0x%08x\n",hmenu, pidl,bAddSeperator);
486
487         return 0;
488 }
489 /*************************************************************************
490  * FileMenu_AddFilesForPidl                     [SHELL32.125]
491  *
492  * NOTES
493  *      uEnumFlags      any SHCONTF flag
494  */
495 int WINAPI FileMenu_AddFilesForPidl (
496         HMENU   hmenu,
497         UINT    uReserved,
498         UINT    uID,
499         LPCITEMIDLIST   pidl,
500         UINT    uFlags,
501         UINT    uEnumFlags,
502         LPFNFMCALLBACK  lpfnCallback)
503 {
504         TRACE("0x%08x 0x%08x 0x%08x %p 0x%08x 0x%08x %p\n",
505         hmenu, uReserved, uID, pidl, uFlags, uEnumFlags, lpfnCallback);
506
507         return FileMenu_InsertUsingPidl ( hmenu, uID, pidl, uFlags, uEnumFlags, lpfnCallback);
508
509 }
510
511
512 /*************************************************************************
513  * FileMenu_TrackPopupMenuEx                    [SHELL32.116]
514  */
515 BOOL WINAPI FileMenu_TrackPopupMenuEx (
516         HMENU hMenu,
517         UINT uFlags,
518         int x,
519         int y,
520         HWND hWnd,
521         LPTPMPARAMS lptpm)
522 {
523         TRACE("0x%08x 0x%08x 0x%x 0x%x 0x%08x %p\n",
524         hMenu, uFlags, x, y, hWnd, lptpm);
525         return TrackPopupMenuEx(hMenu, uFlags, x, y, hWnd, lptpm);
526 }
527
528 /*************************************************************************
529  * FileMenu_GetLastSelectedItemPidls            [SHELL32.107]
530  */
531 BOOL WINAPI FileMenu_GetLastSelectedItemPidls(
532         UINT    uReserved,
533         LPCITEMIDLIST   *ppidlFolder,
534         LPCITEMIDLIST   *ppidlItem)
535 {
536         FIXME("0x%08x %p %p\n",uReserved, ppidlFolder, ppidlItem);
537         return 0;
538 }
539
540 #define FM_ICON_SIZE    16
541 #define FM_Y_SPACE      4
542 #define FM_SPACE1       4
543 #define FM_SPACE2       2
544 #define FM_LEFTBORDER   2
545 #define FM_RIGHTBORDER  8
546 /*************************************************************************
547  * FileMenu_MeasureItem                         [SHELL32.112]
548  */
549 LRESULT WINAPI FileMenu_MeasureItem(
550         HWND    hWnd,
551         LPMEASUREITEMSTRUCT     lpmis)
552 {
553         LPFMITEM pMyItem = (LPFMITEM)(lpmis->itemData);
554         HDC hdc = GetDC(hWnd);
555         SIZE size;
556         LPFMINFO menuinfo;
557
558         TRACE("0x%08x %p %s\n", hWnd, lpmis, pMyItem->szItemText);
559
560         GetTextExtentPoint32A(hdc, pMyItem->szItemText, pMyItem->cchItemText, &size);
561
562         lpmis->itemWidth = size.cx + FM_LEFTBORDER + FM_ICON_SIZE + FM_SPACE1 + FM_SPACE2 + FM_RIGHTBORDER;
563         lpmis->itemHeight = (size.cy > (FM_ICON_SIZE + FM_Y_SPACE)) ? size.cy : (FM_ICON_SIZE + FM_Y_SPACE);
564
565         /* add the menubitmap */
566         menuinfo = FM_GetMenuInfo(pMyItem->hMenu);
567         if (menuinfo->nBorderWidth)
568           lpmis->itemWidth += menuinfo->nBorderWidth;
569
570         TRACE("-- 0x%04x 0x%04x\n", lpmis->itemWidth, lpmis->itemHeight);
571         ReleaseDC (hWnd, hdc);
572         return 0;
573 }
574 /*************************************************************************
575  * FileMenu_DrawItem                            [SHELL32.105]
576  */
577 LRESULT WINAPI FileMenu_DrawItem(
578         HWND                    hWnd,
579         LPDRAWITEMSTRUCT        lpdis)
580 {
581         LPFMITEM pMyItem = (LPFMITEM)(lpdis->itemData);
582         COLORREF clrPrevText, clrPrevBkgnd;
583         int xi,yi,xt,yt;
584         HIMAGELIST hImageList;
585         RECT TextRect, BorderRect;
586         LPFMINFO menuinfo;
587
588         TRACE("0x%08x %p %s\n", hWnd, lpdis, pMyItem->szItemText);
589
590         if (lpdis->itemState & ODS_SELECTED)
591         {
592           clrPrevText = SetTextColor(lpdis->hDC, GetSysColor (COLOR_HIGHLIGHTTEXT));
593           clrPrevBkgnd = SetBkColor(lpdis->hDC, GetSysColor (COLOR_HIGHLIGHT));
594         }
595         else
596         {
597           clrPrevText = SetTextColor(lpdis->hDC, GetSysColor (COLOR_MENUTEXT));
598           clrPrevBkgnd = SetBkColor(lpdis->hDC, GetSysColor (COLOR_MENU));
599         }
600
601         CopyRect(&TextRect, &(lpdis->rcItem));
602
603         /* add the menubitmap */
604         menuinfo = FM_GetMenuInfo(pMyItem->hMenu);
605         if (menuinfo->nBorderWidth)
606           TextRect.left += menuinfo->nBorderWidth;
607
608         BorderRect.right = menuinfo->nBorderWidth;
609 /*      FillRect(lpdis->hDC, &BorderRect, CreateSolidBrush( menuinfo->crBorderColor));
610 */
611         TextRect.left += FM_LEFTBORDER;
612         xi = TextRect.left + FM_SPACE1;
613         yi = TextRect.top + FM_Y_SPACE/2;
614         TextRect.bottom -= FM_Y_SPACE/2;
615
616         xt = xi + FM_ICON_SIZE + FM_SPACE2;
617         yt = yi;
618
619         ExtTextOutA (lpdis->hDC, xt , yt, ETO_OPAQUE, &TextRect, pMyItem->szItemText, pMyItem->cchItemText, NULL);
620
621         Shell_GetImageList(0, &hImageList);
622         ImageList_Draw(hImageList, pMyItem->iIconIndex, lpdis->hDC, xi, yi, ILD_NORMAL);
623
624         TRACE("-- 0x%04x 0x%04x 0x%04x 0x%04x\n", TextRect.left, TextRect.top, TextRect.right, TextRect.bottom);
625
626         SetTextColor(lpdis->hDC, clrPrevText);
627         SetBkColor(lpdis->hDC, clrPrevBkgnd);
628
629         return TRUE;
630 }
631
632 /*************************************************************************
633  * FileMenu_InitMenuPopup                       [SHELL32.109]
634  *
635  * NOTES
636  *  The filemenu is a ownerdrawn menu. Call this function responding to
637  *  WM_INITPOPUPMENU
638  *
639  */
640 BOOL WINAPI FileMenu_InitMenuPopup (HMENU hmenu)
641 {
642         FM_InitMenuPopup(hmenu, NULL);
643         return TRUE;
644 }
645
646 /*************************************************************************
647  * FileMenu_HandleMenuChar                      [SHELL32.108]
648  */
649 LRESULT WINAPI FileMenu_HandleMenuChar(
650         HMENU   hMenu,
651         WPARAM  wParam)
652 {
653         FIXME("0x%08x 0x%08x\n",hMenu,wParam);
654         return 0;
655 }
656
657 /*************************************************************************
658  * FileMenu_DeleteAllItems                      [SHELL32.104]
659  *
660  * NOTES
661  *  exported by name
662  */
663 BOOL WINAPI FileMenu_DeleteAllItems (HMENU hmenu)
664 {
665         MENUITEMINFOA   mii;
666         LPFMINFO        menudata;
667
668         int i;
669
670         TRACE("0x%08x\n", hmenu);
671
672         ZeroMemory ( &mii, sizeof(MENUITEMINFOA));
673         mii.cbSize = sizeof(MENUITEMINFOA);
674         mii.fMask = MIIM_SUBMENU|MIIM_DATA;
675
676         for (i = 0; i < GetMenuItemCount( hmenu ); i++)
677         { GetMenuItemInfoA(hmenu, i, TRUE, &mii );
678
679           if (mii.dwItemData)
680             SHFree((LPFMINFO)mii.dwItemData);
681
682           if (mii.hSubMenu)
683             FileMenu_Destroy(mii.hSubMenu);
684         }
685
686         while (DeleteMenu (hmenu, 0, MF_BYPOSITION)){};
687
688         menudata = FM_GetMenuInfo(hmenu);
689
690         menudata->bInitialized = FALSE;
691
692         return TRUE;
693 }
694
695 /*************************************************************************
696  * FileMenu_DeleteItemByCmd                     [SHELL32.117]
697  *
698  */
699 BOOL WINAPI FileMenu_DeleteItemByCmd (HMENU hMenu, UINT uID)
700 {
701         MENUITEMINFOA mii;
702
703         TRACE("0x%08x 0x%08x\n", hMenu, uID);
704
705         ZeroMemory ( &mii, sizeof(MENUITEMINFOA));
706         mii.cbSize = sizeof(MENUITEMINFOA);
707         mii.fMask = MIIM_SUBMENU;
708
709         GetMenuItemInfoA(hMenu, uID, FALSE, &mii );
710         if ( mii.hSubMenu )
711         {
712           /* FIXME: Do what? */
713         }
714
715         DeleteMenu(hMenu, MF_BYCOMMAND, uID);
716         return TRUE;
717 }
718
719 /*************************************************************************
720  * FileMenu_DeleteItemByIndex                   [SHELL32.140]
721  */
722 BOOL WINAPI FileMenu_DeleteItemByIndex ( HMENU hMenu, UINT uPos)
723 {
724         MENUITEMINFOA mii;
725
726         TRACE("0x%08x 0x%08x\n", hMenu, uPos);
727
728         ZeroMemory ( &mii, sizeof(MENUITEMINFOA));
729         mii.cbSize = sizeof(MENUITEMINFOA);
730         mii.fMask = MIIM_SUBMENU;
731
732         GetMenuItemInfoA(hMenu, uPos, TRUE, &mii );
733         if ( mii.hSubMenu )
734         {
735           /* FIXME: Do what? */
736         }
737
738         DeleteMenu(hMenu, MF_BYPOSITION, uPos);
739         return TRUE;
740 }
741
742 /*************************************************************************
743  * FileMenu_DeleteItemByFirstID                 [SHELL32.141]
744  */
745 BOOL WINAPI FileMenu_DeleteItemByFirstID(
746         HMENU   hMenu,
747         UINT    uID)
748 {
749         TRACE("0x%08x 0x%08x\n", hMenu, uID);
750         return 0;
751 }
752
753 /*************************************************************************
754  * FileMenu_DeleteSeparator                     [SHELL32.142]
755  */
756 BOOL WINAPI FileMenu_DeleteSeparator(HMENU hMenu)
757 {
758         TRACE("0x%08x\n", hMenu);
759         return 0;
760 }
761
762 /*************************************************************************
763  * FileMenu_EnableItemByCmd                     [SHELL32.143]
764  */
765 BOOL WINAPI FileMenu_EnableItemByCmd(
766         HMENU   hMenu,
767         UINT    uID,
768         BOOL    bEnable)
769 {
770         TRACE("0x%08x 0x%08x 0x%08x\n", hMenu, uID,bEnable);
771         return 0;
772 }
773
774 /*************************************************************************
775  * FileMenu_GetItemExtent                       [SHELL32.144]
776  *
777  * NOTES
778  *  if the menu is too big, entries are getting cut away!!
779  */
780 DWORD WINAPI FileMenu_GetItemExtent (HMENU hMenu, UINT uPos)
781 {       RECT rect;
782
783         FIXME("0x%08x 0x%08x\n", hMenu, uPos);
784
785         if (GetMenuItemRect(0, hMenu, uPos, &rect))
786         { FIXME("0x%04x 0x%04x 0x%04x 0x%04x\n",
787           rect.right, rect.left, rect.top, rect.bottom);
788           return ((rect.right-rect.left)<<16) + (rect.top-rect.bottom);
789         }
790         return 0x00100010; /*FIXME*/
791 }
792
793 /*************************************************************************
794  * FileMenu_AbortInitMenu                       [SHELL32.120]
795  *
796  */
797 void WINAPI FileMenu_AbortInitMenu (void)
798 {       TRACE("\n");
799         bAbortInit = TRUE;
800 }
801
802 /*************************************************************************
803  * SHFind_InitMenuPopup                         [SHELL32.149]
804  *
805  *
806  * PARAMETERS
807  *  hMenu               [in] handle of menu previously created
808  *  hWndParent  [in] parent window
809  *  w                   [in] no pointer (0x209 over here) perhaps menu IDs ???
810  *  x                   [in] no pointer (0x226 over here)
811  *
812  * RETURNS
813  *  LPXXXXX                      pointer to struct containing a func addr at offset 8
814  *                                       or NULL at failure.
815  */
816 LPVOID WINAPI SHFind_InitMenuPopup (HMENU hMenu, HWND hWndParent, DWORD w, DWORD x)
817 {       FIXME("hmenu=0x%08x hwnd=0x%08x 0x%08lx 0x%08lx stub\n",
818                 hMenu,hWndParent,w,x);
819         return NULL; /* this is supposed to be a pointer */
820 }
821
822 /*************************************************************************
823  * Shell_MergeMenus                             [SHELL32.67]
824  *
825  */
826 BOOL _SHIsMenuSeparator(HMENU hm, int i)
827 {
828         MENUITEMINFOA mii;
829
830         mii.cbSize = sizeof(MENUITEMINFOA);
831         mii.fMask = MIIM_TYPE;
832         mii.cch = 0;    /* WARNING: We MUST initialize it to 0*/
833         if (!GetMenuItemInfoA(hm, i, TRUE, &mii))
834         {
835           return(FALSE);
836         }
837
838         if (mii.fType & MFT_SEPARATOR)
839         {
840           return(TRUE);
841         }
842
843         return(FALSE);
844 }
845
846 /**********************************************************************/
847
848 HRESULT WINAPI Shell_MergeMenus (HMENU hmDst, HMENU hmSrc, UINT uInsert, UINT uIDAdjust, UINT uIDAdjustMax, ULONG uFlags)
849 {       int             nItem;
850         HMENU           hmSubMenu;
851         BOOL            bAlreadySeparated;
852         MENUITEMINFOA   miiSrc;
853         char            szName[256];
854         UINT            uTemp, uIDMax = uIDAdjust;
855
856         TRACE("hmenu1=0x%04x hmenu2=0x%04x 0x%04x 0x%04x 0x%04x  0x%04lx\n",
857                  hmDst, hmSrc, uInsert, uIDAdjust, uIDAdjustMax, uFlags);
858
859         if (!hmDst || !hmSrc)
860         { return uIDMax;
861         }
862
863         nItem = GetMenuItemCount(hmDst);
864
865         if (uInsert >= (UINT)nItem)     /* insert position inside menu? */
866         {
867           uInsert = (UINT)nItem;        /* append on the end */
868           bAlreadySeparated = TRUE;
869         }
870         else
871         {
872           bAlreadySeparated = _SHIsMenuSeparator(hmDst, uInsert);
873         }
874
875         if ((uFlags & MM_ADDSEPARATOR) && !bAlreadySeparated)
876         {
877           /* Add a separator between the menus */
878           InsertMenuA(hmDst, uInsert, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
879           bAlreadySeparated = TRUE;
880         }
881
882
883         /* Go through the menu items and clone them*/
884         for (nItem = GetMenuItemCount(hmSrc) - 1; nItem >= 0; nItem--)
885         {
886           miiSrc.cbSize = sizeof(MENUITEMINFOA);
887           miiSrc.fMask =  MIIM_STATE | MIIM_ID | MIIM_SUBMENU | MIIM_CHECKMARKS | MIIM_TYPE | MIIM_DATA;
888
889           /* We need to reset this every time through the loop in case menus DON'T have IDs*/
890           miiSrc.fType = MFT_STRING;
891           miiSrc.dwTypeData = szName;
892           miiSrc.dwItemData = 0;
893           miiSrc.cch = sizeof(szName);
894
895           if (!GetMenuItemInfoA(hmSrc, nItem, TRUE, &miiSrc))
896           {
897             continue;
898           }
899
900 /*        TRACE("found menu=0x%04x %s id=0x%04x mask=0x%08x smenu=0x%04x\n", hmSrc, debugstr_a(miiSrc.dwTypeData), miiSrc.wID, miiSrc.fMask,  miiSrc.hSubMenu);
901 */
902           if (miiSrc.fType & MFT_SEPARATOR)
903           {
904             /* This is a separator; don't put two of them in a row */
905             if (bAlreadySeparated)
906               continue;
907
908             bAlreadySeparated = TRUE;
909           }
910           else if (miiSrc.hSubMenu)
911           {
912             if (uFlags & MM_SUBMENUSHAVEIDS)
913             {
914               miiSrc.wID += uIDAdjust;                  /* add uIDAdjust to the ID */
915
916               if (miiSrc.wID > uIDAdjustMax)            /* skip ID's higher uIDAdjustMax */
917                 continue;
918
919               if (uIDMax <= miiSrc.wID)                 /* remember the highest ID */
920                 uIDMax = miiSrc.wID + 1;
921             }
922             else
923             {
924               miiSrc.fMask &= ~MIIM_ID;                 /* Don't set IDs for submenus that didn't have them already */
925             }
926             hmSubMenu = miiSrc.hSubMenu;
927
928             miiSrc.hSubMenu = CreatePopupMenu();
929
930             if (!miiSrc.hSubMenu) return(uIDMax);
931
932             uTemp = Shell_MergeMenus(miiSrc.hSubMenu, hmSubMenu, 0, uIDAdjust, uIDAdjustMax, uFlags & MM_SUBMENUSHAVEIDS);
933
934             if (uIDMax <= uTemp)
935               uIDMax = uTemp;
936
937             bAlreadySeparated = FALSE;
938           }
939           else                                          /* normal menu item */
940           {
941             miiSrc.wID += uIDAdjust;                    /* add uIDAdjust to the ID */
942
943             if (miiSrc.wID > uIDAdjustMax)              /* skip ID's higher uIDAdjustMax */
944               continue;
945
946             if (uIDMax <= miiSrc.wID)                   /* remember the highest ID */
947               uIDMax = miiSrc.wID + 1;
948
949             bAlreadySeparated = FALSE;
950           }
951
952 /*        TRACE("inserting menu=0x%04x %s id=0x%04x mask=0x%08x smenu=0x%04x\n", hmDst, debugstr_a(miiSrc.dwTypeData), miiSrc.wID, miiSrc.fMask, miiSrc.hSubMenu);
953 */
954           if (!InsertMenuItemA(hmDst, uInsert, TRUE, &miiSrc))
955           {
956             return(uIDMax);
957           }
958         }
959
960         /* Ensure the correct number of separators at the beginning of the
961         inserted menu items*/
962         if (uInsert == 0)
963         {
964           if (bAlreadySeparated)
965           {
966             DeleteMenu(hmDst, uInsert, MF_BYPOSITION);
967           }
968         }
969         else
970         {
971           if (_SHIsMenuSeparator(hmDst, uInsert-1))
972           {
973             if (bAlreadySeparated)
974             {
975               DeleteMenu(hmDst, uInsert, MF_BYPOSITION);
976             }
977           }
978           else
979           {
980             if ((uFlags & MM_ADDSEPARATOR) && !bAlreadySeparated)
981             {
982               /* Add a separator between the menus*/
983               InsertMenuA(hmDst, uInsert, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
984             }
985           }
986         }
987         return(uIDMax);
988 }
989