/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) Copyright (C) 2006 and later, Cockos, Inc. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. This file provides basic windows menu API to interface an NSMenu */ #ifndef SWELL_PROVIDED_BY_APP #import #include "swell.h" #include "swell-menugen.h" #include "swell-internal.h" static void __filtnametobuf(char *out, const char *in, int outsz, NSString **eqout=NULL, unsigned int *eqmaskout=NULL); bool SetMenuItemText(HMENU hMenu, int idx, int flag, const char *text) { NSMenu *menu=(NSMenu *)hMenu; if (WDL_NOT_NORMALLY(!menu)) return false; NSMenuItem *item; if (flag & MF_BYPOSITION) item=[menu itemAtIndex:idx]; else item =[menu itemWithTag:idx]; if (!item) { if (!(flag & MF_BYPOSITION)) { const int n = (int) [menu numberOfItems]; for (int x = 0; x < n; x ++) { item=[menu itemAtIndex:x]; if (item && [item hasSubmenu]) { NSMenu *m=[item submenu]; if (m && SetMenuItemText((HMENU)m,idx,flag,text)) return true; } } } return false; } char buf[1024]; NSString *eq = NULL; unsigned int eqmask = 0; __filtnametobuf(buf,text?text:"",sizeof(buf), (flag&MF_SWELL_DO_NOT_CALC_MODIFIERS) ? NULL : &eq,&eqmask); NSString *label=(NSString *)SWELL_CStringToCFString(buf); [item setTitle:label]; if ([item hasSubmenu] && [item submenu]) [[item submenu] setTitle:label]; if (eq) { [item setKeyEquivalentModifierMask:eqmask]; [item setKeyEquivalent:eq]; } [label release]; return true; } bool EnableMenuItem(HMENU hMenu, int idx, int en) { NSMenu *menu=(NSMenu *)hMenu; if (WDL_NOT_NORMALLY(!menu)) return false; NSMenuItem *item; if (en & MF_BYPOSITION) item=[menu itemAtIndex:idx]; else item =[menu itemWithTag:idx]; if (!item) { if (!(en & MF_BYPOSITION)) { const int n=(int)[menu numberOfItems]; for (int x = 0; x < n; x ++) { item=[menu itemAtIndex:x]; if (item && [item hasSubmenu]) { NSMenu *m=[item submenu]; if (m && EnableMenuItem((HMENU)m,idx,en)) return true; } } } return false; } [item setEnabled:((en&MF_GRAYED)?NO:YES)]; return true; } bool CheckMenuItem(HMENU hMenu, int idx, int chk) { NSMenu *menu=(NSMenu *)hMenu; if (WDL_NOT_NORMALLY(!menu)) return false; NSMenuItem *item; if (chk & MF_BYPOSITION) item=[menu itemAtIndex:idx]; else item =[menu itemWithTag:idx]; if (!item) { if (!(chk & MF_BYPOSITION)) { const int n=(int)[menu numberOfItems]; for (int x = 0; x < n; x ++) { item=[menu itemAtIndex:x]; if (item && [item hasSubmenu]) { NSMenu *m=[item submenu]; if (m && CheckMenuItem((HMENU)m,idx,chk)) return true; } } } return false; } [item setState:((chk&MF_CHECKED)?NSOnState:NSOffState)]; return true; } HMENU SWELL_GetCurrentMenu() { return (HMENU)[NSApp mainMenu]; } extern int g_swell_terminating; void SWELL_SetCurrentMenu(HMENU hmenu) { if (WDL_NORMALLY(hmenu && [(id)hmenu isKindOfClass:[NSMenu class]])) { if (!g_swell_terminating) [NSApp setMainMenu:(NSMenu *)hmenu]; } } HMENU GetSubMenu(HMENU hMenu, int pos) { NSMenu *menu=(NSMenu *)hMenu; WDL_ASSERT(menu != NULL); NSMenuItem *item=menu && pos >=0 && pos < [menu numberOfItems] ? [menu itemAtIndex:pos] : 0; if (item && [item hasSubmenu]) return (HMENU)[item submenu]; return 0; } int GetMenuItemCount(HMENU hMenu) { NSMenu *menu=(NSMenu *)hMenu; WDL_ASSERT(menu != NULL); return (int)[menu numberOfItems]; } int GetMenuItemID(HMENU hMenu, int pos) { NSMenu *menu=(NSMenu *)hMenu; if (pos < 0 || pos >= (int)[menu numberOfItems]) { WDL_ASSERT(pos==0); // don't assert if GetMenuItemID(0) is called on an empty menu return 0; } NSMenuItem *item=[menu itemAtIndex:pos]; if (item) { if ([item hasSubmenu]) return -1; return (int)[item tag]; } return 0; } bool SetMenuItemModifier(HMENU hMenu, int idx, int flag, int code, unsigned int mask) { if (WDL_NOT_NORMALLY(hMenu == NULL)) return false; NSMenu *menu=(NSMenu *)hMenu; NSMenuItem *item; if (flag & MF_BYPOSITION) item=[menu itemAtIndex:idx]; else item =[menu itemWithTag:idx]; if (!item) { if (!(flag & MF_BYPOSITION)) { const int n = (int)[menu numberOfItems]; for (int x = 0; x < n; x ++) { item=[menu itemAtIndex:x]; if (item && [item hasSubmenu]) { NSMenu *m=[item submenu]; if (m && SetMenuItemModifier((HMENU)m,idx,flag,code,mask)) return true; } } } return false; } bool suppressShift = false; unichar arrowKey = 0; if (code >= 'A' && code <= 'Z') { arrowKey = (mask & FSHIFT) ? code : (code + 'a' - 'A'); suppressShift=true; } else if ((code>='0' && code <= '9') || code== ' ' || (!(mask&FVIRTKEY) && (code >= '!' && code <= '~'))) { arrowKey=code; } else if (code >= VK_F1 && code <= VK_F24) { arrowKey = NSF1FunctionKey + code - VK_F1; } else switch (code&0xff) { #define DEFKP(wink,mack) case wink: arrowKey = mack; break; DEFKP(VK_UP,NSUpArrowFunctionKey) DEFKP(VK_DOWN,NSDownArrowFunctionKey) DEFKP(VK_LEFT,NSLeftArrowFunctionKey) DEFKP(VK_RIGHT,NSRightArrowFunctionKey) DEFKP(VK_INSERT,NSInsertFunctionKey) DEFKP(VK_DELETE,NSDeleteCharacter) DEFKP(VK_BACK,NSBackspaceCharacter) DEFKP(VK_HOME,NSHomeFunctionKey) DEFKP(VK_END,NSEndFunctionKey) DEFKP(VK_NEXT,NSPageDownFunctionKey) DEFKP(VK_PRIOR,NSPageUpFunctionKey) DEFKP(VK_SUBTRACT,'-') DEFKP(VK_RETURN,'\r') DEFKP(VK_TAB,'\t') DEFKP(VK_ESCAPE,27) // hmm numpad enter, what to do: DEFKP(VK_RETURN|32768, '\r') } unsigned int mask2=0; if (mask&FALT) mask2|=NSAlternateKeyMask; if (!suppressShift) if (mask&FSHIFT) mask2|=NSShiftKeyMask; if (mask&FCONTROL) mask2|=NSCommandKeyMask; if (mask&FLWIN) mask2|=NSControlKeyMask; [item setKeyEquivalentModifierMask:mask2]; [item setKeyEquivalent:arrowKey?[NSString stringWithCharacters:&arrowKey length:1]:@""]; return true; } static void __filtnametobuf(char *out, const char *input, int outsz, NSString **eqout, unsigned int *eqmaskout) { const char *inp = input; while (*inp && *inp != '\t' && outsz>1) { if (*inp == '&') inp++; *out++=*inp++; outsz--; } *out=0; if (eqout && eqmaskout && *inp) { while (*inp && *inp != '\t') inp++; if (*inp++ == '\t') { again: while (*inp) { if (!strnicmp(inp,"Ctrl+",5)) { inp += 5; *eqmaskout |= NSCommandKeyMask; } else if (!strnicmp(inp,"Alt+",4)) { inp += 4; *eqmaskout |= NSAlternateKeyMask; } else if (!strnicmp(inp,"Shift+",6)) { inp += 6; *eqmaskout |= NSShiftKeyMask; } else if (!strnicmp(inp,"Win+",4)) { inp += 4; *eqmaskout |= NSControlKeyMask; } else break; } if (WDL_NOT_NORMALLY(!*inp)) return; char tmp[128]; lstrcpyn(tmp,inp,sizeof(tmp)); char *p = tmp; while (*p && *p != ' ' && *p != ',') p++; bool was_comma = *p == ','; if (was_comma && p == tmp) p++; *p=0; if (WDL_NOT_NORMALLY(!tmp[0])) return; unichar arrowKey = 0; int idx; if (tmp[0] == 'F' && (idx=atoi(tmp+1))> 0 && idx <= 24) arrowKey = NSF1FunctionKey + idx - 1; else if (tmp[0] >= '0' && tmp[0] <= '9') arrowKey = tmp[0]; else { if (!stricmp(tmp,"Up")) arrowKey = NSUpArrowFunctionKey; else if (!stricmp(tmp,"Down")) arrowKey = NSDownArrowFunctionKey; else if (!stricmp(tmp,"Left")) arrowKey = NSLeftArrowFunctionKey; else if (!stricmp(tmp,"Right")) arrowKey = NSRightArrowFunctionKey; else if (!strnicmp(tmp,"Ins",3)) { if (was_comma) { while (*inp && *inp != ',') inp++; if (*inp) { inp++; while (*inp == ' ') inp++; if (*inp) { *eqmaskout = 0; goto again; } } } arrowKey = NSInsertFunctionKey; } else if (!strnicmp(tmp,"Del",3)) arrowKey = NSDeleteFunctionKey; else if (!strnicmp(tmp,"Back",4)) arrowKey = NSBackspaceCharacter; else if (!stricmp(tmp,"Home")) arrowKey = NSHomeFunctionKey; else if (!stricmp(tmp,"End")) arrowKey = NSEndFunctionKey; else if (!stricmp(tmp,"PageDown") || !stricmp(tmp,"PgDn")) arrowKey = NSPageDownFunctionKey; else if (!stricmp(tmp,"PageUp") || !stricmp(tmp,"PgUp")) arrowKey = NSPageUpFunctionKey; else if (!stricmp(tmp,"PageUp") || !stricmp(tmp,"PgUp")) arrowKey = NSPageUpFunctionKey; else if (!stricmp(tmp,"-")) arrowKey = '-'; else if (!stricmp(tmp,"+")) arrowKey = '+'; else if (!stricmp(tmp,"Enter")||!stricmp(tmp,"Return")) arrowKey = '\r'; else if (!stricmp(tmp,"Tab")) arrowKey = '\t'; else if (!strnicmp(tmp,"Esc",3)) arrowKey = 27; else if (!strcmp(tmp,",")) arrowKey = tmp[0]; else if (!stricmp(tmp,"Click")||!stricmp(tmp,"(Click)")) arrowKey = NSUpArrowFunctionKey; else if (!stricmp(tmp,"Doubleclick")||!stricmp(tmp,"(Doubleclick)")) arrowKey = NSHomeFunctionKey; else if (tmp[0] >= 'A' && tmp[0] <= 'Z' && !tmp[1]) { arrowKey = tmp[0]; if (*eqmaskout & NSShiftKeyMask) *eqmaskout &= ~NSShiftKeyMask; else arrowKey += 'a' - 'A'; } else if (!strcmp(tmp,"Ctrl")) { arrowKey=NSUpArrowFunctionKey; *eqmaskout |= NSCommandKeyMask; } else if (!strcmp(tmp,"Alt")) { arrowKey=NSUpArrowFunctionKey; *eqmaskout |= NSAlternateKeyMask; } else if (!strcmp(tmp,"Shift")) { arrowKey=NSUpArrowFunctionKey; *eqmaskout |= NSShiftKeyMask; } else if (!strcmp(tmp,"Win")) { arrowKey=NSUpArrowFunctionKey; *eqmaskout |= NSControlKeyMask; } else { wdl_log("swell-cocoa: unknown key in menu text: '%s' orig '%s'\n",tmp,input); } } if (arrowKey) *eqout = [NSString stringWithCharacters:&arrowKey length:1]; } } } // #define SWELL_MENU_ACCOUNTING #ifdef SWELL_MENU_ACCOUNTING struct menuTmp { NSMenu *menu; NSString *lbl; }; WDL_PtrList allMenus; #endif @implementation SWELL_Menu - (id)copyWithZone:(NSZone *)zone { id rv = [super copyWithZone:zone]; #ifdef SWELL_MENU_ACCOUNTING if (rv) { menuTmp *mt = new menuTmp; mt->menu=(NSMenu *)rv; NSString *lbl = [(SWELL_Menu *)rv title]; mt->lbl = lbl; [lbl retain]; allMenus.Add(mt); NSLog(@"copy menu, new count=%d lbl=%@\n",allMenus.GetSize(),lbl); } #endif return rv; } -(void)dealloc { #ifdef SWELL_MENU_ACCOUNTING int x; bool f=false; for(x=0;xmenu == self) { NSLog(@"dealloc menu, found self %@\n",allMenus.Get(x)->lbl); allMenus.Delete(x); f=true; break; } } NSLog(@"dealloc menu, new count=%d %@\n",allMenus.GetSize(), [self title]); if (!f) { NSLog(@"deleting unfound menu!!\n"); } #endif [super dealloc]; } @end HMENU CreatePopupMenu() { return CreatePopupMenuEx(NULL); } HMENU CreatePopupMenuEx(const char *title) { SWELL_Menu *m; if (title) { char buf[1024]; __filtnametobuf(buf,title,sizeof(buf)); NSString *lbl=(NSString *)SWELL_CStringToCFString(buf); m=[[SWELL_Menu alloc] initWithTitle:lbl]; #ifdef SWELL_MENU_ACCOUNTING menuTmp *mt = new menuTmp; mt->menu=m; mt->lbl = lbl; [lbl retain]; allMenus.Add(mt); NSLog(@"alloc menu, new count=%d lbl=%@\n",allMenus.GetSize(),lbl); #endif [lbl release]; } else { m=[[SWELL_Menu alloc] init]; #ifdef SWELL_MENU_ACCOUNTING menuTmp *mt = new menuTmp; mt->menu=m; mt->lbl = @""; allMenus.Add(mt); NSLog(@"alloc menu, new count=%d lbl=%@\n",allMenus.GetSize(),@""); #endif } [m setAutoenablesItems:NO]; return (HMENU)m; } void DestroyMenu(HMENU hMenu) { if (WDL_NORMALLY(hMenu)) { SWELL_SetMenuDestination(hMenu,NULL); NSMenu *m=(NSMenu *)hMenu; [m release]; } } int AddMenuItem(HMENU hMenu, int pos, const char *name, int tagid) { if (WDL_NOT_NORMALLY(!hMenu)) return -1; NSMenu *m=(NSMenu *)hMenu; NSString *label=(NSString *)SWELL_CStringToCFString(name); NSMenuItem *item=[m insertItemWithTitle:label action:NULL keyEquivalent:@"" atIndex:pos]; [label release]; [item setTag:tagid]; [item setEnabled:YES]; return 0; } bool DeleteMenu(HMENU hMenu, int idx, int flag) { if (WDL_NOT_NORMALLY(!hMenu)) return false; NSMenu *m=(NSMenu *)hMenu; NSMenuItem *item=NULL; if (flag&MF_BYPOSITION) { if (idx >=0 && idx < [m numberOfItems]) item=[m itemAtIndex:idx]; if (!item) return false; } else { item=[m itemWithTag:idx]; if (!item) { const int n = (int) [m numberOfItems]; for (int x=0;xfMask & MIIM_TYPE) { if ((mi->fType &~ MFT_RADIOCHECK) == MFT_STRING && mi->dwTypeData) { char buf[1024]; NSString *eq=NULL; unsigned int eqmask = 0; __filtnametobuf(buf,mi->dwTypeData?mi->dwTypeData:"(null)",sizeof(buf), (mi->fMask & MIIM_SWELL_DO_NOT_CALC_MODIFIERS) ? NULL : &eq,&eqmask); NSString *label=(NSString *)SWELL_CStringToCFString(buf); [item setTitle:label]; if ([item hasSubmenu]) { NSMenu *subm=[item submenu]; if (subm) [subm setTitle:label]; } if (eq) { [item setKeyEquivalentModifierMask:eqmask]; [item setKeyEquivalent:eq]; } [label release]; } } if (mi->fMask & MIIM_SUBMENU) { NSMenu *oldMenu = [item hasSubmenu] ? [item submenu] : NULL; NSMenu *newMenu = (NSMenu*)mi->hSubMenu; if (oldMenu != newMenu) { if (oldMenu) [oldMenu retain]; // we do not destroy the old menu, caller responsibility if (newMenu) [newMenu setTitle:[item title]]; [m setSubmenu:newMenu forItem:item]; if (newMenu) [newMenu release]; // let the parent menu free it } } if (mi->fMask & MIIM_STATE) { [item setState:((mi->fState&MFS_CHECKED)?NSOnState:NSOffState)]; [item setEnabled:((mi->fState&MFS_GRAYED)?NO:YES)]; } if (mi->fMask & MIIM_ID) { [item setTag:mi->wID]; } if (mi->fMask & MIIM_DATA) { SWELL_DataHold* newh = [[SWELL_DataHold alloc] initWithVal:(void*)mi->dwItemData]; [item setRepresentedObject:newh]; [newh release]; } return true; } BOOL GetMenuItemInfo(HMENU hMenu, int pos, BOOL byPos, MENUITEMINFO *mi) { if (WDL_NOT_NORMALLY(!hMenu)) return 0; NSMenu *m=(NSMenu *)hMenu; NSMenuItem *item; if (byPos) { item=[m itemAtIndex:pos]; } else item=[m itemWithTag:pos]; if (!item) { if (!byPos) { const int n = (int)[m numberOfItems]; for (int x = 0; x < n; x ++) { item=[m itemAtIndex:x]; if (item && [item hasSubmenu]) { NSMenu *m1=[item submenu]; if (m1 && GetMenuItemInfo((HMENU)m1,pos,byPos,mi)) return true; } } } return 0; } if (mi->fMask & MIIM_TYPE) { if ([item isSeparatorItem]) mi->fType = MFT_SEPARATOR; else { mi->fType = MFT_STRING; if (mi->dwTypeData && mi->cch) { mi->dwTypeData[0]=0; SWELL_CFStringToCString([item title], (char *)mi->dwTypeData, mi->cch); } } } if (mi->fMask & MIIM_DATA) { SWELL_DataHold *h=[item representedObject]; mi->dwItemData = (INT_PTR)(h && [h isKindOfClass:[SWELL_DataHold class]]? [h getValue] : 0); } if (mi->fMask & MIIM_STATE) { mi->fState=0; if ([item state]) mi->fState|=MFS_CHECKED; if (![item isEnabled]) mi->fState|=MFS_GRAYED; } if (mi->fMask & MIIM_ID) { mi->wID = (unsigned int)[item tag]; } if(mi->fMask & MIIM_SUBMENU) { mi->hSubMenu = (HMENU) ([item hasSubmenu] ? [item submenu] : NULL); } return 1; } void SWELL_InsertMenu(HMENU menu, int pos, unsigned int flag, UINT_PTR idx, const char *str) { MENUITEMINFO mi={sizeof(mi),MIIM_ID|MIIM_STATE|MIIM_TYPE,MFT_STRING, (flag & ~MF_BYPOSITION),(flag&MF_POPUP) ? 0 : (unsigned int)idx,NULL,NULL,NULL,0,(char *)str}; if (flag&MF_POPUP) { mi.hSubMenu = (HMENU)idx; mi.fMask |= MIIM_SUBMENU; mi.fState &= ~MF_POPUP; } if (flag&MF_SEPARATOR) { mi.fMask=MIIM_TYPE; mi.fType=MFT_SEPARATOR; mi.fState &= ~MF_SEPARATOR; } if (flag&MF_BITMAP) { mi.fType=MFT_BITMAP; mi.fState &= ~MF_BITMAP; } InsertMenuItem(menu,pos,(flag&MF_BYPOSITION) ? TRUE : FALSE, &mi); } void InsertMenuItem(HMENU hMenu, int pos, BOOL byPos, MENUITEMINFO *mi) { if (WDL_NOT_NORMALLY(!hMenu)) return; NSMenu *m=(NSMenu *)hMenu; NSMenuItem *item; int ni = (int)[m numberOfItems]; if (!byPos) { pos = (int)[m indexOfItemWithTag:pos]; } if (pos < 0 || pos > ni) pos=ni; NSString *label=0; if ((mi->fType & ~MFT_RADIOCHECK) == MFT_STRING) { char buf[1024]; NSString *eq=NULL; unsigned int eqmask = 0; __filtnametobuf(buf,mi->dwTypeData?mi->dwTypeData:"(null)",sizeof(buf), (mi->fMask & MIIM_SWELL_DO_NOT_CALC_MODIFIERS) ? NULL : &eq,&eqmask); label=(NSString *)SWELL_CStringToCFString(buf); item=[m insertItemWithTitle:label action:NULL keyEquivalent:@"" atIndex:pos]; if (eq) { [item setKeyEquivalentModifierMask:eqmask]; [item setKeyEquivalent:eq]; } } else if (mi->fType == MFT_BITMAP) { item=[m insertItemWithTitle:@"(no image)" action:NULL keyEquivalent:@"" atIndex:pos]; if (mi->dwTypeData) { NSImage *i=(NSImage *)GetNSImageFromHICON((HICON)mi->dwTypeData); if (i) { [item setImage:i]; [item setTitle:@""]; } } } else { item = [NSMenuItem separatorItem]; [m insertItem:item atIndex:pos]; } if ((mi->fMask & MIIM_SUBMENU) && mi->hSubMenu) { if (label) [(NSMenu *)mi->hSubMenu setTitle:label]; [m setSubmenu:(NSMenu *)mi->hSubMenu forItem:item]; [((NSMenu *)mi->hSubMenu) release]; // let the parent menu free it } if (label) [label release]; if (!ni) [m setAutoenablesItems:NO]; [item setEnabled:YES]; if (mi->fMask & MIIM_STATE) { if (mi->fState&MFS_GRAYED) { [item setEnabled:NO]; } if (mi->fState&MFS_CHECKED) { [item setState:NSOnState]; } } if (mi->fMask & MIIM_DATA) { SWELL_DataHold *h=[[SWELL_DataHold alloc] initWithVal:(void*)mi->dwItemData]; [item setRepresentedObject:h]; [h release]; } else { [item setRepresentedObject:nil]; } if (mi->fMask & MIIM_ID) { [item setTag:mi->wID]; } int i; ni = (int)[m numberOfItems]; // try to find a valid action/target for (i = 0; i < ni; i ++) { NSMenuItem *fi=[m itemAtIndex:i]; if (fi && fi != item) { SEL act = [fi action]; id tgt = [fi target]; if (act || tgt) { if (act) [item setAction:act]; if (tgt) [item setTarget:tgt]; break; } } if (i == 5 && ni > 14) i = ni-6; // only look at first and last 6 items or so } } @implementation SWELL_PopupMenuRecv -(id) initWithWnd:(HWND)wnd { if ((self = [super init])) { cbwnd=wnd; m_act=0; } return self; } -(void) onSwellCommand:(id)sender { int tag=(int) [sender tag]; if (tag) m_act=tag; } -(int) isCommand { return m_act; } - (void)menuNeedsUpdate:(NSMenu *)menu { if (cbwnd) { SendMessage(cbwnd,WM_INITMENUPOPUP,(WPARAM)menu,0); SWELL_SetMenuDestination((HMENU)menu,(HWND)self); } } @end static void SWELL_SetMenuDestinationInt(NSMenu *m, HWND hwnd, bool is_top_level, bool do_skip_sub) { [m setDelegate:(id)hwnd]; const int n = (int)[m numberOfItems]; for (int x = 0; x < n; x++) { NSMenuItem *item=[m itemAtIndex:x]; if (item) { if ([item hasSubmenu]) { NSMenu *mm=[item submenu]; if (mm) { if (do_skip_sub) { id del = [mm delegate]; NSString *cn = del ? [del className] : NULL; if (cn) { char buf[1024]; SWELL_CFStringToCString(cn, buf, sizeof(buf)); if (strstr(buf,"NSServices")) continue; } } SWELL_SetMenuDestinationInt(mm,hwnd,false, is_top_level && !x); } } else { if ([item tag]) { [item setTarget:(id)hwnd]; if (hwnd) [item setAction:@selector(onSwellCommand:)]; } } } } } void SWELL_SetMenuDestination(HMENU menu, HWND hwnd) { if (WDL_NOT_NORMALLY(!menu || (hwnd && ![(id)hwnd respondsToSelector:@selector(onSwellCommand:)]))) return; NSMenu *m = (NSMenu *)menu, *par = [m supermenu]; bool do_skip_sub = false; if (par && ![par supermenu] && [par numberOfItems]>0) { NSMenuItem *item = [par itemAtIndex:0]; do_skip_sub = item && [item hasSubmenu] && [item submenu] == m; } SWELL_SetMenuDestinationInt(m,hwnd,!par,do_skip_sub); } int TrackPopupMenu(HMENU hMenu, int flags, int xpos, int ypos, int resvd, HWND hwnd, const RECT *r) { ReleaseCapture(); // match win32 -- TrackPopupMenu() ends any captures if (WDL_NORMALLY(hMenu)) { NSMenu *m=(NSMenu *)hMenu; NSView *v=(NSView *)hwnd; if (v && [v isKindOfClass:[NSWindow class]]) v=[(NSWindow *)v contentView]; if (!v) v=[[NSApp mainWindow] contentView]; if (!v) return 0; NSEvent *event = [NSApp currentEvent]; { //create a new event at these coordinates, faking it NSWindow *w = [v window]; NSPoint pt = NSMakePoint(xpos, ypos); pt=[w convertScreenToBase:pt]; pt.y -= 4; NSInteger wn = [w windowNumber]; // event ? [event windowNumber] : [w windowNumber]; NSTimeInterval ts = event ? [event timestamp] : 0; NSGraphicsContext *gctx = event ? [event context] : 0; event = [NSEvent otherEventWithType:NSApplicationDefined location:pt modifierFlags:0 timestamp:ts windowNumber:wn context:gctx subtype:0 data1:0 data2:0]; } SWELL_PopupMenuRecv *recv = [[SWELL_PopupMenuRecv alloc] initWithWnd:((flags & TPM_NONOTIFY) ? 0 : hwnd)]; SWELL_SetMenuDestination((HMENU)m,(HWND)recv); if (!(flags&TPM_NONOTIFY)&&hwnd) { SendMessage(hwnd,WM_INITMENUPOPUP,(WPARAM)m,0); SWELL_SetMenuDestination((HMENU)m,(HWND)recv); } [NSMenu popUpContextMenu:m withEvent:event forView:v]; int ret=[recv isCommand]; SWELL_SetMenuDestination((HMENU)m,(HWND)NULL); [recv release]; if (ret<=0) return 0; if (flags & TPM_RETURNCMD) return ret; if (hwnd) SendMessage(hwnd,WM_COMMAND,ret,0); return 1; } return 0; } void SWELL_Menu_AddMenuItem(HMENU hMenu, const char *name, int idx, unsigned int flags) { MENUITEMINFO mi={sizeof(mi),MIIM_ID|MIIM_STATE|MIIM_TYPE,MFT_STRING, (unsigned int) ((flags)?MFS_GRAYED:0),(unsigned int)idx,NULL,NULL,NULL,0,(char *)name}; if (!name) { mi.fType = MFT_SEPARATOR; mi.fMask&=~(MIIM_STATE|MIIM_ID); } InsertMenuItem(hMenu,GetMenuItemCount(hMenu),TRUE,&mi); } int SWELL_GenerateMenuFromList(HMENU hMenu, const void *_list, int listsz) { SWELL_MenuGen_Entry *list = (SWELL_MenuGen_Entry *)_list; const int l1=strlen(SWELL_MENUGEN_POPUP_PREFIX); while (listsz>0) { int cnt=1; if (!list->name) SWELL_Menu_AddMenuItem(hMenu,NULL,-1,0); else if (!strcmp(list->name,SWELL_MENUGEN_ENDPOPUP)) break; else if (!strncmp(list->name,SWELL_MENUGEN_POPUP_PREFIX,l1)) { 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}; cnt += SWELL_GenerateMenuFromList(mi.hSubMenu,list+1,listsz-1); InsertMenuItem(hMenu,GetMenuItemCount(hMenu),TRUE,&mi); } else SWELL_Menu_AddMenuItem(hMenu,list->name,list->idx,list->flags); list+=cnt; listsz -= cnt; } return (int) (list + 1 - (SWELL_MenuGen_Entry *)_list); } SWELL_MenuResourceIndex *SWELL_curmodule_menuresource_head; // todo: move to per-module thingy static SWELL_MenuResourceIndex *resById(SWELL_MenuResourceIndex *head, const char *resid) { SWELL_MenuResourceIndex *p=head; while (p) { if (p->resid == resid) return p; p=p->_next; } return 0; } HMENU SWELL_LoadMenu(SWELL_MenuResourceIndex *head, const char *resid) { SWELL_MenuResourceIndex *p; if (!(p=resById(head,resid))) return 0; HMENU hMenu=CreatePopupMenu(); if (hMenu) p->createFunc(hMenu); return hMenu; } HMENU SWELL_DuplicateMenu(HMENU menu) { if (WDL_NOT_NORMALLY(!menu)) return 0; NSMenu *ret = (NSMenu *)[(NSMenu *)menu copy]; return (HMENU)ret; } BOOL SetMenu(HWND hwnd, HMENU menu) { if (WDL_NOT_NORMALLY(!hwnd||![(id)hwnd respondsToSelector:@selector(swellSetMenu:)])) return FALSE; if (g_swell_terminating) return FALSE; [(id)hwnd swellSetMenu:(HMENU)menu]; NSWindow *nswnd = (NSWindow *)hwnd; if ([nswnd isKindOfClass:[NSWindow class]] || ([nswnd isKindOfClass:[NSView class]] && (nswnd=[(NSView *)nswnd window]) && hwnd == (HWND)[nswnd contentView])) { if ([NSApp keyWindow]==nswnd && [NSApp mainMenu] != (NSMenu *)menu) { [NSApp setMainMenu:(NSMenu *)menu]; if (menu) SendMessage(hwnd,WM_INITMENUPOPUP,(WPARAM)menu,0); // find a better place for this! TODO !!! } } return TRUE; } HMENU GetMenu(HWND hwnd) { if (WDL_NOT_NORMALLY(!hwnd)) return NULL; if ([(id)hwnd isKindOfClass:[NSWindow class]]) hwnd = (HWND)[(NSWindow *)hwnd contentView]; if ([(id)hwnd respondsToSelector:@selector(swellGetMenu)]) return (HMENU) [(id)hwnd swellGetMenu]; return NULL; } void DrawMenuBar(HWND hwnd) { } #endif