/* NSMenu.m Copyright (C) 1999 Free Software Foundation, Inc. Author: Michael Hanni Date: 1999 A completely rewritten version of the original source by Scott Christley. and: Author: Ovidiu Predescu Date: May 1997 and: Author: Felipe A. Rodriguez Date: July 1998 This file is part of the GNUstep GUI Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static NSZone *menuZone = NULL; @implementation NSMenu // Class Methods + (void) initialize { if (self == [NSMenu class]) { [self setVersion: 1]; } } + (void) setMenuZone: (NSZone *)zone { menuZone = zone; } // Methods. - (id) init { return [self initWithTitle: @"Menu"]; } - (id) initWithTitle: (NSString *)aTitle { [super init]; // Keep the title. ASSIGN(menu_title, aTitle); // Create an array to store out cells. menu_items = [NSMutableArray new]; // Create a NSMenuView to draw our cells. menu_view = [[NSMenuView alloc] initWithFrame: NSMakeRect(0,0,50,50)]; // Set ourself as the menu for this view. [menu_view setMenu: self]; // We have no supermenu. menu_supermenu = nil; menu_is_tornoff = NO; menu_changed = YES; /* According to the spec, menus do autoenable by default */ menu_autoenable = YES; return self; } /* * - (void)insertItem: (id )newItem * atIndex: (int)index * * This method has been modified to convert anything that conforms to the * Protocol into a NSMenuItemCell which will be added to the * items array. * * Blame: Michael */ - (void) insertItem: (id )newItem atIndex: (int)index { if ([(id)newItem conformsToProtocol: @protocol(NSMenuItem)]) { if ([(id)newItem isKindOfClass: [NSMenuItemCell class]]) [menu_items insertObject: newItem atIndex: index]; else { [self insertItemWithTitle: [newItem title] action: [newItem action] keyEquivalent: [newItem keyEquivalent] atIndex: index]; [(NSMenuItem *)newItem release]; } } else NSLog(@"You must use an object that conforms to NSMenuItem.\n"); menu_changed = YES; } - (id ) insertItemWithTitle: (NSString *)aString action: (SEL)aSelector keyEquivalent: (NSString *)charCode atIndex: (unsigned int)index { id anItem = [NSMenuItemCell new]; [anItem setTitle: aString]; [anItem setAction: aSelector]; [anItem setKeyEquivalent: charCode]; [menu_items insertObject: anItem atIndex: index]; menu_changed = YES; return anItem; } - (void) addItem: (id )newItem { [self insertItem: newItem atIndex: [menu_items count]]; } - (id ) addItemWithTitle: (NSString *)aString action: (SEL)aSelector keyEquivalent: (NSString *)keyEquiv { return [self insertItemWithTitle: aString action: aSelector keyEquivalent: keyEquiv atIndex: [menu_items count]]; } - (void) removeItem: (id )anItem { if ([(NSMenuItemCell *)anItem isKindOfClass: [NSMenuItemCell class]]) { int _index = [menu_items indexOfObject: anItem]; if (_index == -1) return; [menu_items removeObjectAtIndex: _index]; } else { NSLog(@"You must use an NSMenuItemCell, or a derivative thereof.\n"); } menu_changed = YES; } - (void) removeItemAtIndex: (int)index { [menu_items removeObjectAtIndex: index]; menu_changed = YES; } - (void) itemChanged: (id )anObject { // another nebulous method in NSMenu. } - (id ) itemWithTag: (int)aTag { unsigned i, count = [menu_items count]; id menuCell; for (i = 0; i < count; i++) { menuCell = [menu_items objectAtIndex: i]; if ([menuCell tag] == aTag) return menuCell; } return nil; } - (id ) itemWithTitle: (NSString*)aString { unsigned i, count = [menu_items count]; id menuCell; for (i = 0; i < count; i++) { menuCell = [menu_items objectAtIndex: i]; if ([[menuCell title] isEqual: aString]) return menuCell; } return nil; } - (id ) itemAtIndex: (int)index { // FIXME should raise an exception if out of range. return [menu_items objectAtIndex: index]; } - (int) numberOfItems { return [menu_items count]; } - (NSArray *) itemArray { return (NSArray *)menu_items; } - (int) indexOfItem: (id )anObject { if (![(NSMenuItemCell *)anObject isKindOfClass: [NSMenuItemCell class]]) { NSLog(@"You must use an NSMenuItemCell, or a derivative thereof.\n"); return -1; } return [menu_items indexOfObject: anObject]; } - (int) indexOfItemWithTitle: (NSString *)aTitle { id anItem; if ((anItem = [self itemWithTitle: aTitle])) return [menu_items indexOfObject: anItem]; else return -1; } - (int) indexOfItemWithTag: (int)aTag { id anItem; if ((anItem = [self itemWithTag: aTag])) return [menu_items indexOfObject: anItem]; else return -1; } - (int) indexOfItemWithTarget: (id)anObject andAction: (SEL)actionSelector { return -1; } - (int) indexOfItemWithRepresentedObject: (id)anObject { return -1; } - (int) indexOfItemWithSubmenu: (NSMenu *)anObject { return -1; } // Dealing with submenus. - (void) setSubmenu: (NSMenu *)aMenu forItem: (id ) anItem { [anItem setTarget: aMenu]; [anItem setAction: @selector(submenuAction:)]; if (aMenu) aMenu->menu_supermenu = self; ASSIGN(aMenu->menu_title, [anItem title]); // notification that the menu has changed. } - (void) submenuAction: (id)sender { } - (NSMenu *) attachedMenu { return menu_attached_menu; } - (BOOL) isAttached { // eh? return menu_supermenu && [menu_supermenu attachedMenu] == self; } - (BOOL) isTornOff { return menu_is_tornoff; } - (NSPoint) locationForSubmenu: (NSMenu *)aSubmenu { return NSZeroPoint; } - (NSMenu *) supermenu { return menu_supermenu; } - (void) setSupermenu: (NSMenu *)supermenu { ASSIGN(menu_supermenu, supermenu); } - (void) setAutoenablesItems: (BOOL)flag { menu_autoenable = flag; } - (BOOL) autoenablesItems { return menu_autoenable; } - (void) update { // FIXME: needs to be checked. id cells; unsigned i, count; id theApp = [NSApplication sharedApplication]; if (menu_changed) [self sizeToFit]; if (![self autoenablesItems]) return; count = [menu_items count]; /* Temporary disable automatic displaying of menu */ [self setMenuChangedMessagesEnabled: NO]; for (i = 0; i < count; i++) { id cell = [menu_items objectAtIndex: i]; SEL action = [cell action]; id target; NSWindow *keyWindow; NSWindow *mainWindow; id responder; id delegate; id validator = nil; BOOL wasEnabled = [cell isEnabled]; BOOL shouldBeEnabled; /* Update the submenu items if any */ if ([cell hasSubmenu]) [[cell target] update]; /* If there is no action - there can be no validator for the cell */ if (action) { /* If there is a target use that for validation (or nil). */ if ((target = [cell target])) { if ([target respondsToSelector: action]) { validator = target; } } else { validator = [theApp targetForAction: action]; } } if (validator == nil) { shouldBeEnabled = NO; } else if ([validator respondsToSelector: @selector(validateMenuItem:)]) { shouldBeEnabled = [validator validateMenuItem: cell]; } else { shouldBeEnabled = YES; } if (shouldBeEnabled != wasEnabled) { [cell setEnabled: shouldBeEnabled]; // FIXME // [menuCells setNeedsDisplayInRect: [menuCells cellFrameAtRow: i]]; } } if (menu_changed) [self sizeToFit]; /* Reenable displaying of menus */ [self setMenuChangedMessagesEnabled: YES]; } - (BOOL) performKeyEquivalent: (NSEvent*)theEvent { unsigned i; unsigned count = [menu_items count]; NSEventType type = [theEvent type]; if (type != NSKeyDown && type != NSKeyUp) return NO; for (i = 0; i < count; i++) { id cell = [menu_items objectAtIndex: i]; if ([cell hasSubmenu]) { if ([[cell target] performKeyEquivalent: theEvent]) { /* The event has been handled by a cell in submenu */ return YES; } } else { if ([[cell keyEquivalent] isEqual: [theEvent charactersIgnoringModifiers]]) { [menu_view lockFocus]; [(id)cell performClick: self]; [menu_view unlockFocus]; return YES; } } } return NO; } - (void) performActionForItem: (id )cell { NSNotificationCenter *nc; NSDictionary *d; if (![cell isEnabled]) return; nc = [NSNotificationCenter defaultCenter]; d = [NSDictionary dictionaryWithObject: cell forKey: @"MenuItem"]; [nc postNotificationName: NSMenuWillSendActionNotification object: self userInfo: d]; [[NSApplication sharedApplication] sendAction: [cell action] to: [cell target] from: cell]; [nc postNotificationName: NSMenuDidSendActionNotification object: self userInfo: d]; } - (void) setTitle: (NSString*)aTitle { ASSIGN(menu_title, aTitle); [self sizeToFit]; } - (NSString*) title { return menu_title; } - (void) setMenuRepresentation: (id)menuRep { ASSIGN(menu_rep, menuRep); } - (id) menuRepresentation { return menu_rep; } - (void) setMenuChangedMessagesEnabled: (BOOL)flag { menu_ChangedMessagesEnabled = flag; } - (BOOL) menuChangedMessagesEnabled { return menu_ChangedMessagesEnabled; } - (void) sizeToFit { // NSLog(@"- sizeToFit called in NSMenu\n"); // // [menu_view sizeToFit]; // [menu_view setNeedsDisplay: YES]; // menu_changed = NO; } - (void) helpRequested: (NSEvent *)event { // Won't be implemented until we have NSHelp* } // NSCoding - (id) initWithCoder: (NSCoder*)aDecoder { return self; } - (void) encodeWithCoder: (NSCoder*)aCoder { } //NSCopying - (id) copyWithZone: (NSZone*)zone { return self; } @end