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