Validation of the contents of an NSTextFieldCell in a table

I would like to validate the contents of an NSTextFieldCell in a table when the user has finished editing it.
I think I know of 2 methods to do it, they both work for a regular NSTextField outside a table, but I can’t get any of the methods to work for a text field in a table.

The NSTextFieldCell is in a table column in a document-based application.
An array controller manages the values, and that one works.
The NSTextFieldCell’s attribute for Action is set to “Send On End of Editing” (in IB).

Method 1 is a simple bindings method, where I simply bind the NSTextFieldCell to a method (my defined applescript handler) in the File’s Owner.
I can get this to work beautifully for an NSTextFieldCell in a standard NSTextField in the window, but I cannot get it to work for the NSTextFieldCell that is inside a table.

Method 2 is by code. In this case, the NSTextFieldCell is first bound to an outlet in File’s owner via a property thus:

[b]property theTextField : missing value[/b] --bound as an outlet to the NSTextFieldCell in IB

In the code I then explicitly set the action method:

[b]theTextField's setAction_("theValidation:")[/b] --this is in the documentation for NSActionCell, "theValidation:" is a selector.

My expectation is that the following handler would be called after editing the text field in the table:

on theValidation_(sender)
display dialog “works”
end theValidation_

The Xcode documentation says that target by default is set to nil to allow it to be determined at run time. That should be ok for me, but I also tried to set it:
theTextField’s setTarget_(me)
But that did not solve anything.

What am I doing wrong, or misunderstand?
It is as if the table or array controller stops this from working, since it works for a regular NSTextField (outside the table)

PS: the prefix “the” in the property and handler names is actually something different in the real code, I just cleaned it from unnecessary clutter.

If I’m understanding your question correctly, you could try some like this:

		current application's NSNotificationCenter's defaultCenter's addObserver_selector_name_object_(me, "theValidation:", current application's NSControlTextDidEndEditingNotification, theTable)

This notification gets sent when you finish editing in the text cell of a table (whose outlet is theTable).

Ric

rdelmar, your suggestion is close, but does not fully reach my goal.

Adding the thing does indeed trigger my validation handler when a field is edited. Fine so far. To make this work, however, I had to use ‘missing value’ for the last parameter object (which makes it respond to any notification sender object).

Consequently, it responds to any editing of any field in the table. But I want just a certain field (or a certain column) in the table to respond. According to the documentation, the object parameter shall be NSString, and I tried various forms identifying either the column or just the text field cell, but I could not get that to work. I don’t quite understand what string it wants; the natural thing would be a bound outlet, but that is not NSString, and it does not work.

I also note, that the suggested method is totally independent of any bindings I do between the text field cell and the validation handler.

It appears to me as if the text field cell does indeed send a message/action/notification, but it somehow gets “absorbed” by the table. Your suggested method makes the whole table respond and send a message to File’s Owner.

I’m not sure what problem you’re having. The documentation doesn’t say that object parameter should be an NSString, it says (id). I tested my code, and it works with theTable (which is an outlet) for the object parameter.

I don’t know if there is an easier way to do this, but the following code checks to see what cell is being edited before invoking any action (I changed the notification I was using for theValidation selector to the NSControlTextDidChangeNotification). It also shows how to get the field editor, the string value of the cell that is being edited, and how to modify it depending on a condition.

script TableResponcesAppDelegate
	property parent : class "NSObject"
	property theTable : missing value --Connected to the NSTableView
	property theData : missing value
	
	on applicationWillFinishLaunching_(aNotification)
		set theData to current application's NSArray's arrayWithArray_({{theName:"Ric", thecount:1}, {theName:"Steve", thecount:2}})
		setTheData_(theData)
		current application's NSNotificationCenter's defaultCenter's addObserver_selector_name_object_(me, "theValidation:", current application's NSControlTextDidChangeNotification, theTable)
		current application's NSNotificationCenter's defaultCenter's addObserver_selector_name_object_(me, "notif:", current application's NSControlTextDidEndEditingNotification, theTable)
	end applicationWillFinishLaunching_
	
	on theValidation_(aNotification)
		--log aNotification
		set theFieldEditor to aNotification's userInfo's NSFieldEditor
		if (theTable's selectedRow() is 1) and (theTable's editedColumn() is 0) then
			--log theFieldEditor's |string|()
			set theNewString to theFieldEditor's |string|()'s capitalizedString()
			log theNewString
			if theNewString as string is "Paul" then
				set theNewString to "Frank"
			end if
			theFieldEditor's setString_(theNewString)
		end if
	end theValidation_
	
	on notif_(aNotification)
		log theData
	end notif_
	
