On 2010 Feb 19, at 13:30, Sean McBride wrote: > In my case currently, I need several separators and also an 'other...' > item that brings up an open panel.
I don't know about the separators but I have done the 'Other...' item successfully. Referring to my previous message, define a new class called FooChoice which is a thin wrapper around a Foo. A regular FooChoice will have a Foo ivar, but your "Other..." FooChoice will instead have a selector ivar. Now, add to your model fooChoice and setFooChoice: methods which transform between Foo and FooChoice. When setFooChoice: gets a fooChoice with a selector instead of a foo, it plays an NSOpenPanel. (I suppose an MVC purist might do this in a controller class somewhere, but oh well it works for me.) The FooChoice is kind of like a proxy, right? Don't forget +keyPathsForValueAffectingXxxxxxx. I'm pasting in below my ClientChoice class code. It looks like it has some comments in it that you might find interesting. #import <Cocoa/Cocoa.h> @class Client ; /*! @brief A thin wrapper around a client, or a placeholder which allows users to choose "Other Mac Account" or "Choose File" instead of an existing client. @details A client choice which wraps a client will have a non-nil client ivar and a nil selector. A client choice which wraps a placeholder will have a nil client and a non-nil selector. */ @interface ClientChoice : NSObject { Client* m_client ; SEL m_selector ; id m_object ; } /*! @brief The receiver's title, suitable for display as the title of a (popup) menu item. */ @property (readonly) NSString* displayName ; @property (retain) Client* client ; @property (assign) SEL selector ; @property (retain) id object ; /*! @brief Returns a ClientChoice defined by a client. @details Returns a ClientChoice even if client is nil. This is so that the localized "No Selection" placeholder can be returned for displayName and appear in the popup in this case. @param client The client that is being wrapped */ + (ClientChoice*)clientChoiceWithClient:(Client*)client ; + (ClientChoice*)clientChoiceInvolvingOtherMacAccount ; + (ClientChoice*)clientChoiceInvolvingLooseFile ; + (ClientChoice*)clientChoiceNewOtherAccountForWebAppExformat:(NSString*)exformat ; @end #import "ClientChoice.h" #import "Client.h" #import "NSString+LocalizeSSY.h" ; #import "BkmxBasis+Strings.h" #import "Extore.h" @implementation ClientChoice @synthesize client = m_client ; @synthesize selector = m_selector ; @synthesize object = m_object ; - (NSString*)displayName { NSString* displayName = nil ; Client* client = self.client ; if (client) { displayName = [client displayName] ; } else { SEL selector = [self selector] ; if (selector) { NSString* targetString = NSStringFromSelector(selector) ; NSString* aString ; aString = @"chooseClientOnOtherMacAccountWithObject:" ; if ([targetString isEqualToString:aString]) { displayName = [NSString stringWithFormat:@"%C %@", 0x27a4, [NSString localize:@"accountMacOther"]] ; } else if ([targetString isEqualToString:@"chooseClientFromFile"]) { displayName = [NSString stringWithFormat:@"%C %@ %@", 0x27a4, [NSString localizeFormat: @"choose%0", [NSString localize:@"file"]], [[BkmxBasis sharedBasis] labelAdvanced]] ; } else if ([targetString isEqualToString:@"setClientWithNilProfileForWebAppExformat:"]) { NSString* newSlashOther = [NSString stringWithFormat: @"%@/%@", [NSString localize:@"new"], [NSString localize:@"other"]] ; displayName = [NSString stringWithFormat: @"%@ - %@", [[Extore extoreClassForExformat:[self object]] ownerAppDisplayName], newSlashOther] ; } else { NSLog(@"Internal error 510-5918. sel=%@", targetString) ; } } else { displayName = [NSString localizeFormat:@"no%0", [NSString localize:@"selection"]] ; } } return displayName ; } + (ClientChoice*)clientChoice { ClientChoice* clientChoice = [[ClientChoice alloc] init] ; return [clientChoice autorelease] ; } + (ClientChoice*)clientChoiceWithClient:(Client*)client { ClientChoice* clientChoice = [ClientChoice clientChoice] ; [clientChoice setClient:client] ; return clientChoice ; } + (ClientChoice*)clientChoiceInvolvingOtherMacAccount { ClientChoice* clientChoice = [ClientChoice clientChoice] ; [clientChoice setSelector:@selector(chooseClientOnOtherMacAccountWithObject:)] ; return clientChoice ; } + (ClientChoice*)clientChoiceInvolvingLooseFile { ClientChoice* clientChoice = [ClientChoice clientChoice] ; [clientChoice setSelector:@selector(chooseClientFromFile)] ; return clientChoice ; } + (ClientChoice*)clientChoiceNewOtherAccountForWebAppExformat:(NSString*)exformat { ClientChoice* clientChoice = [ClientChoice clientChoice] ; [clientChoice setSelector:@selector(setClientWithNilProfileForWebAppExformat:)] ; [clientChoice setObject:exformat] ; return clientChoice ; } /* NSPopupMenuCell apparently uses isEqual: to match to an existing menu item when it gets the currently-selected value from the model. Evidence: If this method is not implemented, when the user clicks the menu, it adds this value as an additional item, (and also it displays its -description instead of using the model key path in the Content Values binding, which in our case is displayName). So you temporarily have this funky-looking extra item in the menu. Hypothesis: Cocoa uses pointer equality if -isEqual: is not implemented, and doesn't find equality since we generate ClientChoice wrappers as needed, on the fly. Anyhow, I haven't seen this documented anywhere - I just took a wild guess to fix the problem when I saw the extra/funky menu item showing up when I clicked the popup menu in my Import and and Export Client table views, and this fixed it. */ - (BOOL)isEqual:(id)otherChoice { if ([[self client] isEqual:[otherChoice client]]) { return YES ; } else if (([(ClientChoice*)self selector] != NULL) || ([(ClientChoice*)otherChoice selector] != NULL)) { // After a little testing I convinced myself that selectors // are system-wide constants. That is, the value of // @selector(anyMethod:someArg:) is the same pointer address // at any point in the program. So, since the selector ivars // are generated using the @selector() thing, I can just // compare their pointer values directly... return ([(ClientChoice*)self selector] == [(ClientChoice*)otherChoice selector]) ; } return NO ; } // Documentation says to override -hash if you override -isEqual: - (NSUInteger)hash { if ([self client]) { return [[self client] hash] ; } else if ([(ClientChoice*)self selector] != NULL) { return [NSStringFromSelector([(ClientChoice*)self selector]) hash] ; } return 0 ; } - (NSString*)description { NSString* answer = [NSString stringWithFormat:@"ClientChoice %p with ", self] ; if (self.client) { answer = [answer stringByAppendingFormat:@"client; clientSignature %@", self.client.clientSignature] ; } else if (self.selector) { answer = [answer stringByAppendingFormat:@"selector %@", NSStringFromSelector(self.selector)] ; } else { // At one time, I saw this displayed when the user clicks the popup. // Doesn't make sense. I quick-fixed it by mimicking // the -displayName output in this case. Probably this // patch is no longer needed. answer = [NSString localizeFormat:@"no%0", [NSString localize:@"selection"]] ; } return answer ; } - (void)dealloc { [m_client release] ; [super dealloc]; } @end_______________________________________________ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please 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: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com