/* NSMenuView.m Copyright (C) 1999 Free Software Foundation, Inc. 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 static float GSMenuBarHeight = 25.0; // a guess. // These private methods are used in NSPopUpButton. For NSPB we need to be // able to init with a frame, but have a very custom cell size. @implementation NSMenuView // Class methods. + (float)menuBarHeight { return GSMenuBarHeight; } - (BOOL)acceptsFirstMouse: (NSEvent *)theEvent { return YES; } // Init methods. - (id)init { return [self initWithFrame: NSZeroRect]; } - (id)initWithFrame: (NSRect)aFrame { cellSize = NSMakeSize(110,20); menuv_highlightedItemIndex = -1; return [super initWithFrame: aFrame]; } - (id)initWithFrame: (NSRect)aFrame cellSize: (NSSize)aSize { [self initWithFrame:aFrame]; cellSize = aSize; return self; } // Our menu. - (void)setMenu: (NSMenu *)menu { ASSIGN(menuv_menu, menu); menuv_items_link = [menuv_menu itemArray]; } - (NSMenu *)menu { return menuv_menu; } // Or random items. - (void) setPopUpButton: (NSPopUpButton *)popb; { ASSIGN(menuv_popb, popb); menuv_items_link = [menuv_popb itemArray]; } - (NSPopUpButton *)popupButton { return menuv_popb; } - (void)setHorizontal: (BOOL)flag { menuv_horizontal = flag; } - (BOOL)isHorizontal { return menuv_horizontal; } - (void)setFont: (NSFont *)font { ASSIGN(menuv_font, font); } - (NSFont *)font { return menuv_font; } /* * - (void)setHighlightedItemIndex: (int)index * * MacOS-X defines this function as the central way of switching to a new * highlighted item. The index value is == to the item you want * highlighted. When used this method unhighlights the last item (if * applicable) and selects the new item. If index == -1 highlighting is * turned off. * * NOTES (Michael Hanni): * * I modified this method for GNUstep to take submenus into account. This * way we get maximum performance while still using a method outside the * loop. * */ - (void)setHighlightedItemIndex: (int)index { id anItem; BOOL _closeASubmenu = NO; [self lockFocus]; if (index == -1) { if (menuv_highlightedItemIndex != -1) { anItem = [menuv_items_link objectAtIndex: menuv_highlightedItemIndex]; [anItem highlight: NO withFrame: [self rectOfItemAtIndex: menuv_highlightedItemIndex] inView: self]; if ([anItem hasSubmenu] && ![[anItem target] isTornOff]) [[anItem target] close]; [anItem setState: 0]; menuv_highlightedItemIndex = -1; } } else if (index >= 0) { if ( menuv_highlightedItemIndex != -1 ) { anItem = [menuv_items_link objectAtIndex: menuv_highlightedItemIndex]; [anItem highlight: NO withFrame: [self rectOfItemAtIndex: menuv_highlightedItemIndex] inView: self]; if ([anItem hasSubmenu] && ![[anItem target] isTornOff]) [[anItem target] close]; [anItem setState: 0]; } if (menuv_highlightedItemIndex != index) { anItem = [menuv_items_link objectAtIndex: index]; [anItem highlight: YES withFrame: [self rectOfItemAtIndex: index] inView: self]; [anItem setState: 1]; if ([anItem hasSubmenu] && ![[anItem target] isTornOff]) [[anItem target] display]; // set ivar to new index menuv_highlightedItemIndex = index; } else { menuv_highlightedItemIndex = -1; } } [self unlockFocus]; [window flushWindow]; } - (int)highlightedItemIndex { return menuv_highlightedItemIndex; } - (void)setMenuItemCell: (NSMenuItemCell *)cell forItemAtIndex: (int)index { // [menuv_items insertObject: cell atIndex: index]; // resize the cell [cell setNeedsSizing: YES]; // resize menuview [self setNeedsSizing: YES]; } - (NSMenuItemCell *)menuItemCellForItemAtIndex: (int)index { return [menuv_items_link objectAtIndex: index]; } - (NSMenuView *)attachedMenuView { return [[menuv_menu attachedMenu] menuView]; } - (NSMenu *)attachedMenu { return [menuv_menu attachedMenu]; } - (BOOL)isAttached { return [menuv_menu isAttached]; } - (BOOL)isTornOff { return [menuv_menu isTornOff]; } - (void)setHorizontalEdgePadding: (float)pad { menuv_hEdgePad = pad; } - (float)horizontalEdgePadding { return menuv_hEdgePad; } - (void)itemChanged: (NSNotification *)notification { } - (void)itemAdded: (NSNotification *)notification { } - (void)itemRemoved: (NSNotification *)notification { } // Submenus. - (void)detachSubmenu { } - (void)attachSubmenuForItemAtIndex: (int)index { // create rect to display submenu in. // order window with submenu in it to front. } - (void)update { // [menuv_menu update]; if (menuv_needsSizing) [self sizeToFit]; } - (void)setNeedsSizing: (BOOL)flag { menuv_needsSizing = flag; } - (BOOL)needsSizing { return menuv_needsSizing; } - (void)sizeToFit { int i; int howMany = [menuv_items_link count]; int howHigh = (howMany * cellSize.height); float neededWidth = 0; for (i=0;i neededWidth) neededWidth = aWidth; } cellSize.width = 7 + neededWidth + 7 + 7 + 5; if ([window contentView] == self) [window setContentSize: NSMakeSize(cellSize.width,howHigh)]; else [self setFrame: NSMakeRect(0,0,cellSize.width,howHigh)]; } - (void)sizeToFitForPopUpButton { int howHigh = ([menuv_items_link count] * cellSize.height); if ([window contentView] == self) [window setContentSize: NSMakeSize(cellSize.width,howHigh)]; else [self setFrame: NSMakeRect(0,0,cellSize.width,howHigh)]; } - (float)stateImageOffset { if (menuv_needsSizing) [self sizeToFit]; return menuv_stateImageOffset; } - (float)stateImageWidth { if (menuv_needsSizing) [self sizeToFit]; return menuv_stateImageWidth; } - (float)imageAndTitleOffset { if (menuv_needsSizing) [self sizeToFit]; return menuv_imageAndTitleOffset; } - (float)imageAndTitleWidth { if (menuv_needsSizing) [self sizeToFit]; return menuv_imageAndTitleWidth; } - (float)keyEquivalentOffset { if (menuv_needsSizing) [self sizeToFit]; return menuv_keyEqOffset; } - (float)keyEquivalentWidth { if (menuv_needsSizing) [self sizeToFit]; return menuv_keyEqWidth; } - (NSRect)innerRect { return [self bounds]; // this could change if we drew menuitemcells as // plain rects with no bezel like in macOSX. Talk to Michael Hanni if // you would like to see this configurable. } - (NSRect)rectOfItemAtIndex: (int)index { NSRect theRect; if (menuv_needsSizing) [self sizeToFit]; if (index == 0) theRect.origin.y = [self frame].size.height - cellSize.height; else theRect.origin.y = [self frame].size.height - (cellSize.height * (index + 1)); theRect.origin.x = 0; theRect.size = cellSize; return theRect; } - (int)indexOfItemAtPoint: (NSPoint)point { // The MacOSX API says that this method calls - rectOfItemAtIndex for // *every* cell to figure this out. Well, instead we will just do some // simple math. NSRect aRect = [self rectOfItemAtIndex: 0]; // this will need some finnessing but should be close. return ([self frame].size.height - point.y) / aRect.size.height; } - (void)setNeedsDisplayForItemAtIndex: (int)index { [[menuv_items_link objectAtIndex: index] setNeedsDisplay: YES]; } - (NSPoint)locationForSubmenu: (NSMenu *)aSubmenu { if (menuv_needsSizing) [self sizeToFit]; // find aSubmenu's parent // position aSubmenu's window to be adjacent to its parent. // return new origin of window. return NSZeroPoint; } - (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 { // huh. } // Drawing. - (void)drawRect: (NSRect)rect { int i; NSRect aRect = [self frame]; int howMany = [menuv_items_link count]; // This code currently doesn't take intercell spacing into account. I'll // need to fix that. aRect.origin.y = cellSize.height * (howMany - 1); aRect.size = cellSize; for (i=0;i= 0 && index < theCount) { [self setHighlightedItemIndex: index]; lastIndex = index; } while (!done) { event = [theApp nextEventMatchingMask: eventMask untilDate: theDistantFuture inMode: NSEventTrackingRunLoopMode dequeue: YES]; switch ([event type]) { case NSRightMouseUp: case NSLeftMouseUp: /* right mouse up or left mouse up means we're done */ done = YES; break; case NSRightMouseDragged: case NSLeftMouseDragged: lastLocation = [[self window] mouseLocationOutsideOfEventStream]; #if 0 NSLog (@"location = (%f, %f, %f)", lastLocation.x, [[self window] frame].origin.x, [[self window] frame].size.width); #endif if (lastLocation.x > 0 && lastLocation.x < [[self window] frame].size.width) { lastLocation = [self convertPoint: lastLocation fromView: nil]; index = (height - lastLocation.y) / cellSize.height; #if 0 NSLog (@"location = (%f, %f)", lastLocation.x, lastLocation.y); NSLog (@"index = %d\n", index); #endif if (index >= 0 && index < theCount) { if (index != lastIndex) { [self setHighlightedItemIndex: index]; lastIndex = index; } else { if (weWereOut) { [self setHighlightedItemIndex: index]; lastIndex = index; weWereOut = NO; } } } } else if (lastLocation.x > [[self window] frame].size.width) { NSRect aRect = [self rectOfItemAtIndex: lastIndex]; if (lastLocation.y > aRect.origin.y && lastLocation.y < aRect.origin.y + aRect.size.height && [[menuv_items_link objectAtIndex: lastIndex] hasSubmenu]) { weLeftMenu = YES; done = YES; } else { [self setHighlightedItemIndex: -1]; lastIndex = index; weWereOut = YES; [window flushWindow]; } } else if (lastLocation.x < 0) { if ([menuv_menu supermenu]) { weRightMenu = YES; done = YES; } else { [self setHighlightedItemIndex: -1]; lastIndex = index; weWereOut = YES; [window flushWindow]; } } else { // FIXME, Michael. This might be needed... or not? NSLog(@"This is the final else... its evil\n"); if (lastIndex >= 0 && lastIndex < theCount) { [self setHighlightedItemIndex: -1]; lastIndex = index; weWereOut = YES; [window flushWindow]; } } [window flushWindow]; default: break; } } if (!weLeftMenu && !weRightMenu && !weWereOut && menuv_highlightedItemIndex != -1) { if (![[menuv_items_link objectAtIndex: menuv_highlightedItemIndex] hasSubmenu]) { BOOL finished = NO; NSMenu *aMenu = menuv_menu; if (index >= 0 && index < theCount) selectedCell = [menuv_items_link objectAtIndex: index]; else selectedCell = nil; [self setHighlightedItemIndex: -1]; NSLog(@"just mouseUp'ed."); if ([selectedCell action] && ![selectedCell target]) [menuv_menu performActionForItem: [menuv_items_link objectAtIndex: lastIndex]]; else [menuv_popb performSelector:[selectedCell action] withObject:selectedCell]; while (!finished) { // "forward"cursive menu find. if ([aMenu attachedMenu]) { aMenu = [aMenu attachedMenu]; } else finished = YES; } finished = NO; while (!finished) { // Recursive menu close & deselect. if ([aMenu supermenu] && ![aMenu isTornOff]) { [[[aMenu supermenu] menuView] setHighlightedItemIndex: -1]; // [aMenu close]; aMenu = [aMenu supermenu]; } else finished = YES; [window flushWindow]; } } else if ([[menuv_items_link objectAtIndex: menuv_highlightedItemIndex] hasSubmenu] && [[[menuv_items_link objectAtIndex: menuv_highlightedItemIndex] target] isTornOff]) { // This code does not work. Please ignore. FIXME, Michael. // close transient. // [self setHighlightedItemIndex: -1]; // // [[[menuv_menu supermenu] menuView] setHighlightedItemIndex: -1]; } } else if (weRightMenu) { NSPoint cP = [[self window] convertBaseToScreen: lastLocation]; [self setHighlightedItemIndex: -1]; if ([menuv_menu supermenu] && ![menuv_menu isTornOff]) { [self mouseUp: [NSEvent mouseEventWithType: NSLeftMouseUp location: cP modifierFlags: [event modifierFlags] timestamp: [event timestamp] windowNumber: [[self window] windowNumber] context: [event context] eventNumber: [event eventNumber] clickCount: [event clickCount] pressure: [event pressure]]]; [[[menuv_menu supermenu] menuView] mouseDown: [NSEvent mouseEventWithType: NSLeftMouseDragged location: cP modifierFlags: [event modifierFlags] timestamp: [event timestamp] windowNumber: [[[[menuv_menu supermenu] menuView] window] windowNumber] context: [event context] eventNumber: [event eventNumber] clickCount: [event clickCount] pressure: [event pressure]]]; } } else if (weLeftMenu) { /* The weLeftMenu case */ NSPoint cP = [[self window] convertBaseToScreen: lastLocation]; NSLog(@"Urph.\n"); selectedCell = [menuv_items_link objectAtIndex: lastIndex]; if ([selectedCell hasSubmenu]) { [self mouseUp: [NSEvent mouseEventWithType: NSLeftMouseUp location: cP modifierFlags: [event modifierFlags] timestamp: [event timestamp] windowNumber: [[self window] windowNumber] context: [event context] eventNumber: [event eventNumber] clickCount: [event clickCount] pressure: [event pressure]]]; [[[selectedCell target] menuView] mouseDown: [NSEvent mouseEventWithType: NSLeftMouseDragged location: cP modifierFlags: [event modifierFlags] timestamp: [event timestamp] windowNumber: [[[[selectedCell target] menuView] window] windowNumber] context: [event context] eventNumber: [event eventNumber] clickCount: [event clickCount] pressure: [event pressure]]]; } } return YES; } - (void)mouseDown: (NSEvent *)theEvent { [self trackWithEvent: theEvent]; } @end