/**
NSMenuView
Copyright (C) 1999 Free Software Foundation, Inc.
Author: Fred Kiefer
Date: Sep 2001
Author: David Lazaro Saz
Date: Oct 1999
Author: Michael Hanni
Date: 1999
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
@implementation NSMenuView
static NSRect
_addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
{
if (isHorizontal == NO)
{
aRect.origin.x--;
aRect.size.width++;
}
else
{
aRect.origin.y--;
aRect.size.height++;
}
return aRect;
}
/*
* Class methods.
*/
+ (float) menuBarHeight
{
static float height = 0.0;
if (height == 0.0)
{
NSFont *font = [NSFont menuFontOfSize: 0.0];
/* Should make up 23 for the default font */
height = ([font boundingRectForFont].size.height) + 8;
}
return height;
}
/*
* NSView overrides
*/
- (BOOL) acceptsFirstMouse: (NSEvent*)theEvent
{
return YES;
}
/*
* Init methods.
*/
- (id) initWithFrame: (NSRect)aFrame
{
NSRect r;
self = [super initWithFrame: aFrame];
_font = RETAIN([NSFont menuFontOfSize: 0.0]);
r = [_font boundingRectForFont];
/* Should make up 110, 20 for default font */
_cellSize = NSMakeSize (r.size.width * 10., r.size.height + 5.);
_highlightedItemIndex = -1;
_horizontalEdgePad = 4.;
/* Set the necessary offset for the menuView. That is, how many pixels
* do we need for our left side border line. For regular menus this
* equals 1, for popups it is 0.
*/
_leftBorderOffset = 1;
// Create an array to store our menu item cells.
_itemCells = [NSMutableArray new];
return self;
}
- (id)initAsTearOff
{
//FIXME
return [self init];
}
- (void) dealloc
{
// We must remove the menu view from the menu list of observers.
if ( _menu )
{
[[NSNotificationCenter defaultCenter] removeObserver: self
name: nil
object: _menu];
}
RELEASE(_font);
/* Clean the pointer to us stored into the _itemCells. */
[_itemCells makeObjectsPerformSelector: @selector(setMenuView:)
withObject: nil];
RELEASE(_itemCells);
[super dealloc];
}
/*
* Getting and Setting Menu View Attributes
*/
- (void) setMenu: (NSMenu*)menu
{
NSNotificationCenter *theCenter = [NSNotificationCenter defaultCenter];
if (_menu != nil)
{
// Remove this menu view from the old menu list of observers.
[theCenter removeObserver: self name: nil object: _menu];
}
/* menu is retaining us, so we should not be retaining menu. */
_menu = menu;
_items_link = [_menu itemArray];
// Add this menu view to the menu's list of observers.
[theCenter addObserver: self
selector: @selector(itemChanged:)
name: NSMenuDidChangeItemNotification
object: _menu];
[theCenter addObserver: self
selector: @selector(itemAdded:)
name: NSMenuDidAddItemNotification
object: _menu];
[theCenter addObserver: self
selector: @selector(itemRemoved:)
name: NSMenuDidRemoveItemNotification
object: _menu];
// Force menu view's layout to be recalculated.
[self setNeedsSizing: YES];
}
- (NSMenu*) menu
{
return _menu;
}
- (void) setHorizontal: (BOOL)flag
{
_horizontal = flag;
}
- (BOOL) isHorizontal
{
return _horizontal;
}
- (void) setFont: (NSFont*)font
{
ASSIGN(_font, font);
}
- (NSFont*) font
{
return _font;
}
- (void) setHighlightedItemIndex: (int)index
{
NSMenuItemCell *aCell;
if (index == _highlightedItemIndex)
return;
// Unhighlight old
if (_highlightedItemIndex != -1)
{
aCell = [_itemCells objectAtIndex: _highlightedItemIndex];
[aCell setHighlighted: NO];
[self setNeedsDisplayForItemAtIndex: _highlightedItemIndex];
}
// Set ivar to new index.
_highlightedItemIndex = index;
// Highlight new
if (_highlightedItemIndex != -1)
{
aCell = [_itemCells objectAtIndex: _highlightedItemIndex];
[aCell setHighlighted: YES];
[self setNeedsDisplayForItemAtIndex: _highlightedItemIndex];
}
}
- (int) highlightedItemIndex
{
return _highlightedItemIndex;
}
- (void) setMenuItemCell: (NSMenuItemCell *)cell
forItemAtIndex: (int)index
{
NSMenuItem *anItem = [_items_link objectAtIndex: index];
[_itemCells replaceObjectAtIndex: index withObject: cell];
[cell setMenuItem: anItem];
[cell setMenuView: self];
if ([self highlightedItemIndex] == index)
[cell setHighlighted: YES];
else
[cell setHighlighted: NO];
// Mark the new cell and the menu view as needing resizing.
[cell setNeedsSizing: YES];
[self setNeedsSizing: YES];
}
- (NSMenuItemCell*) menuItemCellForItemAtIndex: (int)index
{
return [_itemCells objectAtIndex: index];
}
- (NSMenuView*) attachedMenuView
{
NSMenu *attachedMenu;
if ((attachedMenu = [_menu attachedMenu]))
return [attachedMenu menuRepresentation];
else
return nil;
}
- (NSMenu*) attachedMenu
{
return [_menu attachedMenu];
}
- (BOOL) isAttached
{
return [_menu isAttached];
}
- (BOOL) isTornOff
{
return [_menu isTornOff];
}
- (void) setHorizontalEdgePadding: (float)pad
{
_horizontalEdgePad = pad;
}
- (float) horizontalEdgePadding
{
return _horizontalEdgePad;
}
/*
* Notification Methods
*/
- (void) itemChanged: (NSNotification*)notification
{
int index = [[[notification userInfo] objectForKey: @"NSMenuItemIndex"]
intValue];
// Mark the cell associated with the item as needing resizing.
[[_itemCells objectAtIndex: index] setNeedsSizing: YES];
// Mark the menu view as needing to be resized.
[self setNeedsSizing: YES];
}
- (void) itemAdded: (NSNotification*)notification
{
int index = [[[notification userInfo]
objectForKey: @"NSMenuItemIndex"] intValue];
NSMenuItem *anItem = [_items_link objectAtIndex: index];
id aCell = [NSMenuItemCell new];
int wasHighlighted = _highlightedItemIndex;
[aCell setMenuItem: anItem];
[aCell setMenuView: self];
[aCell setFont: _font];
/* Unlight the previous highlighted cell if the index of the highlighted
* cell will be ruined up by the insertion of the new cell. */
if (wasHighlighted >= index)
{
[self setHighlightedItemIndex: -1];
}
[_itemCells insertObject: aCell atIndex: index];
/* Restore the highlighted cell, with the new index for it. */
if (wasHighlighted >= index)
{
/* Please note that if wasHighlighted == -1, it shouldn't be possible
* to be here. */
[self setHighlightedItemIndex: ++wasHighlighted];
}
[aCell setNeedsSizing: YES];
RELEASE(aCell);
// Mark the menu view as needing to be resized.
[self setNeedsSizing: YES];
}
- (void) itemRemoved: (NSNotification*)notification
{
int wasHighlighted = [self highlightedItemIndex];
int index = [[[notification userInfo] objectForKey: @"NSMenuItemIndex"]
intValue];
if (index <= wasHighlighted)
{
[self setHighlightedItemIndex: -1];
}
[_itemCells removeObjectAtIndex: index];
if (wasHighlighted > index)
{
[self setHighlightedItemIndex: --wasHighlighted];
}
// Mark the menu view as needing to be resized.
[self setNeedsSizing: YES];
}
/*
* Working with Submenus.
*/
- (void) detachSubmenu
{
NSMenu *attachedMenu = [_menu attachedMenu];
NSMenuView *attachedMenuView;
if (!attachedMenu)
return;
attachedMenuView = [attachedMenu menuRepresentation];
[attachedMenuView detachSubmenu];
[attachedMenuView setHighlightedItemIndex: -1];
if ([attachedMenu isFollowTransient])
{
[attachedMenu closeTransient];
[attachedMenuView setHighlightedItemIndex: _oldHighlightedItemIndex];
}
else
[attachedMenu close];
}
- (void) attachSubmenuForItemAtIndex: (int)index
{
/*
* Transient menus are used for torn-off menus, which are already on the
* screen and for sons of transient menus. As transients disappear as
* soon as we release the mouse the user will be able to leave submenus
* open on the screen and interact with other menus at the same time.
*/
NSMenu *attachableMenu = [[_items_link objectAtIndex: index] submenu];
if ([attachableMenu isTornOff] || [_menu isFollowTransient])
{
_oldHighlightedItemIndex = [[attachableMenu menuRepresentation]
highlightedItemIndex];
[attachableMenu displayTransient];
[[attachableMenu menuRepresentation] setHighlightedItemIndex: -1];
}
else
[attachableMenu display];
}
/*
* Calculating Menu Geometry
*/
- (void) update
{
[_menu update];
if (_needsSizing)
[self sizeToFit];
}
- (void) setNeedsSizing: (BOOL)flag
{
_needsSizing = flag;
}
- (BOOL) needsSizing
{
return _needsSizing;
}
- (void) sizeToFit
{
unsigned i;
unsigned howMany = [_itemCells count];
float neededImageAndTitleWidth = [_font widthOfString:
[_menu title]] + 17;
float neededKeyEquivalentWidth = 0.0;
float neededStateImageWidth = 0.0;
float accumulatedOffset = 0.0;
/* Set the necessary offset for the menuView. That is, how many pixels
* do we need for our left side border line. For regular menus this
* equals 1, for popups it is 0.
*
* Why here? I could not think of a better place. I figure that everyone
* should sizeToFit their popup/menu before using it so we should get
* this set properly fairly early.
*/
if ([_menu _ownedByPopUp])
_leftBorderOffset = 0;
// TODO: Optimize this loop.
for (i = 0; i < howMany; i++)
{
float anImageAndTitleWidth;
float anImageWidth;
float aKeyEquivalentWidth;
float aStateImageWidth;
float aTitleWidth;
NSMenuItemCell *aCell = [_itemCells objectAtIndex: i];
// State image area.
aStateImageWidth = [aCell stateImageWidth];
if (aStateImageWidth > neededStateImageWidth)
neededStateImageWidth = aStateImageWidth;
// Image and title area.
aTitleWidth = [aCell titleWidth];
anImageWidth = [aCell imageWidth];
switch ([aCell imagePosition])
{
case NSNoImage:
anImageAndTitleWidth = aTitleWidth;
break;
case NSImageOnly:
anImageAndTitleWidth = anImageWidth;
break;
case NSImageLeft:
case NSImageRight:
anImageAndTitleWidth = anImageWidth + aTitleWidth + xDist;
break;
case NSImageBelow:
case NSImageAbove:
case NSImageOverlaps:
default:
if (aTitleWidth > anImageWidth)
anImageAndTitleWidth = aTitleWidth;
else
anImageAndTitleWidth = anImageWidth;
break;
}
anImageAndTitleWidth += aStateImageWidth;
if (anImageAndTitleWidth > neededImageAndTitleWidth)
neededImageAndTitleWidth = anImageAndTitleWidth;
// Key equivalent area.
aKeyEquivalentWidth = [aCell keyEquivalentWidth];
if (aKeyEquivalentWidth > neededKeyEquivalentWidth)
neededKeyEquivalentWidth = aKeyEquivalentWidth;
}
// Cache the needed widths.
_stateImageWidth = neededStateImageWidth;
_imageAndTitleWidth = neededImageAndTitleWidth;
_keyEqWidth = neededKeyEquivalentWidth;
// Calculate the offsets and cache them.
_stateImageOffset = _imageAndTitleOffset = accumulatedOffset
= _horizontalEdgePad;
accumulatedOffset += 2 * _horizontalEdgePad + neededImageAndTitleWidth;
_keyEqOffset = accumulatedOffset += _horizontalEdgePad;
accumulatedOffset += neededKeyEquivalentWidth + _horizontalEdgePad;
// Calculate frame size.
if (![_menu _ownedByPopUp])
{
_cellSize.width = accumulatedOffset + 3; // Add the border width
}
if (_horizontal == NO)
{
[self setFrameSize: NSMakeSize(_cellSize.width + _leftBorderOffset,
(howMany * _cellSize.height))];
}
else
{
[self setFrameSize: NSMakeSize((howMany * _cellSize.width),
_cellSize.height + _leftBorderOffset)];
}
_needsSizing = NO;
}
- (float) stateImageOffset
{
if (_needsSizing)
[self sizeToFit];
return _stateImageOffset;
}
- (float) stateImageWidth
{
if (_needsSizing)
[self sizeToFit];
return _stateImageWidth;
}
- (float) imageAndTitleOffset
{
if (_needsSizing)
[self sizeToFit];
return _imageAndTitleOffset;
}
- (float) imageAndTitleWidth
{
if (_needsSizing)
[self sizeToFit];
return _imageAndTitleWidth;
}
- (float) keyEquivalentOffset
{
if (_needsSizing)
[self sizeToFit];
return _keyEqOffset;
}
- (float) keyEquivalentWidth
{
if (_needsSizing)
[self sizeToFit];
return _keyEqWidth;
}
- (NSRect) innerRect
{
if (_horizontal == NO)
{
return NSMakeRect(_bounds.origin.x + _leftBorderOffset, _bounds.origin.y,
_bounds.size.width - _leftBorderOffset, _bounds.size.height);
}
else
{
return NSMakeRect(_bounds.origin.x, _bounds.origin.y + _leftBorderOffset,
_bounds.size.width, _bounds.size.height - _leftBorderOffset);
}
}
- (NSRect) rectOfItemAtIndex: (int)index
{
NSRect theRect;
if (_needsSizing == YES)
{
[self sizeToFit];
}
/* When we are a normal menu we fiddle with the origin so that the item
* rect is shifted 1 pixel over so we do not draw on the heavy line at
* origin.x = 0. However, for popups we don't want this modification of
* our rect so our _leftBorderOffset = 0 (set in sizeToFit).
*/
if (_horizontal == NO)
{
theRect.origin.y = _bounds.size.height - (_cellSize.height * (index + 1));
theRect.origin.x = _leftBorderOffset;
}
else
{
theRect.origin.x = _cellSize.width * index;
theRect.origin.y = _leftBorderOffset;
}
theRect.size = _cellSize;
/* NOTE: This returns the correct NSRect for drawing cells, but nothing
* else (unless we are a popup). This rect will have to be modified for
* event calculation, etc..
*/
return theRect;
}
- (int) indexOfItemAtPoint: (NSPoint)point
{
unsigned howMany = [_itemCells count];
int i;
for (i = 0; i < howMany; i++)
{
NSRect aRect = [self rectOfItemAtIndex: i];
/* We need to modify the rect to take into account the modifications
* to origin made by [-rectOfItemAtIndex:] in order to return an
* item clicked at the left hand margin. However, for a popup this
* calculation is unnecessary since we have no extra margin.
*/
if (![_menu _ownedByPopUp])
aRect = _addLeftBorderOffsetToRect(aRect, _horizontal);
if (NSMouseInRect(point, aRect, NO))
return i;
}
return -1;
}
- (void) setNeedsDisplayForItemAtIndex: (int)index
{
NSRect aRect;
aRect = [self rectOfItemAtIndex: index];
/* We need to modify the rect to take into account the modifications
* to origin made by [-rectOfItemAtIndex:] in order to return an
* item clicked at the left hand margin. However, for a popup this
* calculation is unnecessary since we have no extra margin.
*/
if (![_menu _ownedByPopUp])
aRect = _addLeftBorderOffsetToRect(aRect, _horizontal);
[self setNeedsDisplayInRect: aRect];
}
/**
Returns the correct frame origin for aSubmenu based on the location
of the receiver. This location may depend on the current NSInterfaceStyle.
*/
- (NSPoint) locationForSubmenu: (NSMenu *)aSubmenu
{
NSRect frame = [_window frame];
NSRect submenuFrame;
if (_needsSizing)
[self sizeToFit];
if (aSubmenu)
submenuFrame = [[[aSubmenu menuRepresentation] window] frame];
else
submenuFrame = NSZeroRect;
if (_horizontal == NO)
{
if (NSInterfaceStyleForKey(@"NSMenuInterfaceStyle", nil)
== GSWindowMakerInterfaceStyle)
{
NSRect aRect = [self rectOfItemAtIndex:
[_menu indexOfItemWithSubmenu: aSubmenu]];
NSPoint subOrigin = [_window convertBaseToScreen:
NSMakePoint(aRect.origin.x,
aRect.origin.y)];
return NSMakePoint (NSMaxX(frame),
subOrigin.y - NSHeight(submenuFrame) - 3 +
2*[NSMenuView menuBarHeight]);
}
else
{
return NSMakePoint(NSMaxX(frame),
NSMaxY(frame) - NSHeight(submenuFrame));
}
}
else
{
NSRect aRect = [self rectOfItemAtIndex:
[_menu indexOfItemWithSubmenu: aSubmenu]];
NSPoint subOrigin = [_window convertBaseToScreen:
NSMakePoint(NSMinX(aRect),
NSMaxY(aRect))];
return NSMakePoint(subOrigin.x,
subOrigin.y - NSHeight(submenuFrame));
}
}
- (void) resizeWindowWithMaxHeight: (float)maxHeight
{
// set the menuview's window to max height in order to keep on screen?
}
- (void) setWindowFrameForAttachingToRect: (NSRect)screenRect
onScreen: (NSScreen*)screen
preferredEdge: (NSRectEdge)edge
popUpSelectedItem: (int)selectedItemIndex
{
NSRect r;
NSRect cellFrame;
NSRect screenFrame;
int items = [_itemCells count];
// Convert the screen rect to our view
cellFrame.size = screenRect.size;
cellFrame.origin = [_window convertScreenToBase: screenRect.origin];
cellFrame = [self convertRect: cellFrame fromView: nil];
_cellSize = cellFrame.size;
[self sizeToFit];
/*
* Compute the frame
*/
screenFrame = screenRect;
if (items > 1)
{
float f;
if (_horizontal == NO)
{
f = screenRect.size.height * (items - 1);
screenFrame.size.height += f;
screenFrame.origin.y -= f;
}
else
{
f = screenRect.size.width * (items - 1);
screenFrame.size.width += f;
}
}
// Move the menu window to screen?
// TODO
// Compute position for popups, if needed
if (selectedItemIndex > -1)
{
if (_horizontal == NO)
{
screenFrame.origin.y += screenRect.size.height * selectedItemIndex;
}
else
{
screenFrame.origin.x -= screenRect.size.width * selectedItemIndex;
}
}
// Get the frameRect
r = [NSWindow frameRectForContentRect: screenFrame
styleMask: [_window styleMask]];
// Update position,if needed, using the preferredEdge;
// TODO
// Set the window frame
[_window setFrame: r display: NO];
}
/*
* Drawing.
*/
- (void) drawRect: (NSRect)rect
{
int i;
int howMany = [_itemCells count];
/* For popupButtons we do not want this dark line. */
if (![_menu _ownedByPopUp])
{
NSGraphicsContext *ctxt = GSCurrentContext();
// Draw a dark gray line at the left of the menu item cells.
DPSgsave(ctxt);
DPSsetlinewidth(ctxt, 1);
DPSsetgray(ctxt, NSDarkGray);
if (_horizontal == NO)
{
DPSmoveto(ctxt, NSMinX(_bounds), NSMinY(_bounds));
DPSrlineto(ctxt, 0, _bounds.size.height);
}
else
{
DPSmoveto(ctxt, NSMinX(_bounds), NSMaxY(_bounds));
DPSrlineto(ctxt, _bounds.size.width, 0);
}
DPSstroke(ctxt);
DPSgrestore(ctxt);
}
// Draw the menu cells.
for (i = 0; i < howMany; i++)
{
NSRect aRect;
NSMenuItemCell *aCell;
aRect = [self rectOfItemAtIndex: i];
if (NSIntersectsRect(rect, aRect) == YES)
{
aCell = [_itemCells objectAtIndex: i];
[aCell drawWithFrame: aRect inView: self];
}
}
}
/*
* Event Handling
*/
- (void) performActionWithHighlightingForItemAtIndex: (int)index
{
NSMenu *candidateMenu = _menu;
NSMenuView *targetMenuView;
int indexToHighlight = index;
for (;;)
{
NSMenu *superMenu = [candidateMenu supermenu];
if (superMenu == nil
|| [candidateMenu isAttached]
|| [candidateMenu isTornOff])
{
targetMenuView = [candidateMenu menuRepresentation];
break;
}
else
{
indexToHighlight = [superMenu indexOfItemWithSubmenu: candidateMenu];
candidateMenu = superMenu;
}
}
if ([targetMenuView attachedMenu])
[targetMenuView detachSubmenu];
[targetMenuView setHighlightedItemIndex: indexToHighlight];
/* We need to let the run loop run a little so that the fact that
* the item is highlighted gets displayed on screen.
*/
[[NSRunLoop currentRunLoop]
runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]];
[_menu performActionForItemAtIndex: index];
if (![_menu _ownedByPopUp])
{
[targetMenuView setHighlightedItemIndex: -1];
}
}
#define MOVE_THRESHOLD_DELTA 2.0
#define DELAY_MULTIPLIER 10
- (BOOL) trackWithEvent: (NSEvent*)event
{
unsigned eventMask = NSPeriodicMask;
NSDate *theDistantFuture = [NSDate distantFuture];
int index;
NSPoint location;
NSPoint lastLocation = {0,0};
NSMenu *alreadyAttachedMenu = NO;
BOOL mouseMoved = NO;
BOOL delayedSelect = NO;
int delayCount = 0;
float xDelta = MOVE_THRESHOLD_DELTA;
NSEvent *original;
NSEventType type = [event type];
NSEventType end;
/*
* The original event is unused except to determine whether the method
* was invoked in response to a right or left mouse down.
* We pass the same event on when we want tracking to move into a
* submenu.
*/
original = AUTORELEASE(RETAIN(event));
if (type == NSRightMouseDown)
{
end = NSRightMouseUp;
eventMask |= NSRightMouseUpMask | NSRightMouseDraggedMask;
}
else if (type == NSOtherMouseDown)
{
end = NSOtherMouseUp;
eventMask |= NSOtherMouseUpMask | NSOtherMouseDraggedMask;
}
else
{
end = NSLeftMouseUp;
eventMask |= NSLeftMouseUpMask | NSLeftMouseDraggedMask;
}
do
{
location = [_window mouseLocationOutsideOfEventStream];
index = [self indexOfItemAtPoint: location];
if (index != _highlightedItemIndex)
{
mouseMoved = YES; /* Ok - had an initial movement. */
}
if (type == NSPeriodic)
{
if ([_menu isPartlyOffScreen])
{
NSPoint pointerLoc = [_window convertBaseToScreen: location];
// TODO: Why 1 in the Y axis?
if (pointerLoc.x == 0 || pointerLoc.y == 1
|| pointerLoc.x == [[_window screen] frame].size.width - 1)
[_menu shiftOnScreen];
}
if (delayedSelect && mouseMoved && [event type] == NSPeriodic)
{
float xDiff = location.x - lastLocation.x;
if (xDiff > xDelta)
{
delayCount++;
if (delayCount >= DELAY_MULTIPLIER)
delayedSelect = NO;
}
else
{
delayedSelect = NO;
}
lastLocation = location;
}
}
if (index == -1)
{
NSWindow *w;
location = [_window convertBaseToScreen: location];
/*
* If the mouse is back in the supermenu, we return NO so that
* our caller knows the button was not released.
*/
w = [[_menu supermenu] window];
if (w != nil && NSMouseInRect(location, [w frame], NO) == YES)
{
return NO;
}
/*
* if the mouse is in our attached menu - get that menu to track it.
*/
w = [[_menu attachedMenu] window];
if (w != nil && NSMouseInRect(location, [w frame], NO) == YES)
{
if ([[self attachedMenuView] trackWithEvent: original])
return YES;
}
else
{
if (index != _highlightedItemIndex)
[self setHighlightedItemIndex: index];
}
#if 0
if (([_menu supermenu] && ![_menu isTornOff])
|| [_menu isFollowTransient])
return NO;
#endif
}
else
{
if (index != _highlightedItemIndex)
{
if (![_menu attachedMenu] || !delayedSelect)
{
[self setHighlightedItemIndex: index];
if ([_menu attachedMenu])
[self detachSubmenu];
if ((alreadyAttachedMenu =
[[_items_link objectAtIndex: index] submenu]))
{
[self attachSubmenuForItemAtIndex: index];
mouseMoved = NO;
delayedSelect = YES;
delayCount = 0;
}
else
{
delayedSelect = NO;
}
}
}
}
event = [NSApp nextEventMatchingMask: eventMask
untilDate: theDistantFuture
inMode: NSEventTrackingRunLoopMode
dequeue: YES];
type = [event type];
}
while (type != end);
// Perform actions as needed.
if (index != -1 && !alreadyAttachedMenu)
{
// Stop the periodic events before performing the action
[NSEvent stopPeriodicEvents];
[_menu performActionForItemAtIndex: index];
if (![_menu isFollowTransient] && ![_menu _ownedByPopUp])
[self setHighlightedItemIndex: -1];
}
// Close menus if needed.
if (!_keepAttachedMenus || index == -1
|| (alreadyAttachedMenu && [alreadyAttachedMenu isFollowTransient]))
{
NSMenu *parentMenu;
NSMenu *masterMenu;
for (parentMenu = masterMenu = _menu;
(parentMenu = [masterMenu supermenu])
&& (![masterMenu isTornOff] || [masterMenu isFollowTransient]);
masterMenu = parentMenu);
if ([masterMenu attachedMenu])
{
NSMenuView *masterMenuView = [masterMenu menuRepresentation];
[masterMenuView detachSubmenu];
[masterMenuView setHighlightedItemIndex: -1];
}
}
return YES;
}
- (void) mouseDown: (NSEvent*)theEvent
{
NSRect currentFrame;
NSRect originalFrame;
NSPoint currentTopLeft;
NSPoint originalTopLeft;
_keepAttachedMenus = YES;
originalFrame = [_window frame];
originalTopLeft = originalFrame.origin;
originalTopLeft.y += originalFrame.size.height;
[NSEvent startPeriodicEventsAfterDelay: 0.1 withPeriod: 0.05];
[self trackWithEvent: theEvent];
[NSEvent stopPeriodicEvents];
currentFrame = [_window frame];
currentTopLeft = currentFrame.origin;
currentTopLeft.y += currentFrame.size.height;
if (NSEqualPoints(currentTopLeft, originalTopLeft) == NO)
{
NSPoint origin = currentFrame.origin;
origin.x += (originalTopLeft.x - currentTopLeft.x);
origin.y += (originalTopLeft.y - currentTopLeft.y);
[_menu nestedSetFrameOrigin: origin];
[_menu nestedCheckOffScreen];
}
_keepAttachedMenus = NO;
}
- (BOOL) performKeyEquivalent: (NSEvent *)theEvent
{
return [_menu performKeyEquivalent: theEvent];
}
/*
* NSCoding Protocol
*
* Normally unused because NSMenu does not encode its NSMenuView since
* NSMenuView is considered a platform specific way of rendering the menu.
*/
- (void) encodeWithCoder: (NSCoder*)encoder
{
[super encodeWithCoder: encoder];
[encoder encodeObject: _itemCells];
[encoder encodeObject: _font];
[encoder encodeValueOfObjCType: @encode(BOOL) at: &_horizontal];
[encoder encodeValueOfObjCType: @encode(float) at: &_horizontalEdgePad];
[encoder encodeValueOfObjCType: @encode(NSSize) at: &_cellSize];
}
- (id) initWithCoder: (NSCoder*)decoder
{
self = [super initWithCoder: decoder];
[decoder decodeValueOfObjCType: @encode(id) at: &_itemCells];
[_itemCells makeObjectsPerformSelector: @selector(setMenuView:)
withObject: self];
[decoder decodeValueOfObjCType: @encode(id) at: &_font];
[decoder decodeValueOfObjCType: @encode(BOOL) at: &_horizontal];
[decoder decodeValueOfObjCType: @encode(float) at: &_horizontalEdgePad];
[decoder decodeValueOfObjCType: @encode(NSSize) at: &_cellSize];
_highlightedItemIndex = -1;
_needsSizing = YES;
return self;
}
@end
@implementation NSMenuView (GNUstepPrivate)
- (NSArray *)_itemCells
{
return _itemCells;
}
@end