/* NSMenu.m Copyright (C) 1999 Free Software Foundation, Inc. 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 { [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; return self; } - (void)insertItem:(id )newItem atIndex:(int)index { if ([(NSMenuItemCell *)newItem isKindOfClass:[NSMenuItemCell class]]) [menu_items insertObject:newItem atIndex:index]; else NSLog(@"You must use an NSMenuItemCell, or a derivative thereof.\n"); } - (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 { // The spec says we call [self insertItem]; but why waste the overhead? if ([(NSMenuItemCell *)newItem isKindOfClass:[NSMenuItemCell class]]) [menu_items insertObject:newItem atIndex:[menu_items count]]; else NSLog(@"You must use an NSMenuItemCell, or a derivative thereof.\n"); menu_changed = YES; } - (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; int 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