Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
Discussion Groups
General
GeneralPortable MacsHardwareNetworking
Applications
Mac ApplicationsEudoraFirefox / MozillaInternet ExplorerOutlook ExpressMS OfficeEntourageExcelPowerPointWordVirtual PCMedia PlayerOther MS Products
Programming
Mac ProgrammingCodeWarriorPerl
Country Specific
Australian Mac GroupUK Mac Group

Mac Forum / Programming / Mac Programming / April 2008



Tip: Looking for answers? Try searching our database.

Manipulating an NSArrayController

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Cory - 20 Apr 2008 06:41 GMT
Alright, so I have a question on the best way to achieve this. I
realize there are probably many ways (as were pointed out to me
already) but I'm not sure which is the best way to do it, or perhaps
the easiest way. Here's what I've got:

I have an application that keeps track of many objects that I've
created. The main nib window has a NSTableView to show all the
objects, and on the right is an area for all the information on that
object to be displayed. There are add and remove buttons on the
bottom, and when the user clicks the add button a new nib file window
appears asking about the object to be added. Once all the information
is filled in, the user clicks OK and the new object is added to the
ArrayController in the main nib. I have some of this working and some
not.

I have the ArrayController in the main nib file, and the table shows
all the objects in the array. I have an AppController.m that I created
to manage all the things my application would be calling. When the
user clicks the add button, this is called from the AppController

- (IBAction)addButton:(id)sender {
    // Init and set window NIB
    addWindow = [[AddWindowController
alloc]initWithWindowNibName:@"AddWindow"];
    // Open window
    [addWindow window];
}

In the AddWindow, I have an instance of my object with all the fields
mapped to it. I tried writing another method in my AppController to be
called when the user is done to add the new object to my
ArrayController in the first nib, but I had to instantiate a new
AppController that didn't know about the ArrayController in the other
nib, because it belonged to the new nib. From the way it sounds, I can
use the File's Owner in the new nib file because the AppController
owns the second nib to call the method, and because the AppController
belongs to the first nib I can connect them this way. Does that make
sense? Is that the correct way of doing things, or is that something
that just happens to work that shouldn't be done?

Also, when my new window is brought up it appears on top, but the
first window still has focus. How can I force the focus to show up on
the first text field in the new window?

The last thing (well, for now) that I'll need help with is saving this
list of objects to a file on close and loading them next time the
application is opened. What would I do to save this, and where would
it be stored?