end script

So, if you type “paul” into the second row of the first column, the code will change it to “Frank”

Ric

Well, unless I too did not understood your question, then you need to set a delegate class for your table view and use either controlTextDidBeginEditing: or controlTextDidEndEditing: to catch the cell’s value. This worked for me just fine.

I don’t think so – it should be the NSControl object posting the notification.

Ric, thanks a zillion for taking your time to not only post this, but also test the code!
After some digestion, I got it to work fine in my real project.
The field editor thing was a really fancy extras that I may find use for.

I need to comment/clarify the following though:

I was referring to the last parameter object in the handler: addObserver_selector_name_object_
The documentation for the method says:
- (void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(NSString *)notificationSender
To my understanding, the last parameter shall be NSString (or a pointer to it).
You and Shane appear to say something different, which confuses me considerably. There is something I don’t seem to understand here.

To gain a better understanding of all this (for future work), it would be good to know why the simple binding method fails just because the text field is inside a table. I’m thinking of my first simple method where I simply bind the NSTextFieldCell to a defined applescript handler in the File’s Owner, which works perfectly for an NSTextFieldCell in a standard NSTextField, but fails for an NSTextFieldCell in a table.

Also, I experimented some with adding an observer, trying to learn a bit more: I wonder why it is necessary to bind it to the table view rather than to a column of the table or to the NSTextFieldCell itself? (I did at least not get the latter things to work.)

leonsimard, thanks for the suggestion of explicitly setting the delegate of the table view!
Actually, I already experimented with control_textShouldEndEditing_ but since I never set the delegate of the table explicitly, I never got it to work.
I took for granted that since the delegate is set for the window, that it would be “inherited” by the objects inside the window, but this is obviously not the case.

Once I explicitly set the delegate of the table view in IB it works perfectly well! I can also use the method controlTextDidChange_ instead, and make it behave exactly the way Ric’s proposed solution does.

So, in the end, I think that the easiest and probably most elegant solution is to explicitly set the delegate of the table view in IB, and then implement controlTextDidChange_ with its body along the lines of theValidation_ as proposed by Ric with the fancy use of a field editor.

Thanks,
/Harald

That definition is from the NSDistributedNotificationCenter Class reference, and perhaps that is an error in the docs. The method, that Shane and I were referring to (with the same name) is in the NSNotificationCenter Class reference, and says:

I’m not sure what you mean by binding a text field cell to a handler – generally, you bind the value of a text field to a property in your code not the object itself, and not to a handler. Do you mean that you have an action handler that’s called when you finish editing your text field?

Ric

Aha!! That explains the confusion!

I mean the latter, i.e control-click the File’s Owner and drag from a ‘Received Action’ (my defined handler) to the NSTextFieldCell.

/Harald

Hi,

actually there is no need to subscribe explicitly to the controlTextDidEndEditing notification.
It will be sent automatically via the NSTableView’s delegate.
Just connect the delegate with your controller class or wherever you want to receive the notification
and implement the method

Another way to validate a cell in a table view is to implement the tableView:setObjectValue:forTableColumn:row: method of the NSTableViewDataSource Protocol or, if the array controller holds objects of a custom class, in their setter methods of the appropriate instance variables

Is this still valid? I see that almost everything is deprecated. What is the correct way nowadays?

A lot of the deprecations are just shuffling of methods from one protocol to another, as in house-keeping.