1996-05-30 20:03:15 +00:00
|
|
|
/*
|
|
|
|
NSMenu.m
|
|
|
|
|
|
|
|
Copyright (C) 1996 Free Software Foundation, Inc.
|
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
Author: Ovidiu Predescu <ovidiu@net-community.com>
|
|
|
|
Date: May 1997
|
|
|
|
A completely rewritten version of the original source by Scott Christley.
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
|
|
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
|
1996-10-18 17:14:13 +00:00
|
|
|
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.
|
1996-05-30 20:03:15 +00:00
|
|
|
*/
|
|
|
|
|
1997-09-23 22:43:24 +00:00
|
|
|
#include <gnustep/gui/config.h>
|
1997-02-18 00:29:25 +00:00
|
|
|
#include <Foundation/NSCoder.h>
|
|
|
|
#include <Foundation/NSArray.h>
|
1997-07-07 16:56:52 +00:00
|
|
|
#include <Foundation/NSProcessInfo.h>
|
1997-08-27 21:20:19 +00:00
|
|
|
#include <Foundation/NSString.h>
|
1997-07-07 16:56:52 +00:00
|
|
|
|
1997-02-18 00:29:25 +00:00
|
|
|
#include <AppKit/NSMatrix.h>
|
1997-07-07 16:56:52 +00:00
|
|
|
#include <AppKit/NSApplication.h>
|
|
|
|
#include <AppKit/NSWindow.h>
|
|
|
|
#include <AppKit/NSEvent.h>
|
|
|
|
#include <AppKit/NSFont.h>
|
|
|
|
#include <AppKit/NSMenu.h>
|
1997-10-16 19:55:54 +00:00
|
|
|
#include <math.h>
|
1996-05-30 20:03:15 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
#ifdef MAX
|
|
|
|
# undef MAX
|
|
|
|
#endif
|
1997-12-04 01:58:57 +00:00
|
|
|
#define MAX(a, b) \
|
|
|
|
({typeof(a) _a = (a); typeof(b) _b = (b); \
|
1997-07-07 16:56:52 +00:00
|
|
|
_a > _b ? _a : _b; })
|
1996-05-30 20:03:15 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
|
|
|
|
@interface NSMenu (PrivateMethods2)
|
|
|
|
- (void)_menuChanged;
|
|
|
|
@end
|
|
|
|
|
1997-12-04 01:58:57 +00:00
|
|
|
@interface NSMenuMatrix (PrivateMethods2)
|
|
|
|
- (void)_resizeMenuForCellSize;
|
|
|
|
@end
|
|
|
|
|
1998-08-30 16:06:47 +00:00
|
|
|
//*****************************************************************************
|
|
|
|
//
|
|
|
|
// NSMenuMatrix
|
|
|
|
//
|
|
|
|
//*****************************************************************************
|
1997-07-07 16:56:52 +00:00
|
|
|
|
|
|
|
@implementation NSMenuMatrix
|
|
|
|
|
|
|
|
// Class variables
|
|
|
|
static NSFont* menuFont = nil;
|
|
|
|
|
|
|
|
- initWithFrame:(NSRect)rect
|
1996-05-30 20:03:15 +00:00
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
[super initWithFrame:rect];
|
|
|
|
cells = [NSMutableArray new];
|
|
|
|
|
|
|
|
/* Don't initialize menuFont in +initialize since we don't know if the
|
|
|
|
DGS process knows anything about the fonts yet. */
|
|
|
|
if (!menuFont)
|
|
|
|
menuFont = [[NSFont systemFontOfSize:0] retain];
|
|
|
|
|
1998-10-15 12:04:53 +00:00
|
|
|
cellSize = NSMakeSize (1, [menuFont pointSize] - [menuFont descender] + 6);
|
1997-07-07 16:56:52 +00:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)dealloc
|
|
|
|
{
|
|
|
|
NSDebugLog (@"NSMenuMatrix of menu '%@' dealloc", [menu title]);
|
|
|
|
|
|
|
|
[cells release];
|
|
|
|
[super dealloc];
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (id)copyWithZone:(NSZone*)zone
|
1996-05-30 20:03:15 +00:00
|
|
|
{
|
1997-08-18 17:10:23 +00:00
|
|
|
NSMenuMatrix* copy = [[isa alloc] initWithFrame:[self frame]];
|
1997-07-07 16:56:52 +00:00
|
|
|
int i, count;
|
|
|
|
|
|
|
|
NSDebugLog (@"copy menu matrix of menu with title '%@'", [menu title]);
|
|
|
|
for (i = 0, count = [cells count]; i < count; i++) {
|
|
|
|
id aCell = [cells objectAtIndex:i];
|
|
|
|
id cellCopy = [[aCell copyWithZone:zone] autorelease];
|
|
|
|
|
|
|
|
[copy->cells addObject:cellCopy];
|
|
|
|
}
|
|
|
|
|
|
|
|
copy->cellSize = cellSize;
|
|
|
|
copy->menu = menu;
|
|
|
|
if (selectedCell) {
|
|
|
|
int index = [cells indexOfObject:selectedCell];
|
|
|
|
|
|
|
|
copy->selectedCell = [[cells objectAtIndex:index] retain];
|
|
|
|
}
|
|
|
|
copy->selectedCellRect = selectedCellRect;
|
|
|
|
|
|
|
|
return copy;
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (void)_resizeMenuForCellSize
|
1996-09-12 19:24:32 +00:00
|
|
|
{
|
1997-12-04 01:58:57 +00:00
|
|
|
int i, count;
|
|
|
|
float titleWidth;
|
|
|
|
|
|
|
|
/* Compute the new width of the menu cells matrix */
|
|
|
|
cellSize.width = 0;
|
|
|
|
count = [cells count];
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
titleWidth = [menuFont widthOfString:
|
|
|
|
[[cells objectAtIndex:i] stringValue]];
|
|
|
|
cellSize.width = MAX(titleWidth + ADDITIONAL_WIDTH, cellSize.width);
|
|
|
|
}
|
|
|
|
cellSize.width = MAX([menuFont widthOfString:[menu title]]
|
|
|
|
+ ADDITIONAL_WIDTH,
|
|
|
|
cellSize.width);
|
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
/* Resize the frame to hold all the menu cells */
|
|
|
|
[super setFrameSize:NSMakeSize (cellSize.width,
|
|
|
|
(cellSize.height + INTERCELL_SPACE) * [cells count] - INTERCELL_SPACE)];
|
1996-09-12 19:24:32 +00:00
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (id <NSMenuItem>)insertItemWithTitle:(NSString*)aString
|
1998-09-08 21:50:33 +00:00
|
|
|
action:(SEL)aSelector
|
|
|
|
keyEquivalent:(NSString*)charCode
|
|
|
|
atIndex:(unsigned int)index
|
1996-05-30 20:03:15 +00:00
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
id menuCell = [[[NSMenu cellClass] new] autorelease];
|
|
|
|
|
1998-07-18 19:21:22 +00:00
|
|
|
[menuCell setFont:menuFont]; // set font first in order to avoid
|
|
|
|
// recalc of some cached params in xraw
|
1997-07-07 16:56:52 +00:00
|
|
|
[menuCell setTitle:aString];
|
|
|
|
[menuCell setAction:aSelector];
|
|
|
|
[menuCell setKeyEquivalent:charCode];
|
|
|
|
|
|
|
|
[cells insertObject:menuCell atIndex:index];
|
|
|
|
|
|
|
|
return menuCell;
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (void)removeItem:(id <NSMenuItem>)anItem
|
1996-05-30 20:03:15 +00:00
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
int row = [cells indexOfObject:anItem];
|
1996-10-03 18:45:41 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
if (row == -1)
|
|
|
|
return;
|
1996-09-12 19:24:32 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
[cells removeObjectAtIndex:row];
|
|
|
|
}
|
1996-09-12 19:24:32 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (NSArray*)itemArray { return cells; }
|
1996-10-03 18:45:41 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (id <NSMenuItem>)itemWithTitle:(NSString*)aString
|
|
|
|
{
|
|
|
|
int i, count = [cells count];
|
|
|
|
id menuCell;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
menuCell = [cells objectAtIndex:i];
|
|
|
|
if ([[menuCell title] isEqual:aString])
|
|
|
|
return menuCell;
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id <NSMenuItem>)itemWithTag:(int)aTag
|
|
|
|
{
|
|
|
|
int i, count = [cells count];
|
|
|
|
id menuCell;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
menuCell = [cells objectAtIndex:i];
|
|
|
|
if ([menuCell tag] == aTag)
|
|
|
|
return menuCell;
|
|
|
|
}
|
|
|
|
return nil;
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (NSRect)cellFrameAtRow:(int)index
|
1996-05-30 20:03:15 +00:00
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
NSRect rect;
|
1996-05-30 20:03:15 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
rect.origin.x = 0;
|
|
|
|
rect.origin.y = ([cells count] - index - 1)
|
|
|
|
* (cellSize.height + INTERCELL_SPACE);
|
|
|
|
rect.size = cellSize;
|
1996-05-30 20:03:15 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
return rect;
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (void)drawRect:(NSRect)rect
|
1996-05-30 20:03:15 +00:00
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
int i, count = [cells count];
|
1997-10-09 22:55:31 +00:00
|
|
|
int max, howMany;
|
1997-07-07 16:56:52 +00:00
|
|
|
NSRect intRect = {{0, 0}, {0, 0}};
|
1996-05-30 20:03:15 +00:00
|
|
|
|
1997-10-16 19:55:54 +00:00
|
|
|
// If there are no cells then just return
|
|
|
|
if (count == 0) return;
|
|
|
|
|
|
|
|
max = ceil((float)count - rect.origin.y / (cellSize.height + INTERCELL_SPACE));
|
|
|
|
howMany = ceil(rect.size.height / (cellSize.height + INTERCELL_SPACE));
|
1997-10-09 22:55:31 +00:00
|
|
|
|
|
|
|
intRect.origin.y = (count - max) * (cellSize.height + INTERCELL_SPACE);
|
1997-07-07 16:56:52 +00:00
|
|
|
intRect.size = cellSize;
|
1997-10-16 19:55:54 +00:00
|
|
|
for (i = max - 1; howMany > 0; i--, howMany--) {
|
1997-07-07 16:56:52 +00:00
|
|
|
id aCell = [cells objectAtIndex:i];
|
1996-05-30 20:03:15 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
[aCell drawWithFrame:intRect inView:self];
|
|
|
|
intRect.origin.y += cellSize.height + INTERCELL_SPACE;
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
|
|
|
|
1998-08-30 16:06:47 +00:00
|
|
|
- (NSSize)cellSize { return cellSize; }
|
1997-07-07 16:56:52 +00:00
|
|
|
- (void)setMenu:(NSMenu*)anObject { menu = anObject; }
|
1998-08-30 16:06:47 +00:00
|
|
|
- (void)setSelectedCell:(id)aCell { selectedCell = aCell; }
|
|
|
|
- (id)selectedCell { return selectedCell; }
|
|
|
|
- (NSRect)selectedCellRect { return selectedCellRect; }
|
1997-07-07 16:56:52 +00:00
|
|
|
|
|
|
|
@end /* NSMenuMatrix */
|
|
|
|
|
|
|
|
|
1998-08-30 16:06:47 +00:00
|
|
|
//*****************************************************************************
|
|
|
|
//
|
|
|
|
// NSMenu
|
|
|
|
//
|
|
|
|
//*****************************************************************************
|
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
@implementation NSMenu
|
|
|
|
|
|
|
|
// Class variables
|
|
|
|
static NSZone *menuZone = NULL;
|
|
|
|
static Class menuCellClass = nil;
|
|
|
|
|
|
|
|
+ (void)initialize
|
1996-05-30 20:03:15 +00:00
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
menuCellClass = [NSMenuItem class];
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
+ (void)setMenuZone:(NSZone*)zone
|
1996-05-30 20:03:15 +00:00
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
menuZone = zone;
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
+ (NSZone*)menuZone
|
1996-09-12 19:24:32 +00:00
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
return menuZone;
|
1996-09-12 19:24:32 +00:00
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
+ (void)setCellClass:(Class)aClass
|
1996-05-30 20:03:15 +00:00
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
menuCellClass = aClass;
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
+ (Class)cellClass
|
|
|
|
{
|
|
|
|
return menuCellClass;
|
|
|
|
}
|
|
|
|
|
|
|
|
- init
|
|
|
|
{
|
|
|
|
return [self initWithTitle:
|
|
|
|
[[[NSProcessInfo processInfo] processName] lastPathComponent]];
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (id)initWithTitle:(NSString*)aTitle
|
1996-05-30 20:03:15 +00:00
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
// SUBCLASS to initialize other "instance variables"
|
|
|
|
NSRect rect = {{0, 0}, {80, 20}};
|
|
|
|
|
|
|
|
ASSIGN(title, aTitle);
|
|
|
|
menuCells = [[NSMenuMatrix alloc] initWithFrame:rect];
|
|
|
|
[menuCells setMenu:self];
|
|
|
|
menuChangedMessagesEnabled = YES;
|
|
|
|
autoenablesItems = YES;
|
|
|
|
return self;
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (void)dealloc
|
|
|
|
{
|
|
|
|
NSDebugLog (@"NSMenu '%@' dealloc", title);
|
1996-05-30 20:03:15 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
[title release];
|
|
|
|
[menuCells release];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
1996-09-12 19:24:32 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (id)copyWithZone:(NSZone*)zone
|
|
|
|
{
|
|
|
|
NSMenu* copy = NSAllocateObject (isa, 0, zone);
|
|
|
|
int i, count;
|
|
|
|
NSArray* cells;
|
|
|
|
|
|
|
|
NSDebugLog (@"copy menu with title '%@'", [self title]);
|
|
|
|
|
|
|
|
copy->title = [title copyWithZone:zone];
|
|
|
|
|
|
|
|
copy->menuCells = [menuCells copyWithZone:zone];
|
|
|
|
[copy->menuCells setMenu:copy];
|
|
|
|
|
|
|
|
/* Change the supermenu object of the new cells to the new menu */
|
|
|
|
cells = [copy->menuCells itemArray];
|
|
|
|
for (i = 0, count = [cells count]; i < count; i++) {
|
|
|
|
id cell = [cells objectAtIndex:i];
|
|
|
|
|
|
|
|
if ([cell hasSubmenu]) {
|
|
|
|
NSMenu* submenu = [cell target];
|
|
|
|
|
|
|
|
submenu->supermenu = copy;
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
1997-07-07 16:56:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
[copy->menuCells setFrame:[menuCells frame]];
|
|
|
|
|
|
|
|
copy->supermenu = supermenu;
|
|
|
|
copy->attachedMenu = nil;
|
|
|
|
copy->autoenablesItems = autoenablesItems;
|
|
|
|
copy->menuChangedMessagesEnabled = menuChangedMessagesEnabled;
|
|
|
|
copy->menuHasChanged = menuHasChanged;
|
|
|
|
|
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id <NSMenuItem>)addItemWithTitle:(NSString*)aString
|
1998-09-08 21:50:33 +00:00
|
|
|
action:(SEL)aSelector
|
|
|
|
keyEquivalent:(NSString*)charCode
|
1997-07-07 16:56:52 +00:00
|
|
|
{
|
|
|
|
return [self insertItemWithTitle:aString
|
1998-09-08 21:50:33 +00:00
|
|
|
action:aSelector
|
|
|
|
keyEquivalent:charCode
|
|
|
|
atIndex:[[menuCells itemArray] count]];
|
1997-07-07 16:56:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id <NSMenuItem>)insertItemWithTitle:(NSString*)aString
|
1998-09-08 21:50:33 +00:00
|
|
|
action:(SEL)aSelector
|
|
|
|
keyEquivalent:(NSString*)charCode
|
|
|
|
atIndex:(unsigned int)index
|
1997-07-07 16:56:52 +00:00
|
|
|
{
|
|
|
|
id menuCell = [menuCells insertItemWithTitle:aString
|
1998-09-08 21:50:33 +00:00
|
|
|
action:aSelector
|
|
|
|
keyEquivalent:charCode
|
|
|
|
atIndex:index];
|
|
|
|
menuHasChanged = YES; // menu needs update
|
1997-07-07 16:56:52 +00:00
|
|
|
|
|
|
|
return menuCell;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)removeItem:(id <NSMenuItem>)anItem
|
|
|
|
{
|
|
|
|
[menuCells removeItem:anItem];
|
1998-09-08 21:50:33 +00:00
|
|
|
menuHasChanged = YES; // menu needs update
|
1997-07-07 16:56:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)itemArray
|
|
|
|
{
|
|
|
|
return [menuCells itemArray];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id <NSMenuItem>)itemWithTag:(int)aTag
|
|
|
|
{
|
|
|
|
return [menuCells itemWithTag:aTag];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id <NSMenuItem>)itemWithTitle:(NSString*)aString
|
|
|
|
{
|
|
|
|
return [menuCells itemWithTitle:aString];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setSubmenu:(NSMenu*)aMenu forItem:(id <NSMenuItem>)anItem
|
|
|
|
{
|
|
|
|
NSString* itemTitle = [anItem title];
|
|
|
|
|
|
|
|
[anItem setTarget:aMenu];
|
|
|
|
[anItem setAction:@selector(submenuAction:)];
|
|
|
|
if (aMenu)
|
|
|
|
aMenu->supermenu = self;
|
|
|
|
|
|
|
|
[itemTitle retain];
|
|
|
|
// [aMenu->title release];
|
|
|
|
aMenu->title = itemTitle;
|
|
|
|
|
|
|
|
[self _menuChanged];
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)submenuAction:(id)sender
|
1997-07-07 16:56:52 +00:00
|
|
|
{
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (NSMenu*)attachedMenu
|
1996-05-30 20:03:15 +00:00
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
return attachedMenu;
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isAttached
|
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
return supermenu && [supermenu attachedMenu] == self;
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isTornOff
|
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
// SUBCLASS
|
|
|
|
return NO;
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (NSPoint)locationForSubmenu:(NSMenu*)aSubmenu
|
1996-05-30 20:03:15 +00:00
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
// SUBCLASS
|
1996-05-30 20:03:15 +00:00
|
|
|
return NSZeroPoint;
|
|
|
|
}
|
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (NSMenu*)supermenu
|
|
|
|
{
|
|
|
|
return supermenu;
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (void)setAutoenablesItems:(BOOL)flag
|
1996-05-30 20:03:15 +00:00
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
autoenablesItems = flag;
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)autoenablesItems
|
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
return autoenablesItems;
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (void)update
|
|
|
|
{
|
|
|
|
// SUBCLASS to redisplay the menu
|
|
|
|
|
|
|
|
id cells;
|
|
|
|
int i, count;
|
1997-08-16 23:47:24 +00:00
|
|
|
id theApp = [NSApplication sharedApplication];
|
1997-07-07 16:56:52 +00:00
|
|
|
|
1997-08-16 23:47:24 +00:00
|
|
|
if (![[theApp mainMenu] autoenablesItems])
|
1997-07-07 16:56:52 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
cells = [menuCells itemArray];
|
|
|
|
count = [cells count];
|
|
|
|
|
|
|
|
/* Temporary disable automatic displaying of menu */
|
|
|
|
[self setMenuChangedMessagesEnabled:NO];
|
|
|
|
|
1998-12-01 10:24:19 +00:00
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
id<NSMenuItem> cell = [cells 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;
|
|
|
|
}
|
|
|
|
}
|
1997-07-07 16:56:52 +00:00
|
|
|
else
|
1998-12-01 10:24:19 +00:00
|
|
|
{
|
|
|
|
/* Search the key window's responder chain */
|
|
|
|
keyWindow = [theApp keyWindow];
|
|
|
|
responder = [keyWindow firstResponder];
|
|
|
|
while (responder)
|
|
|
|
{
|
|
|
|
if ([responder respondsToSelector: action])
|
|
|
|
{
|
|
|
|
validator = responder;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
responder = [responder nextResponder];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (validator == nil)
|
|
|
|
{
|
|
|
|
/* Search the key window */
|
|
|
|
if ([keyWindow respondsToSelector: action])
|
|
|
|
{
|
|
|
|
validator = keyWindow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (validator == nil)
|
|
|
|
{
|
|
|
|
/* Search the key window's delegate */
|
|
|
|
delegate = [keyWindow delegate];
|
|
|
|
if ([delegate respondsToSelector: action])
|
|
|
|
{
|
|
|
|
validator = delegate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (validator == nil)
|
|
|
|
{
|
|
|
|
mainWindow = [theApp mainWindow];
|
|
|
|
if (mainWindow != keyWindow)
|
|
|
|
{
|
|
|
|
/* Search the main window's responder chain */
|
|
|
|
responder = [mainWindow firstResponder];
|
|
|
|
while (responder)
|
|
|
|
{
|
|
|
|
if ([responder respondsToSelector: action])
|
|
|
|
{
|
|
|
|
validator = responder;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
responder = [responder nextResponder];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (validator == nil)
|
|
|
|
{
|
|
|
|
/* Search the main window */
|
|
|
|
if ([mainWindow respondsToSelector: action])
|
|
|
|
{
|
|
|
|
validator = mainWindow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (validator == nil)
|
|
|
|
{
|
|
|
|
/* Search the main window's delegate */
|
|
|
|
delegate = [mainWindow delegate];
|
|
|
|
if ([delegate respondsToSelector: action])
|
|
|
|
{
|
|
|
|
validator = delegate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (validator == nil)
|
|
|
|
{
|
|
|
|
/* Search the NSApplication object */
|
|
|
|
if ([theApp respondsToSelector: action])
|
|
|
|
{
|
|
|
|
validator = theApp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (validator == nil)
|
|
|
|
{
|
|
|
|
/* Search the NSApplication object's delegate */
|
|
|
|
delegate = [theApp delegate];
|
|
|
|
if ([delegate respondsToSelector: action])
|
|
|
|
{
|
|
|
|
validator = theApp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1997-07-07 16:56:52 +00:00
|
|
|
}
|
|
|
|
|
1998-12-01 10:24:19 +00:00
|
|
|
if (validator == nil)
|
|
|
|
{
|
|
|
|
shouldBeEnabled = NO;
|
|
|
|
}
|
|
|
|
else if ([validator respondsToSelector: @selector(validateMenuItem:)])
|
|
|
|
{
|
|
|
|
shouldBeEnabled = [validator validateMenuItem: cell];
|
|
|
|
}
|
1997-07-07 16:56:52 +00:00
|
|
|
else
|
1998-12-01 10:24:19 +00:00
|
|
|
{
|
|
|
|
shouldBeEnabled = YES;
|
|
|
|
}
|
1997-07-07 16:56:52 +00:00
|
|
|
|
1998-12-01 10:24:19 +00:00
|
|
|
if (shouldBeEnabled != wasEnabled)
|
|
|
|
{
|
|
|
|
[cell setEnabled: shouldBeEnabled];
|
|
|
|
[menuCells setNeedsDisplayInRect: [menuCells cellFrameAtRow: i]];
|
|
|
|
}
|
1997-07-07 16:56:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Reenable displaying of menus */
|
|
|
|
[self setMenuChangedMessagesEnabled:YES];
|
|
|
|
|
1998-12-01 10:24:19 +00:00
|
|
|
if (menuHasChanged)
|
|
|
|
[self sizeToFit];
|
|
|
|
|
|
|
|
/* FIXME - only doing this here 'cos auto-display doesn't work */
|
|
|
|
if ([menuCells needsDisplay])
|
|
|
|
[menuCells display];
|
1997-07-07 16:56:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)performActionForItem:(id <NSMenuItem>)cell
|
1996-09-12 19:24:32 +00:00
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
SEL action;
|
|
|
|
id target;
|
|
|
|
NSWindow* keyWindow;
|
|
|
|
NSWindow* mainWindow;
|
|
|
|
id responder;
|
|
|
|
id delegate;
|
1997-08-16 23:47:24 +00:00
|
|
|
id theApp = [NSApplication sharedApplication];
|
1997-07-07 16:56:52 +00:00
|
|
|
|
|
|
|
if (![cell isEnabled])
|
|
|
|
return;
|
|
|
|
|
|
|
|
action = [cell action];
|
|
|
|
|
|
|
|
/* Search the target */
|
|
|
|
if ((target = [cell target]) && [target respondsToSelector:action]) {
|
1997-08-27 21:20:19 +00:00
|
|
|
[target performSelector:action withObject:cell];
|
1997-07-07 16:56:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Search the key window's responder chain */
|
1997-08-16 23:47:24 +00:00
|
|
|
keyWindow = [theApp keyWindow];
|
1997-07-07 16:56:52 +00:00
|
|
|
responder = [keyWindow firstResponder];
|
|
|
|
while (responder) {
|
|
|
|
if ([responder respondsToSelector:action]) {
|
1997-08-27 21:20:19 +00:00
|
|
|
[responder performSelector:action withObject:cell];
|
1997-07-07 16:56:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
responder = [responder nextResponder];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Search the key window */
|
|
|
|
if ([keyWindow respondsToSelector:action]) {
|
1997-08-27 21:20:19 +00:00
|
|
|
[keyWindow performSelector:action withObject:cell];
|
1997-07-07 16:56:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Search the key window's delegate */
|
|
|
|
delegate = [keyWindow delegate];
|
|
|
|
if ([delegate respondsToSelector:action]) {
|
1997-08-27 21:20:19 +00:00
|
|
|
[delegate performSelector:action withObject:cell];
|
1997-07-07 16:56:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
1997-08-16 23:47:24 +00:00
|
|
|
mainWindow = [theApp mainWindow];
|
1997-07-07 16:56:52 +00:00
|
|
|
if (mainWindow != keyWindow) {
|
|
|
|
/* Search the main window's responder chain */
|
|
|
|
responder = [mainWindow firstResponder];
|
|
|
|
while (responder) {
|
|
|
|
if ([responder respondsToSelector:action]) {
|
1997-08-27 21:20:19 +00:00
|
|
|
[responder performSelector:action withObject:cell];
|
1997-07-07 16:56:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
responder = [responder nextResponder];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Search the main window */
|
|
|
|
if ([mainWindow respondsToSelector:action]) {
|
1997-08-27 21:20:19 +00:00
|
|
|
[mainWindow performSelector:action withObject:cell];
|
1997-07-07 16:56:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Search the main window's delegate */
|
|
|
|
delegate = [mainWindow delegate];
|
|
|
|
if ([delegate respondsToSelector:action]) {
|
1997-08-27 21:20:19 +00:00
|
|
|
[delegate performSelector:action withObject:cell];
|
1997-07-07 16:56:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Search the NSApplication object */
|
1997-08-16 23:47:24 +00:00
|
|
|
if ([theApp respondsToSelector:action]) {
|
1997-08-27 21:20:19 +00:00
|
|
|
[theApp performSelector:action withObject:cell];
|
1997-07-07 16:56:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Search the NSApplication object's delegate */
|
1997-08-16 23:47:24 +00:00
|
|
|
delegate = [theApp delegate];
|
1997-07-07 16:56:52 +00:00
|
|
|
if ([delegate respondsToSelector:action]) {
|
1997-08-27 21:20:19 +00:00
|
|
|
[delegate performSelector:action withObject:cell];
|
1997-07-07 16:56:52 +00:00
|
|
|
return;
|
|
|
|
}
|
1996-09-12 19:24:32 +00:00
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (BOOL)performKeyEquivalent:(NSEvent*)theEvent
|
1996-05-30 20:03:15 +00:00
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
id cells = [menuCells itemArray];
|
|
|
|
int i, count = [cells count];
|
|
|
|
NSEventType type = [theEvent type];
|
1996-12-05 13:07:59 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
if (type != NSKeyDown || type != NSKeyUp)
|
|
|
|
return NO;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
id<NSMenuItem> cell = [cells 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]]
|
|
|
|
&& [cell keyEquivalentModifierMask] == [theEvent modifierFlags]) {
|
|
|
|
[menuCells lockFocus];
|
|
|
|
[(id)cell performClick:self];
|
|
|
|
[menuCells unlockFocus];
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NO;
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (void)setMenuChangedMessagesEnabled:(BOOL)flag
|
1996-05-30 20:03:15 +00:00
|
|
|
{
|
1997-07-07 16:56:52 +00:00
|
|
|
menuChangedMessagesEnabled = flag;
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (BOOL)menuChangedMessagesEnabled
|
|
|
|
{
|
|
|
|
return menuChangedMessagesEnabled;
|
|
|
|
}
|
1996-12-05 13:07:59 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (void)sizeToFit
|
|
|
|
{
|
|
|
|
// SUBCLASS
|
1997-12-04 01:58:57 +00:00
|
|
|
[menuCells _resizeMenuForCellSize];
|
|
|
|
[menuCells setNeedsDisplay:YES];
|
1997-07-07 16:56:52 +00:00
|
|
|
menuHasChanged = NO;
|
1996-05-30 20:03:15 +00:00
|
|
|
}
|
|
|
|
|
1997-12-04 01:58:57 +00:00
|
|
|
- (void)setTitle:(NSString*)aTitle
|
|
|
|
{
|
|
|
|
ASSIGN(title, aTitle);
|
|
|
|
[self sizeToFit];
|
|
|
|
}
|
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (NSString*)title
|
|
|
|
{
|
|
|
|
return title;
|
|
|
|
}
|
1996-09-12 19:24:32 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (NSMenuMatrix*)menuCells
|
|
|
|
{
|
|
|
|
return menuCells;
|
|
|
|
}
|
|
|
|
|
|
|
|
- initWithCoder:(NSCoder*)aDecoder
|
|
|
|
{
|
|
|
|
return self;
|
|
|
|
}
|
1996-09-12 19:24:32 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (void)encodeWithCoder:(NSCoder*)aCoder
|
1996-09-12 19:24:32 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
1998-11-12 10:49:00 +00:00
|
|
|
// non OS spec methods
|
|
|
|
- (void)_rightMouseDisplay
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
@end /* NSMenu */
|
|
|
|
|
|
|
|
|
|
|
|
@implementation NSMenu (PrivateMethods2)
|
1998-11-12 10:49:00 +00:00
|
|
|
|
1997-07-07 16:56:52 +00:00
|
|
|
- (void)_menuChanged
|
|
|
|
{
|
|
|
|
menuHasChanged = YES;
|
|
|
|
if (menuChangedMessagesEnabled)
|
|
|
|
[self sizeToFit];
|
|
|
|
}
|
1998-11-12 10:49:00 +00:00
|
|
|
|
1996-09-12 19:24:32 +00:00
|
|
|
@end
|