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