Re: NSTableView with PopUpButtonCell column with different content per row
Re: NSTableView with PopUpButtonCell column with different content per row
- Subject: Re: NSTableView with PopUpButtonCell column with different content per row
- From: Jerry Krinock <email@hidden>
- Date: Fri, 19 Feb 2010 14:51:53 -0800
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 (email@hidden)
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:
This email sent to email@hidden