update plists MACOSX_DEPLOYMENT_TARGET = 10.7.0
[wdl/wdl-ol.git] / WDL / swell / swell-menu.mm
blob5ff7120384a96b3a113a3bf8d57d0a1b48fd5bf1
1 /* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX)
2    Copyright (C) 2006 and later, Cockos, Inc.
4     This software is provided 'as-is', without any express or implied
5     warranty.  In no event will the authors be held liable for any damages
6     arising from the use of this software.
8     Permission is granted to anyone to use this software for any purpose,
9     including commercial applications, and to alter it and redistribute it
10     freely, subject to the following restrictions:
12     1. The origin of this software must not be misrepresented; you must not
13        claim that you wrote the original software. If you use this software
14        in a product, an acknowledgment in the product documentation would be
15        appreciated but is not required.
16     2. Altered source versions must be plainly marked as such, and must not be
17        misrepresented as being the original software.
18     3. This notice may not be removed or altered from any source distribution.
19   
21     This file provides basic windows menu API to interface an NSMenu
23   */
25 #ifndef SWELL_PROVIDED_BY_APP
27 #import <Cocoa/Cocoa.h>
29 #include "swell.h"
30 #include "swell-menugen.h"
32 #include "swell-internal.h"
35 static void __filtnametobuf(char *out, const char *in, int outsz)
37   while (*in && outsz>1)
38   {
39     if (*in == '\t') break;
40     if (*in == '&')
41     {
42       in++;
43     }
44     *out++=*in++;
45     outsz--;
46   }
47   *out=0;
52 bool SetMenuItemText(HMENU hMenu, int idx, int flag, const char *text)
54   NSMenu *menu=(NSMenu *)hMenu;
55   
56   NSMenuItem *item;
57   if (flag & MF_BYPOSITION) item=[menu itemAtIndex:idx];
58   else item =[menu itemWithTag:idx];
59   if (!item) 
60   {
61     if (!(flag & MF_BYPOSITION))
62     {
63       const int n = (int) [menu numberOfItems];
64       for (int x = 0; x < n; x ++)
65       {
66         item=[menu itemAtIndex:x];
67         if (item && [item hasSubmenu])
68         {
69           NSMenu *m=[item submenu];
70           if (m && SetMenuItemText((HMENU)m,idx,flag,text)) return true;
71         }
72       }
73     }
74     return false;
75   }
76   char buf[1024];
77   __filtnametobuf(buf,text?text:"",sizeof(buf));
78   NSString *label=(NSString *)SWELL_CStringToCFString(buf);
79   
80   [item setTitle:label];
81   if ([item hasSubmenu] && [item submenu]) [[item submenu] setTitle:label];
83   [label release];
84   return true;
87 bool EnableMenuItem(HMENU hMenu, int idx, int en)
89   NSMenu *menu=(NSMenu *)hMenu;
90   
91   NSMenuItem *item;
92   if (en & MF_BYPOSITION) item=[menu itemAtIndex:idx];
93   else item =[menu itemWithTag:idx];
94   if (!item) 
95   {
96     if (!(en & MF_BYPOSITION))
97     {
98       const int n=(int)[menu numberOfItems];
99       for (int x = 0; x < n; x ++)
100       {
101         item=[menu itemAtIndex:x];
102         if (item && [item hasSubmenu])
103         {
104           NSMenu *m=[item submenu];
105           if (m && EnableMenuItem((HMENU)m,idx,en)) return true;
106         }
107       }
108     }
109     return false;
110   }
111   [item setEnabled:((en&MF_GRAYED)?NO:YES)];
112   return true;
115 bool CheckMenuItem(HMENU hMenu, int idx, int chk)
117   NSMenu *menu=(NSMenu *)hMenu;
118   if (!menu) return false;
119   
120   NSMenuItem *item;
121   if (chk & MF_BYPOSITION) item=[menu itemAtIndex:idx];
122   else item =[menu itemWithTag:idx];
123   if (!item) 
124   {
125     if (!(chk & MF_BYPOSITION))
126     {
127       const int n=(int)[menu numberOfItems];
128       for (int x = 0; x < n; x ++)
129       {
130         item=[menu itemAtIndex:x];
131         if (item && [item hasSubmenu])
132         {
133           NSMenu *m=[item submenu];
134           if (m && CheckMenuItem((HMENU)m,idx,chk)) return true;
135         }
136       }
137     }
138     return false;  
139   }
140   [item setState:((chk&MF_CHECKED)?NSOnState:NSOffState)];
141   
142   return true;
144 HMENU SWELL_GetCurrentMenu()
146   return (HMENU)[NSApp mainMenu];
149 extern int g_swell_terminating;
151 void SWELL_SetCurrentMenu(HMENU hmenu)
153   if (hmenu && [(id)hmenu isKindOfClass:[NSMenu class]])
154   {
155     if (!g_swell_terminating) [NSApp setMainMenu:(NSMenu *)hmenu];
156   }
159 HMENU GetSubMenu(HMENU hMenu, int pos)
161   NSMenu *menu=(NSMenu *)hMenu;
162   
163   NSMenuItem *item=menu && pos >=0 && pos < [menu numberOfItems] ? [menu itemAtIndex:pos] : 0; 
164   if (item && [item hasSubmenu]) return (HMENU)[item submenu];
165   return 0;
168 int GetMenuItemCount(HMENU hMenu)
170   NSMenu *menu=(NSMenu *)hMenu;
171   return (int)[menu numberOfItems];
174 int GetMenuItemID(HMENU hMenu, int pos)
176   NSMenu *menu=(NSMenu *)hMenu;
177   if (pos < 0 || pos >= (int)[menu numberOfItems]) return 0;
178   
179   NSMenuItem *item=[menu itemAtIndex:pos]; 
180   if (item) 
181   {
182     if ([item hasSubmenu]) return -1;
183     return (int)[item tag];
184   }
185   return 0;
188 bool SetMenuItemModifier(HMENU hMenu, int idx, int flag, int code, unsigned int mask)
191 #if 0 // enable this once we make SWELL_KeyToASCII decent
192   int n2=0;
193   int n1 = SWELL_KeyToASCII(code,flag,&n2);
194   if (n1)
195   {
196     code=n1;
197     flag=n2;
198   }
199 #endif
200   
201   NSMenu *menu=(NSMenu *)hMenu;
202   
203   NSMenuItem *item;
204   if (flag & MF_BYPOSITION) item=[menu itemAtIndex:idx];
205   else item =[menu itemWithTag:idx];
206   if (!item) 
207   {
208     if (!(flag & MF_BYPOSITION))
209     {
210       const int n = (int)[menu numberOfItems];
211       for (int x = 0; x < n; x ++)
212       {
213         item=[menu itemAtIndex:x];
214         if (item && [item hasSubmenu])
215         {
216           NSMenu *m=[item submenu];
217           if (m && SetMenuItemModifier((HMENU)m,idx,flag,code,mask)) return true;
218         }
219       }
220     }
221     return false;
222   }
223   
224   bool suppressShift = false;
225   unichar arrowKey = 0;
226   int codelow = code&127;
227   if ((code>='A' && code <='Z') ||
228       (code>='0' && code <= '9') ||   
229       ( !(mask&FVIRTKEY) && 
230        ( 
231          codelow == '\'' ||
232          codelow == '/' ||
233          codelow == '\\' ||
234          codelow == '|' ||
235          codelow == '"' || 
236          codelow == ',' ||
237          codelow == '.' || 
238          codelow == '!' ||
239          codelow == '?' ||
240          codelow == '[' || 
241          codelow == ']' 
242         )))      
243   {
244     arrowKey=codelow;
245     if (!(mask & FSHIFT) && arrowKey < 256) arrowKey=tolower(arrowKey);
246     
247     if (code>='A' && code<='Z') suppressShift=true;
248   }
249   else if (code >= VK_F1 && code <= VK_F24)
250   {
251     arrowKey = NSF1FunctionKey + code - VK_F1;
252   }
253   else switch (code&0xff)
254   {
255     #define DEFKP(wink,mack) case wink: arrowKey = mack; break;
256     DEFKP(VK_UP,NSUpArrowFunctionKey)
257     DEFKP(VK_DOWN,NSDownArrowFunctionKey)
258     DEFKP(VK_LEFT,NSLeftArrowFunctionKey)
259     DEFKP(VK_RIGHT,NSRightArrowFunctionKey)
260     DEFKP(VK_INSERT,NSInsertFunctionKey)
261     DEFKP(VK_DELETE,NSDeleteCharacter)
262     DEFKP(VK_BACK,NSBackspaceCharacter) 
263     DEFKP(VK_HOME,NSHomeFunctionKey)
264     DEFKP(VK_END,NSEndFunctionKey)
265     DEFKP(VK_NEXT,NSPageDownFunctionKey)
266     DEFKP(VK_PRIOR,NSPageUpFunctionKey)
267     DEFKP(VK_SUBTRACT,'-')
268   }
269    
270   unsigned int mask2=0;
271   if (mask&FALT) mask2|=NSAlternateKeyMask;
272   if (!suppressShift) if (mask&FSHIFT) mask2|=NSShiftKeyMask;
273   if (mask&FCONTROL) mask2|=NSCommandKeyMask;
274   if (mask&FLWIN) mask2|=NSControlKeyMask;
275      
276   [item setKeyEquivalentModifierMask:mask2];
277   [item setKeyEquivalent:arrowKey?[NSString stringWithCharacters:&arrowKey length:1]:@""];
278   return true;
281 // #define SWELL_MENU_ACCOUNTING
283 #ifdef SWELL_MENU_ACCOUNTING
284 struct menuTmp
286   NSMenu *menu;
287   NSString *lbl;
290 WDL_PtrList<menuTmp> allMenus;
291 #endif
293 @implementation SWELL_Menu
294 - (id)copyWithZone:(NSZone *)zone
296   id rv = [super copyWithZone:zone];
297 #ifdef SWELL_MENU_ACCOUNTING
298   if (rv)
299   {
300     menuTmp *mt = new menuTmp;
301     mt->menu=(NSMenu *)rv;
302     NSString *lbl = [(SWELL_Menu *)rv title];
303     mt->lbl = lbl;
304     [lbl retain];
305     allMenus.Add(mt);
306     NSLog(@"copy menu, new count=%d lbl=%@\n",allMenus.GetSize(),lbl);
307   }
308 #endif
309   return rv;
311 -(void)dealloc
313 #ifdef SWELL_MENU_ACCOUNTING
314   int x;
315   bool f=false;
316   for(x=0;x<allMenus.GetSize();x++)
317   {
318     if (allMenus.Get(x)->menu == self)
319     {
320       NSLog(@"dealloc menu, found self %@\n",allMenus.Get(x)->lbl);
321       allMenus.Delete(x);
322       f=true;
323       break;
324     }
325   }
327   NSLog(@"dealloc menu, new count=%d %@\n",allMenus.GetSize(), [self title]);
328   if (!f) 
329   {
330     NSLog(@"deleting unfound menu!!\n");
331   }
332 #endif
333   [super dealloc];
335 @end
337 HMENU CreatePopupMenu()
339   return CreatePopupMenuEx(NULL);
341 HMENU CreatePopupMenuEx(const char *title)
343   SWELL_Menu *m;
344   if (title)
345   {
346     char buf[1024];
347     __filtnametobuf(buf,title,sizeof(buf));
348     NSString *lbl=(NSString *)SWELL_CStringToCFString(buf);
349     m=[[SWELL_Menu alloc] initWithTitle:lbl];
350 #ifdef SWELL_MENU_ACCOUNTING
351     menuTmp *mt = new menuTmp;
352     mt->menu=m;
353     mt->lbl = lbl;
354     [lbl retain];
355     allMenus.Add(mt);
356     NSLog(@"alloc menu, new count=%d lbl=%@\n",allMenus.GetSize(),lbl);
357 #endif
358     [lbl release];
359   }
360   else
361   {
362     m=[[SWELL_Menu alloc] init];
363 #ifdef SWELL_MENU_ACCOUNTING
364     menuTmp *mt = new menuTmp;
365     mt->menu=m;
366     mt->lbl = @"<none>";
367     allMenus.Add(mt);
368     NSLog(@"alloc menu, new count=%d lbl=%@\n",allMenus.GetSize(),@"<none>");
369 #endif
370   }
371   [m setAutoenablesItems:NO];
373   return (HMENU)m;
376 void DestroyMenu(HMENU hMenu)
378   if (hMenu)
379   {
380     SWELL_SetMenuDestination(hMenu,NULL);
381     NSMenu *m=(NSMenu *)hMenu;
382     [m release];
383   }
388 int AddMenuItem(HMENU hMenu, int pos, const char *name, int tagid)
390   if (!hMenu) return -1;
391   NSMenu *m=(NSMenu *)hMenu;
392   NSString *label=(NSString *)SWELL_CStringToCFString(name); 
393   NSMenuItem *item=[m insertItemWithTitle:label action:NULL keyEquivalent:@"" atIndex:pos];
394   [label release];
395   [item setTag:tagid];
396   [item setEnabled:YES];
397   return 0;
400 bool DeleteMenu(HMENU hMenu, int idx, int flag)
402   if (!hMenu) return false;
403   NSMenu *m=(NSMenu *)hMenu;
404   NSMenuItem *item=NULL;
405   
406   if (flag&MF_BYPOSITION)
407   {
408     if (idx >=0 && idx < [m numberOfItems])
409       item=[m itemAtIndex:idx];
410     if (!item) return false;
411   }
412   else
413   {
414     item=[m itemWithTag:idx];
415     if (!item) 
416     {
417       const int n = (int) [m numberOfItems];
418       for (int x=0;x<n;x++)
419       {
420         item=[m itemAtIndex:x];
421         if (item && [item hasSubmenu])
422         {
423           if (DeleteMenu((HMENU)[item submenu],idx,flag)) return true;
424         }
425       }
426       return false;
427     }
428   }
429   
430   if ([item hasSubmenu])
431   {
432     HMENU sm = (HMENU)[item submenu];
433     if (sm) SWELL_SetMenuDestination(sm,NULL);
434     [m setSubmenu:nil forItem:item];
435   }
436   [m removeItem:item];
437   return true;
441 BOOL SetMenuItemInfo(HMENU hMenu, int pos, BOOL byPos, MENUITEMINFO *mi)
443   if (!hMenu) return 0;
444   NSMenu *m=(NSMenu *)hMenu;
445   NSMenuItem *item;
446   if (byPos) item=[m itemAtIndex:pos];
447   else item=[m itemWithTag:pos];
449   if (!item) 
450   {
451     if (!byPos)
452     {
453       const int n = (int)[m numberOfItems];
454       for (int x = 0; x < n; x ++)
455       {
456         item=[m itemAtIndex:x];
457         if (item && [item hasSubmenu])
458         {
459           NSMenu *m1=[item submenu];
460           if (m1 && SetMenuItemInfo((HMENU)m1,pos,byPos,mi)) return true;
461         }
462       }      
463     }
464     return 0;
465   }
466   
467   if (mi->fMask & MIIM_TYPE)
468   {
469     if (mi->fType == MFT_STRING && mi->dwTypeData)
470     {
471       char buf[1024];
472       __filtnametobuf(buf,mi->dwTypeData?mi->dwTypeData:"(null)",sizeof(buf));
473       NSString *label=(NSString *)SWELL_CStringToCFString(buf); 
474       
475       [item setTitle:label];
477       if ([item hasSubmenu])
478       {
479         NSMenu *subm=[item submenu];
480         if (subm) [subm setTitle:label];
481       }
482       
483       [label release];      
484     }
485   }
486   if (mi->fMask & MIIM_SUBMENU) 
487   {
488     NSMenu *oldMenu = [item hasSubmenu] ? [item submenu] : NULL;
489     NSMenu *newMenu = (NSMenu*)mi->hSubMenu;
490     if (oldMenu != newMenu)
491     {
492       if (oldMenu) [oldMenu retain]; // we do not destroy the old menu, caller responsibility
494       if (newMenu) [newMenu setTitle:[item title]];
495       [m setSubmenu:newMenu forItem:item];
496       if (newMenu) [newMenu release]; // let the parent menu free it
497     }
498   }
500   if (mi->fMask & MIIM_STATE)
501   {
502     [item setState:((mi->fState&MFS_CHECKED)?NSOnState:NSOffState)];
503     [item setEnabled:((mi->fState&MFS_GRAYED)?NO:YES)];
504   }
505   if (mi->fMask & MIIM_ID)
506   {
507     [item setTag:mi->wID];
508   }
509   if (mi->fMask & MIIM_DATA)
510   {
511     SWELL_DataHold* newh = [[SWELL_DataHold alloc] initWithVal:(void*)mi->dwItemData];
512     [item setRepresentedObject:newh]; 
513     [newh release];    
514   }
515   
516   return true;
519 BOOL GetMenuItemInfo(HMENU hMenu, int pos, BOOL byPos, MENUITEMINFO *mi)
521   if (!hMenu) return 0;
522   NSMenu *m=(NSMenu *)hMenu;
523   NSMenuItem *item;
524   if (byPos)
525   {
526     item=[m itemAtIndex:pos];
527   }
528   else item=[m itemWithTag:pos];
529   
530   if (!item) 
531   {
532     if (!byPos)
533     {
534       const int n = (int)[m numberOfItems];
535       for (int x = 0; x < n; x ++)
536       {
537         item=[m itemAtIndex:x];
538         if (item && [item hasSubmenu])
539         {
540           NSMenu *m1=[item submenu];
541           if (m1 && GetMenuItemInfo((HMENU)m1,pos,byPos,mi)) return true;
542         }
543       }      
544     }
545     return 0;
546   }
547   
548   if (mi->fMask & MIIM_TYPE)
549   {
550     if ([item isSeparatorItem]) mi->fType = MFT_SEPARATOR;
551     else
552     {
553       mi->fType = MFT_STRING;
554       if (mi->dwTypeData && mi->cch)
555       {
556         mi->dwTypeData[0]=0;
557         SWELL_CFStringToCString([item title], (char *)mi->dwTypeData, mi->cch);
558       }
559     }
560   }
561   
562   if (mi->fMask & MIIM_DATA)
563   {
564     SWELL_DataHold *h=[item representedObject];
565     mi->dwItemData =  (INT_PTR)(h && [h isKindOfClass:[SWELL_DataHold class]]? [h getValue] : 0);
566   }
567   
568   if (mi->fMask & MIIM_STATE)
569   {
570     mi->fState=0;
571     if ([item state]) mi->fState|=MFS_CHECKED;
572     if (![item isEnabled]) mi->fState|=MFS_GRAYED;
573   }
574   
575   if (mi->fMask & MIIM_ID)
576   {
577     mi->wID = (unsigned int)[item tag];
578   }
579   
580   if(mi->fMask & MIIM_SUBMENU)
581   {
582     mi->hSubMenu = (HMENU) ([item hasSubmenu] ? [item submenu] : NULL);
583   }
584   
585   return 1;
586   
589 void SWELL_InsertMenu(HMENU menu, int pos, unsigned int flag, UINT_PTR idx, const char *str)
591   MENUITEMINFO mi={sizeof(mi),MIIM_ID|MIIM_STATE|MIIM_TYPE,MFT_STRING,
592     (flag & ~MF_BYPOSITION),(flag&MF_POPUP) ? 0 : (unsigned int)idx,NULL,NULL,NULL,0,(char *)str};
593   
594   if (flag&MF_POPUP) 
595   {
596     mi.hSubMenu = (HMENU)idx;
597     mi.fMask |= MIIM_SUBMENU;
598     mi.fState &= ~MF_POPUP;
599   }
600   
601   if (flag&MF_SEPARATOR)
602   {
603     mi.fMask=MIIM_TYPE;
604     mi.fType=MFT_SEPARATOR;
605     mi.fState &= ~MF_SEPARATOR;
606   }
607   
608   if (flag&MF_BITMAP)
609   {
610     mi.fType=MFT_BITMAP;
611     mi.fState &= ~MF_BITMAP;
612   }
613     
614   InsertMenuItem(menu,pos,(flag&MF_BYPOSITION) ?  TRUE : FALSE, &mi);
618 void InsertMenuItem(HMENU hMenu, int pos, BOOL byPos, MENUITEMINFO *mi)
620   if (!hMenu) return;
621   NSMenu *m=(NSMenu *)hMenu;
622   NSMenuItem *item;
623   int ni = (int)[m numberOfItems];
624   
625   if (!byPos) 
626   {
627     pos = (int)[m indexOfItemWithTag:pos];
628   }
629   if (pos < 0 || pos > ni) pos=ni; 
630   
631   NSString *label=0;
632   if (mi->fType == MFT_STRING)
633   {
634     char buf[1024];
635     __filtnametobuf(buf,mi->dwTypeData?mi->dwTypeData:"(null)",sizeof(buf));
636     label=(NSString *)SWELL_CStringToCFString(buf); 
637     item=[m insertItemWithTitle:label action:NULL keyEquivalent:@"" atIndex:pos];
638   }
639   else if (mi->fType == MFT_BITMAP)
640   {
641     item=[m insertItemWithTitle:@"(no image)" action:NULL keyEquivalent:@"" atIndex:pos];
642     if (mi->dwTypeData)
643     {
644       NSImage *i=(NSImage *)GetNSImageFromHICON((HICON)mi->dwTypeData);
645       if (i)
646       {
647         [item setImage:i];
648         [item setTitle:@""];
649       }
650     }
651   }
652   else
653   {
654     item = [NSMenuItem separatorItem];
655     [m insertItem:item atIndex:pos];
656   }
657   
658   if ((mi->fMask & MIIM_SUBMENU) && mi->hSubMenu)
659   {
660     if (label) [(NSMenu *)mi->hSubMenu setTitle:label];
661     [m setSubmenu:(NSMenu *)mi->hSubMenu forItem:item];
662     [((NSMenu *)mi->hSubMenu) release]; // let the parent menu free it
663   }
664   if (label) [label release];
665   
666   if (!ni) [m setAutoenablesItems:NO];
667   [item setEnabled:YES];
668   
669   if (mi->fMask & MIIM_STATE)
670   {
671     if (mi->fState&MFS_GRAYED)
672     {
673       [item setEnabled:NO];
674     }
675     if (mi->fState&MFS_CHECKED)
676     {
677       [item setState:NSOnState];
678     }
679   }
681    if (mi->fMask & MIIM_DATA)
682   {
683     SWELL_DataHold *h=[[SWELL_DataHold alloc] initWithVal:(void*)mi->dwItemData];
684     [item setRepresentedObject:h];
685     [h release];
686   }
687   else
688   {
689     [item setRepresentedObject:nil];
690   }
692   if (mi->fMask & MIIM_ID)
693   {
694     [item setTag:mi->wID];
695   }
696   
697   int i;
698   ni = (int)[m numberOfItems];
699   // try to find a valid action/target
700   for (i = 0; i < ni; i ++)
701   {
702     NSMenuItem *fi=[m itemAtIndex:i];
703     if (fi && fi != item)
704     {
705       SEL act = [fi action];
706       id tgt = [fi target];
707       if (act || tgt)
708       {
709         if (act) [item setAction:act];
710         if (tgt) [item setTarget:tgt];
711         break;
712       }
713     }
714     if (i == 5 && ni > 14) i = ni-6; // only look at first and last 6 items or so
715   }
720 @implementation SWELL_PopupMenuRecv
721 -(id) initWithWnd:(HWND)wnd
723   if ((self = [super init]))
724   {
725     cbwnd=wnd;
726     m_act=0;
727   }
728   return self;
731 -(void) onSwellCommand:(id)sender
733   int tag=(int) [sender tag];
734   if (tag)
735     m_act=tag;
738 -(int) isCommand
740   return m_act;
743 - (void)menuNeedsUpdate:(NSMenu *)menu
745   if (cbwnd)
746   {
747     SendMessage(cbwnd,WM_INITMENUPOPUP,(WPARAM)menu,0);
748     SWELL_SetMenuDestination((HMENU)menu,(HWND)self);
749   }
752 @end
754 void SWELL_SetMenuDestination(HMENU menu, HWND hwnd)
756   if (!menu || (hwnd && ![(id)hwnd respondsToSelector:@selector(onSwellCommand:)])) return;
757   
758   NSMenu *m=(NSMenu *)menu;
759   [m setDelegate:(id)hwnd];
760   const int n = (int)[m numberOfItems];
761   for (int x = 0; x < n; x++)
762   {
763     NSMenuItem *item=[m itemAtIndex:x];
764     if (item)
765     {
766       if ([item hasSubmenu])
767       {
768         NSMenu *mm=[item submenu];
769         if (mm) SWELL_SetMenuDestination((HMENU)mm,hwnd);
770       }
771       else
772       {
773         if ([item tag])
774         {
775           [item setTarget:(id)hwnd];
776           if (hwnd) [item setAction:@selector(onSwellCommand:)];
777         }
778       }
779     }
780   }
783 int TrackPopupMenu(HMENU hMenu, int flags, int xpos, int ypos, int resvd, HWND hwnd, const RECT *r)
785   ReleaseCapture(); // match win32 -- TrackPopupMenu() ends any captures
786   if (hMenu)
787   {
788     NSMenu *m=(NSMenu *)hMenu;
789     NSView *v=(NSView *)hwnd;
790     if (v && [v isKindOfClass:[NSWindow class]]) v=[(NSWindow *)v contentView];
791     if (!v) v=[[NSApp mainWindow] contentView];
792     if (!v) return 0;
793     
794     NSEvent *event = [NSApp currentEvent];
795        
796     {      
797       //create a new event at these coordinates, faking it
798       NSWindow *w = [v window];
799       NSPoint pt = NSMakePoint(xpos, ypos);
800       pt=[w convertScreenToBase:pt];
801       pt.y -= 4;
802       NSInteger wn = [w windowNumber]; // event ? [event windowNumber] : [w windowNumber];
803       NSTimeInterval ts = event ? [event timestamp] : 0;
804       NSGraphicsContext *gctx = event ? [event context] : 0;
805       event = [NSEvent otherEventWithType:NSApplicationDefined location:pt modifierFlags:0 timestamp:ts windowNumber:wn context:gctx subtype:0 data1:0 data2:0];
806     }
807     
808     SWELL_PopupMenuRecv *recv = [[SWELL_PopupMenuRecv alloc] initWithWnd:((flags & TPM_NONOTIFY) ? 0 : hwnd)];
809     
810     SWELL_SetMenuDestination((HMENU)m,(HWND)recv);
811     
812     if (!(flags&TPM_NONOTIFY)&&hwnd)
813     {
814       SendMessage(hwnd,WM_INITMENUPOPUP,(WPARAM)m,0);
815       SWELL_SetMenuDestination((HMENU)m,(HWND)recv);
816     }
817     
818     [NSMenu popUpContextMenu:m withEvent:event forView:v];
820     int ret=[recv isCommand];    
821     SWELL_SetMenuDestination((HMENU)m,(HWND)NULL);
822     [recv release];
823     
824     if (ret<=0) return 0;
825     
826     if (flags & TPM_RETURNCMD) return ret;
827     
828     if (hwnd) SendMessage(hwnd,WM_COMMAND,ret,0);
829     
830     return 1;
831   }
832   return 0;
838 void SWELL_Menu_AddMenuItem(HMENU hMenu, const char *name, int idx, unsigned int flags)
840   MENUITEMINFO mi={sizeof(mi),MIIM_ID|MIIM_STATE|MIIM_TYPE,MFT_STRING,
841     (unsigned int) ((flags)?MFS_GRAYED:0),(unsigned int)idx,NULL,NULL,NULL,0,(char *)name};
842   if (!name)
843   {
844     mi.fType = MFT_SEPARATOR;
845     mi.fMask&=~(MIIM_STATE|MIIM_ID);
846   }
847   InsertMenuItem(hMenu,GetMenuItemCount(hMenu),TRUE,&mi);
850 int SWELL_GenerateMenuFromList(HMENU hMenu, const void *_list, int listsz)
852   SWELL_MenuGen_Entry *list = (SWELL_MenuGen_Entry *)_list;
853   const int l1=strlen(SWELL_MENUGEN_POPUP_PREFIX);
854   while (listsz>0)
855   {
856     int cnt=1;
857     if (!list->name) SWELL_Menu_AddMenuItem(hMenu,NULL,-1,0);
858     else if (!strcmp(list->name,SWELL_MENUGEN_ENDPOPUP)) break;
859     else if (!strncmp(list->name,SWELL_MENUGEN_POPUP_PREFIX,l1)) 
860     { 
861       MENUITEMINFO mi={sizeof(mi),MIIM_SUBMENU|MIIM_STATE|MIIM_TYPE,MFT_STRING,0,0,CreatePopupMenuEx(list->name+l1),NULL,NULL,0,(char *)list->name+l1};
862       cnt += SWELL_GenerateMenuFromList(mi.hSubMenu,list+1,listsz-1);
863       InsertMenuItem(hMenu,GetMenuItemCount(hMenu),TRUE,&mi);
864     }
865     else SWELL_Menu_AddMenuItem(hMenu,list->name,list->idx,list->flags);
867     list+=cnt;
868     listsz -= cnt;
869   }
870   return (int) (list + 1 - (SWELL_MenuGen_Entry *)_list);
874 SWELL_MenuResourceIndex *SWELL_curmodule_menuresource_head; // todo: move to per-module thingy
876 static SWELL_MenuResourceIndex *resById(SWELL_MenuResourceIndex *head, const char *resid)
878   SWELL_MenuResourceIndex *p=head;
879   while (p)
880   {
881     if (p->resid == resid) return p;
882     p=p->_next;
883   }
884   return 0;
887 HMENU SWELL_LoadMenu(SWELL_MenuResourceIndex *head, const char *resid)
889   SWELL_MenuResourceIndex *p;
890   
891   if (!(p=resById(head,resid))) return 0;
892   HMENU hMenu=CreatePopupMenu();
893   if (hMenu) p->createFunc(hMenu);
894   return hMenu;
897 HMENU SWELL_DuplicateMenu(HMENU menu)
899   if (!menu) return 0;
900   NSMenu *ret = (NSMenu *)[(NSMenu *)menu copy];
901   return (HMENU)ret;
904 BOOL  SetMenu(HWND hwnd, HMENU menu)
906   if (!hwnd||![(id)hwnd respondsToSelector:@selector(swellSetMenu:)]) return FALSE;
907   if (g_swell_terminating)  return FALSE;
909   [(id)hwnd swellSetMenu:(HMENU)menu];
910   NSWindow *nswnd = (NSWindow *)hwnd;
911   if ([nswnd isKindOfClass:[NSWindow class]] || 
912      ([nswnd isKindOfClass:[NSView class]] && (nswnd=[(NSView *)nswnd window]) && hwnd == (HWND)[nswnd contentView]))
913   {
914     if ([NSApp keyWindow]==nswnd &&
915         [NSApp mainMenu] != (NSMenu *)menu)
916     {
917       [NSApp setMainMenu:(NSMenu *)menu];
918       if (menu) SendMessage(hwnd,WM_INITMENUPOPUP,(WPARAM)menu,0); // find a better place for this! TODO !!!
919     }
920   }
921   
922   return TRUE;
925 HMENU GetMenu(HWND hwnd)
927   if (!hwnd) return NULL;
928   if ([(id)hwnd isKindOfClass:[NSWindow class]]) hwnd = (HWND)[(NSWindow *)hwnd contentView];
929   if ([(id)hwnd respondsToSelector:@selector(swellGetMenu)]) return (HMENU) [(id)hwnd swellGetMenu];
930   return NULL;
933 void DrawMenuBar(HWND hwnd)
938 #endif