Re: Must remove submenu when dealloccing NSMenuItem?
Re: Must remove submenu when dealloccing NSMenuItem?
- Subject: Re: Must remove submenu when dealloccing NSMenuItem?
- From: Jerry Krinock <email@hidden>
- Date: Wed, 21 Feb 2007 21:39:12 -0800
- Thread-topic: Must remove submenu when dealloccing NSMenuItem?
on 07/02/21 20:34, stephen joseph butler at email@hidden wrote:
> Can you post some code for how you create these submenus?
Surely. Here you go!!
If anyone's interested, there's a lot more where this came from :)
By the way, in my original post I forgot to mention that the telltale
function that I saw in the stack trace of those crash reports a few weeks
ago was "HIToolbox PopulateMenu()".
*******************************************************************
Method in subclass of NSOutlineView which gives contextual menu:
*******************************************************************
- (NSMenu*)menuForTableColumnIndex:(int)iCol rowIndex:(int)iRow {
// Snipped out processing of iCol and iRow to get item
// and other local variables derived from item
NSMenu* menu = [[NSMenu allocWithZone:[NSMenu menuZone]]
initWithTitle:@""];
// Temporary variables which will be used while adding Menu Items
NSMenuItem* menuItem ;
SEL action ;
NSMenu* bookmarksHierarchicalMenu ; // Used in Copy To
_beLazy = NO ;
// Now, add menu items
// Snipped out adding several menu items
// Add Menu item "Copy to ->"
menuItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]]
initWithTitle:NSLocalizedString(@"Copy to", nil)
action:nil
keyEquivalent:@""];
bookmarksHierarchicalMenu = [[SSMenu alloc]
initWithOwningMenuItem:menuItem] ;
// Note: owningMenuItem is a convenience, so I don't have to
// use -supermenu and then enumerate through its -itemArray.
// So as not to create a retain cycle, it is a "weak reference";
// it is ^not^ retained by SSMenu
[menuItem setSubmenu:bookmarksHierarchicalMenu] ;
[bookmarksHierarchicalMenu setDelegate:
[self bookmarksHierarchicalMenuDataSource]] ;
// The above is an instance of MenuOutlineDataSource, see below.
// In -awakeFromNib, it is initWithHost:self
// That is, this outline view is what I call the "host" of the
// MenuOutlineDataSource. (It's a weak reference, not retained)
[bookmarksHierarchicalMenu release] ;
[menuItem setTag:2] ;
[menu addItem:menuItem];
[menuItem release];
// Snipped out adding additional menu items
// Snipped out releases of other local variables
return [menu autorelease] ;
}
*******************************************************************
Class: SSMenu
*******************************************************************
@interface SSMenu : NSMenu {
id _owningMenuItem ; // the NSMenuItem which this object is the submenu
of
// weak reference, to avoid retain cycles
}
// Snipped declarations
@end
@implementation SSMenu
- (void)setOwningMenuItem_Weakly:(id)x {
// Weak reference. Not retained, to avoid retain cycle
_owningMenuItem = x ;
}
- (id)owningMenuItem {
return _owningMenuItem ;
}
- (void)removeSubmenusFromAllItems {
int lastIndex = [[self itemArray] count] - 1 ;
int i ;
for (i=lastIndex; i>=0; i--) {
NSMenuItem* item = [self itemAtIndex:i] ;
[item setSubmenu:nil] ;
}
}
- (id)initWithOwningMenuItem:(id)owningMenuItem {
self = [super init];
if (self != nil) {
[self setOwningMenuItem_Weakly:owningMenuItem] ;
}
return self;
}
- (void) dealloc {
[self removeSubmenusFromAllItems] ;
[self setDelegate:nil] ;
[super dealloc];
}
- (id)retain {
return [super retain] ;
}
- (void)release {
[super release] ;
}
@end
*******************************************************************
Protocol: SSHierarchicalMenuHost
*******************************************************************
@protocol SSHierarchicalMenuHost
- (BOOL)beLazy ;
// For explanation of beLazy, see:
// http://www.cocoabuilder.com/archive/message/cocoa/2006/7/20/168013
- (NSArray*)selectedObjects ;
@end
*******************************************************************
Class: MenuOutlineDataSource
*******************************************************************
// A Data Source which provides delegate methods
// to build a hierarchical bookmarks menu
#import "SSApp/SSHierarchicalMenuHost.h"
@interface MenuOutlineDataSource : NSObject {
id _host ; // Weak reference, not retained, to avoid retain cycle
}
- (id) initWithHost:(id <SSHierarchicalMenuHost>)host ;
@end
@implementation MenuOutlineDataSource
- (void)setHost_Weakly:(id)host {
// Weak reference. Not retained, to avoid retain cycle
_host = host ;
}
- (id)host {
return _host ;
}
- (IBAction)abortBecauseCopyNotAllowed:(id)sender {
NSBeep() ;
[[NSApp delegate] alertCannotCopy] ;
}
- (IBAction)abortBecauseMoveNotAllowed:(id)sender {
NSBeep() ;
}
- (IBAction)moveOrCopySelectionToSender:(id)sender {
BmItem* targetFolder = [sender representedObject] ;
int tag = 0 ;
NSMenuItem* ancestor = sender ;
// Go up the menu hierarchy until a tag is found, to indicate
// whether this came from the Move To or Copy To menuItem
while (tag == 0) {
ancestor = [(SSMenu*)[ancestor menu] owningMenuItem] ;
tag = [ancestor tag] ;
}
enum SSParentingAction action ;
if (tag == 1) {
action = SSParentingMove ;
}
else {
action = SSParentingCopy ;
}
[[NSApp delegate] parentingAction:action
items:[[self host] selectedObjects]
toParents:targetFolder
atIndexes:[NSNumber numberWithInt:0]] ;
}
- (int)numberOfItemsInMenu:(SSMenu*)menu {
int answer ;
if ([[self host] beLazy]) {
answer = 0 ;
}
else {
BmItem* itemRepresentedByMenu = [[menu owningMenuItem]
representedObject] ;
if (!itemRepresentedByMenu) {
// root item
answer =
[[[NSDocumentController sharedDocumentController]
bookmarksDocuments] count] ;
}
else {
// not root item
answer = [itemRepresentedByMenu numberOfSubfolders] ;
}
}
return answer ;
}
- (BOOL)menu:(SSMenu*)menu
updateItem:(NSMenuItem*)item
atIndex:(int)index
shouldCancel:(BOOL)shouldCancel {
BOOL answer ;
id <SSHierarchicalMenuHost> host = [self host] ;
if ([host beLazy]) {
answer = NO ;
}
else {
BmItem* itemRepresentedByMenu =
[[menu owningMenuItem] representedObject] ;
NSString* title ;
id rawRepresentedItem ;
BmItem* cookedRepresentedItem ;
if (!itemRepresentedByMenu) {
// itemRepresentedByMenu is a root
rawRepresentedItem =
[[[NSDocumentController sharedDocumentController]
bookmarksDocuments] objectAtIndex:index] ;
title = [rawRepresentedItem displayName] ;
cookedRepresentedItem = [rawRepresentedItem root] ;
}
else {
// itemRepresentedByMenu is not a root
rawRepresentedItem =
[[itemRepresentedByMenu subfolders] objectAtIndex:index] ;
title = [rawRepresentedItem name] ;
cookedRepresentedItem = rawRepresentedItem ;
}
[item setRepresentedObject:cookedRepresentedItem] ;
[item setTitle:title] ;
[item setTarget:self] ;
// Set action depending on whether the drop proposed move/copy is OK
// Note: If I setEnabled:NO, this grays it out, and the triangle
still
// appears on the right, but unfortunately the submenu does not
appear
// I wish I could have a grayed out menu item with a working
submenu,
// but Cocoa does not seem to support this. So I leave it always
// enabled and set the action to do explain and/or beep after item
// is clicked.
NSArray* proposedPayload = [host selectedObjects] ;
if (![[NSApp delegate] checkOkToCopyItems:proposedPayload]) {
[item setAction:@selector(abortBecauseCopyNotAllowed:)] ;
}
else if (![[NSApp delegate] checkOKToInsertItems:proposedPayload
atBmLocation:
[BmLocation BmLocationWithParent:
cookedRepresentedItem index:0]]) {
[item setAction:@selector(abortBecauseMoveNotAllowed:)] ;
}
else {
[item setAction:@selector(moveOrCopySelectionToSender:)] ;
}
if ([cookedRepresentedItem numberOfSubfolders] > 0) {
// Create and add a submenu to item
SSMenu* submenu = [[SSMenu alloc] initWithOwningMenuItem:item] ;
[submenu setDelegate:self] ;
[item setSubmenu:submenu] ;
[submenu release] ;
}
answer = YES ;
}
// If you return NO, it does not come back and ask to update any
// more items, so you can end up with "empty" menu items if the menu
// is actually shown
return answer ;
}
- (id)initWithHost:(id <SSHierarchicalMenuHost>)host {
if (self = [super init]) {
[self setHost_Weakly:host] ;
}
return self;
}
@end
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden