1. Welcome to the new MacRumors forums. See our announcement and read our FAQ

Simple Undo and Bindings Problem

Discussion in 'Mac Programming' started by stadidas, Nov 15, 2007.

  1. macrumors regular

    #1
    Hi everyone,

    I'm having a bit of an issue with the undo manager and bindings which I'm sure someone here can help me out with.
    In my MyDocument class I have a reference to an instance of DocumentModel, which has the following attributes:

    Code:
    @interface DocumentModel : NSObject
    {
    	double currentBalance;
    	double amountToSave;
    	double moneyToSpendPerDay;
    	double moneyToSpendPerWeek;
    	
    	int weeksLeft;
    	int daysLeft;
    	int numberOfDays;
    	
    	NSCalendarDate *endOfTermDate;
    	
    	int saveCheckbox;
    	
    	NSMutableArray *logEntries;
    }
    

    This instance of DocumentModel has an instance of NSObjectController bound to it. I then have an NSArrayController that I bind to the 'logEntries' key of the ObjectController, in order to control the logEntries MutableArray.
    This is all working perfectly well. However, I wish to override the insert and remove methods of the NSArrayController, and in the process make use of undo / redo :

    Code:
    - (void)insertObject:(LogEntry *)newLog inLogEntriesAtIndex:(int)index
    {
    	NSUndoManager *undo = [self undoManager];
    	[[undo prepareWithInvocationTarget:self]
    		removeObjectFromLogEntriesAtIndex:index];
    	
    	NSString *undoText;
    	if(newLog.typeFlag == BALANCE_CHANGE_FLAG)
    	{
    		undoText = [NSString stringWithFormat:@"Balance Change"];
    	}
    	else
    	{
    		undoText = [NSString stringWithFormat:@"Add '%@'", newLog.transactionDescription];
    	}
    	
    	if(![undo isUndoing])
    	{
    		[undo setActionName:undoText ];
    	}
    	
    	[logEntries insertObject:newLog atIndex:index];
    	[self calculateBalanceAndBudget];
    }
    
    - (void)removeObjectFromLogEntriesAtIndex:(int)index
    {
    	
    	LogEntry *entry = [logEntries objectAtIndex:index];
    	NSUndoManager *undo = [self undoManager];
    	[[undo prepareWithInvocationTarget:self]
    		 insertObject:entry inLogEntriesAtIndex:index];
    	
    	NSString *undoText;
    	if(entry.typeFlag == BALANCE_CHANGE_FLAG)
    	{
    		undoText = [NSString stringWithFormat:@"Delete Balance Change"];
    	}
    	else
    	{
    		undoText = [NSString stringWithFormat:@"Delete '%@'", entry.transactionDescription];
    	}
    	
    	if(![undo isUndoing])
    	{
    		[undo setActionName:undoText ];
    	}
    	
    	[logEntries removeObjectAtIndex:index];
    	[self calculateBalanceAndBudget];
    }
    
    I have tried putting these methods in MyDocument and in the DocumentModel objects, but whenever I add an entry nothing is put in the undo manager.
    If anyone can shed any light on the issue I would much appreciate it!
     
  2. Moderator emeritus

    kainjow

    #2
    Are you sure the NSUndoManager has been properly setup? What does [self hasUndoManager]; return?
     
  3. macrumors regular

    #3
    Hi Kainjow,

    I put the following print statement in the insertObject method of DataModel:

    Code:
    NSLog(@"DataModel UndoManager: %@", [self hasUndoManager]);
    
    which printed "*** -[DocumentModel hasUndoManager]: unrecognized selector sent to instance 0x1042c60" to the console. I take it from this that I have not got NSUndoManager correctly set up for this class. How would I go about doing this?
     
  4. Moderator emeritus

    kainjow

    #4
    Oops I thought your DocumentModel object was a NSDocument subclass. What about your [self undoManager] code. Can you post that?
     
  5. macrumors regular

    #5
    Ah no, MyDocument keeps a reference to an instance of DocumentModel. Calling [self undoManager] on DocumentModel returns:
    *** -[DocumentModel undoManager]: unrecognized selector sent to instance 0x1044fa0

    Is it an acceptable form of obtaining a reference to the document undoManager to just pass a reference to the DocumentModel object at creation from MyDocument. For example to have a variable named 'undoManager' in DocumentModel and then call
    Code:
    [documentModel setUndoManager:[self undoManager]];
    
    from MyDocument?
     
  6. Moderator emeritus

    kainjow

    #6
    Ok I'm confused then.

    In your original post, where are those methods being used? MyDocument or DocumentModel? If MyDocument, then the undoManager methods are fine since NSDocument maintains its own NSUndoManager, but you probably need to make sure setHasUndoManager: returns YES first. If you're calling them in DocumentModel, it will not work unless you have an undoManager method defined.
     
  7. macrumors regular

    #7
    They are being called in DocumentModel. I have now added NSUndoManager *undoManager to DocumentModel, and pass it an undoManager from MyDocument like this:

    Code:
    documentModel.undoManager = [self undoManager];
    
    I have edited the insert and remove methods to use this undoManager reference and it all works now, Cheers for your help, you've been very handy as always.
     

Share This Page