Thanks!
Cory
Cory - 24 Apr 2008 03:31 GMT
> Alright, so I have a question on the best way to achieve this. I
> realize there are probably many ways (as were pointed out to me
[quoted text clipped - 48 lines]
> Thanks!
> Cory

Can anyone help me with this?
David Phillip Oster - 26 Apr 2008 04:28 GMT
In article
<14922686-3e10-47ef-9c99-cda6ae2c3bed@a23g2000hsc.googlegroups.com>,

> > Alright, so I have a question on the best way to achieve this. I
> > realize there are probably many ways (as were pointed out to me
[quoted text clipped - 50 lines]
>
> Can anyone help me with this?

I replied privately off list.

Table of Contents:
  //  AppDelegate.h
  //  AppDelegate.m
  //  AddAction.h
  //  AddAction.m
  //  Item.h
  //  Item.m

//  AppDelegate.h
#import <Cocoa/Cocoa.h>

@class Item;
@class AddAction;

@interface AppDelegate : NSObject {
 NSMutableArray *addActions_;
 NSMutableArray *model_;
}
- (NSMutableArray *)model;
- (void)setModel:(NSMutableArray *)model;
- (IBAction)addItem:(id)sender;
- (void)appendItem:(Item *)item;

- (void)addActionWillClose:(AddAction *)action;
@end

//  AppDelegate.m
#import "AppDelegate.h"
#import "AddAction.h"
#import "Item.h"

@implementation AppDelegate
- (id)init {
 self = [super init];
 if (self) {
   addActions_ = [[NSMutableArray alloc] init];
   model_ = [[NSMutableArray alloc] init];
 }
 return self;
}

// Note: the app's delegate isn't actually ever dealloced. [app
terminate] will
//  just call exit() without cleaning up.
- (void)dealloc {
 [addActions_ makeObjectsPerformSelector:@selector(close)];
 [addActions_ release];
 [model_ release];
 [super dealloc];
}

#pragma mark -
#pragma mark Accessors

- (NSMutableArray *)model {
 return model_;
}

- (void)setModel:(NSMutableArray *)model {
 [model_ autorelease];
 model_ = [model retain];
}

- (void)appendItem:(Item *)item {
// we call setter to trigger bindings. There are many ways to trigger
bindings.
 NSMutableArray *a = [self model];
 [a addObject:item];
 [self setModel:a];
}

#pragma mark -
#pragma mark Actions

- (IBAction)addItem:(id)sender {
 AddAction *action = [[[AddAction alloc] initWithOwner:self]
autorelease];
 [addActions_ addObject:action];
}

- (void)addActionWillClose:(AddAction *)action {
 [addActions_ removeObject:action];
}

#pragma mark -
#pragma mark I/O

- (NSString *)filePath {
 // NSACE stands for NSArrayControllerExample
 NSString *folderPath = [@"~/Library/Application Support/NSACE"
stringByExpandingTildeInPath];
 NSFileManager *fm = [NSFileManager defaultManager];
 BOOL isDir = NO;
 if ( ! ([fm fileExistsAtPath:folderPath isDirectory:&isDir] && isDir))
{
   if( ! [fm createDirectoryAtPath:folderPath attributes:nil]) {
     // if couldn't make dir, fall back on:
     folderPath = [@"~/Documents" stringByExpandingTildeInPath];
   }
 }
 return [folderPath stringByAppendingPathComponent:@"NSACE.nsace"];
}

// for this example, we always write.
-(BOOL)hasModelChanged {
 return YES;
}

- (NSMutableArray *)initialDummyModel {
 NSMutableArray *m = [NSMutableArray array];
 [m addObject:[Item itemWithName:@"John Smith" address:@"Smith Town"
email:@"Smith@example.com"]];
 [m addObject:[Item itemWithName:@"Jim Jones" address:@"Jones Town"
email:@"Jones@example.com"]];
 [m addObject:[Item itemWithName:@"James Kirk" address:@"Kirk City"
email:@"Kirk@example.com"]];
 [m addObject:[Item itemWithName:@"Lena Horn" address:@"Hornville"
email:@"Horn@example.com"]];
 return m;
}

// you can use the command line tool plutil to convert the data file to
a text, XML format
- (BOOL)readFile:(NSString *)filePath error:(NSError **)error {
 NSFileManager *fm = [NSFileManager defaultManager];
 // OK for file not to exist.
 if ( ! [fm fileExistsAtPath:filePath]) {
   // generic initial model.
   [self setModel:[self initialDummyModel]];
   return YES;
 }
 NSData *data = [NSData dataWithContentsOfFile:filePath options:0
error:error];
 if (data) {
   NSMutableArray *m = [NSKeyedUnarchiver unarchiveObjectWithData:data];
   if (m) {
     [self setModel:m];
     return YES;
   }
   NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
     NSLocalizedString(@"CouldntDecode", @""),
NSLocalizedDescriptionKey,
     nil];
   *error = [NSError errorWithDomain:@"appDomain" code:-2
userInfo:dict];
 }
 return NO;
}

- (BOOL)writeFile:(NSString *)filePath error:(NSError **)error {
 NSData *data = [NSKeyedArchiver archivedDataWithRootObject:model_];
 if (nil == data) {
   NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
     NSLocalizedString(@"CouldntMakeEncode", @""),
NSLocalizedDescriptionKey,
     nil];
   *error = [NSError errorWithDomain:@"appDomain" code:-1
userInfo:dict];
   return NO;
 }
 return [data writeToFile:filePath options:NSAtomicWrite error:error];
}

// Called 'cause we are delegate of main window
#pragma mark -
#pragma mark App Callbacks (this is the app delegate)

- (void)windowWillClose:(NSNotification *)notification {
 [NSApp terminate:nil];
}

- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication
*)sender {
 return YES;
}

- (void)applicationDidFinishLaunching:(NSNotification *)notification {
 NSError *error = nil;
 if( ! [self readFile:[self filePath] error:&error] ){
   [NSApp presentError:error];
 }
}

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication
*)sender {
 if ([self hasModelChanged]) {
   NSError *error = nil;
   if( ! [self writeFile:[self filePath] error:&error] ){
     [NSApp presentError:error];
   }
 }
 return NSTerminateNow;
}

@end

//  AddAction.h

#import <Cocoa/Cocoa.h>

@class Item;
@class AppDelegate;

@interface AddAction : NSObject {
 Item *item_;
 AppDelegate *owner_; // WEAK
 IBOutlet NSWindow *window_;
}
- (id)initWithOwner:(AppDelegate *)owner;
- (Item *)item;
- (void)setItem:(Item *)item;
- (NSWindow *)window;
- (IBAction)cancel:(id)sender;
- (IBAction)ok:(id)sender;

- (void)close;

@end

//  AddAction.m

#import "AddAction.h"
#import "AppDelegate.h"
#import "Item.h"

// Feel free to comment out the lines labelled:
//alloc/dealloc testing
// once you are convinced we aren't leaking addActions.

@implementation AddAction
- (id)initWithOwner:(AppDelegate *)owner {
NSLog(@"AddAction initWithOwner"); //alloc/dealloc testing
 self = [super init];
 if (self) {
   owner_ = owner;
   NSNib *itemNib = [[[NSNib alloc] initWithNibNamed:@"itemEdit"
bundle:nil] autorelease];
   [itemNib instantiateNibWithOwner:self topLevelObjects:nil];
   item_ = [[Item alloc] init];
   [window_ makeKeyAndOrderFront:nil];
 }
 return self;
}

- (void)dealloc {
NSLog(@"AddAction dealloc"); //alloc/dealloc testing
 [item_ release];
 owner_ = nil;
 [super dealloc];
}

- (Item *)item {
 return item_;
}

- (void)setItem:(Item *)item {
 [item_ autorelease];
 item_ = [item retain];
}

- (NSWindow *)window {
 return window_;
}

- (void)close {
 [owner_ addActionWillClose:self];
 [window_ orderOut:nil];
// Sketch has a comment about Cocoa bindings creating a retain loop in
Tiger.
// this line fixes that.
 [self unbind:@"item"];
}

- (IBAction)cancel:(id)sender {
 [self close];
}

- (IBAction)ok:(id)sender {
// so we don't need to tab out of last feed for it to be seen.
 [window_ endEditingFor:nil];

 [owner_ appendItem:[self item]];
 [self close];
}

@end

//  Item.h
@interface Item : NSObject<NSCoding> {
@private
 NSString *name_;
 NSString *email_;
 NSString *address_;
 NSColor *color_;
}
+(Item *)itemWithName:(NSString *)name
     address:(NSString *)address
       email:(NSString *)email ;

- (NSString *)name;
- (void)setName:(NSString *)name;

- (NSString *)email;
- (void)setEmail:(NSString *)email;

- (NSString *)address;
- (void)setAddress:(NSString *)address;

- (NSColor *)color;
- (void)setColor:(NSColor *)color;

@end

//  Item.m
#import "Item.h"

@implementation Item

+(Item *)itemWithName:(NSString *)name address:(NSString *)address
email:(NSString *)email {
 Item *val = [[[Item alloc] init] autorelease];
 [val setName:name];
 [val setAddress:address];
 [val setEmail:email];
 [val setColor:[NSColor blackColor]];
 return val;
}

- (id)init{
 self = [super init];
 return self;
}

- (void)dealloc {
 [name_ release];
 [email_ release];
 [address_ release];
 [color_ release];
 [super dealloc];
}

- (NSString *)name {
 return name_;
}

- (void)setName:(NSString *)name {
 [name_ autorelease];
 name_ = [name copy];
}

- (NSString *)email {
 return email_;
}

- (void)setEmail:(NSString *)email {
 [email_ autorelease];
 email_ = [email copy];
}

- (NSString *)address {
 return address_;
}

- (void)setAddress:(NSString *)address {
 [address_ autorelease];
 address_ = [address copy];
}

- (NSColor *)color {
 return color_;
}

- (void)setColor:(NSColor *)color {
 [color_ autorelease];
 color_ = [color retain];
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
 if(name_){ [aCoder encodeObject:name_ forKey:@"name"]; }
 if(email_){ [aCoder encodeObject:email_ forKey:@"email"]; }
 if(address_){ [aCoder encodeObject:address_ forKey:@"address"]; }
 if(color_){ [aCoder encodeObject:color_ forKey:@"color"]; }
}

- (id)initWithCoder:(NSCoder *)aDecoder {
 name_ = [[aDecoder decodeObjectForKey:@"name"] retain];
 email_ = [[aDecoder decodeObjectForKey:@"email"] retain];
 address_ = [[aDecoder decodeObjectForKey:@"address"] retain];
 color_ = [[aDecoder decodeObjectForKey:@"color"] retain];
 return self;
}

@end
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.