Missing cellValueChanged-handler

Hi,

in a table view the user sets decimal numbers into some column cells of a selected row. After edditing every cell the sum of the numbers should set into the cell in the last column. That have I done i AS Studio, but in ASObjC I need help.
As a solution being for the time I have bind the following code to a button. That helps only partly.


        property parent : class "NSObject"
	
	property theData : missing value
	
	property theArrayController : missing value -- contentArray ArrayController
	
	-- IB Actions (button clicks)

	on insertNames_(sender)
		try
			set theContents to the clipboard as string
		end try
		set oldTextItemDelimiters to AppleScript's text item delimiters
		set AppleScript's text item delimiters to return
		set theCount to (count text items of theContents)
		if last text item of theContents is "" then
			set theCount to theCount - 1
		end if
		set newData to {}
		repeat with i from 1 to theCount
			set newName to text item i of theContents
			set newData to newData & {{column1:newName}} -- arrangedObjects TableColumn and identifier
		end repeat
		set my theData to newData
		set AppleScript's text item delimiters to oldTextItemDelimiters
	end insertNames_
	
	on calculate_(sender)
		set theArraysSelectedRow to theArrayController's selectedObjects()
		set sum to 0
		repeat with a in {"column2", "column3", "column4"} -- arrangedObjects TableColumn and identifier
			set cellContent to (theArraysSelectedRow's valueForKey_(a))
			try
				set sum to sum + (cellContent as integer)
			end try
		end repeat
		--log sum
		
		theArraysSelectedRow's setValue_forKey_(sum, "column5")
	end calculate_


Heiner

Model: iMac
Browser: Safari 525.13
Operating System: Mac OS X (10.6)

You can probably use one of the table delegate methods to trigger your script, but it might be better to take a more object-oriented approach. So you would make a new class representing a row of your table, and in it you would define properties for each of your columns. You would define setters for columns 2-4 that also re-calculated column 5.

You would set the Class Name of the array controller to this class and not have the array controller bind to anything. Rather than add items by changing the value of theData, you’d do it by using the array controller’s addObject; or addObjects: method.

So your new class might look like this:

script OneRow
	property parent : class "NSObject"
	property column1 : missing value
	property column2 : missing value
	property column3 : missing value
	property column4 : missing value
	property column5 : missing value
	
	on init()
		my initWithData_({"", 0, 0, 0})
		return result
	end init
	
	on initWithData_(aList)
		continue init()
		set {my column1, my column2, my column3, my column4} to aList as list
		set my column5 to column2 + column3 + column4
		return me
	end initWithData_
	
	on setColumn2_(n)
		set my column2 to n as real
		set my column5 to ((n as real) + column3 as real) + column4 as real
	end setColumn2_
	
	on setColumn3_(n)
		set my column3 to n as real
		set my column5 to ((n as real) + column2 as real) + column4 as real
	end setColumn3_
	
	on setColumn4_(n)
		set my column4 to n as real
		set my column5 to ((n as real) + column3 as real) + column2 as real
	end setColumn4_
	
end script

In your main script, to add a blank row you can just call the array controller’s add: method. If you want some initial data, you could do something like this:


	property ac : missing value -- array controller
	
	on applicationWillFinishLaunching_(aNotification)
		set newRow to current application's OneRow's initWithData_({"Name", 1, 2, 3})
		ac's addObject_(newRow)

It’s a different approach, and might seem a bit strange, but it enables you to let the array controller do most of the work, including storing all the data.

Hi Shane,

thank you for your help.

But I have problems furthermore.
First, the table view that I have, is just an example for testing. Later on it should have 8 columns at least.( The user can append additional columns. But that’s for later.)

Second, I tried to use your code and did what you wrote (I think): I

a) appended your script at the end of my script;
b) edited the ‘WillFinishLaunching’-handler on your way;
c) deleted the variable theData
d) substituted the line ‘set myData to newdata’ for 'theArrayController’s addObjects_(newData)

The result is only the first line in the table works well due to the initial settings.
But wenn I add the name list from clipboard it doesn’t work.

Here are my bindings:

  • property theArrayController to Array Controller
  • theArrayController to AppDelig.
  • the values of every column to Array Controller with key ‘arrangedObjects’ and path ‘column1’, etc.

Heiner

The principle is the same, though – you’ll just need more setters. You could try simplifying them a bit like this:

	on setColumn2_(n)
		set my column5 to (column5 as real) + (n as real) - (column2 as real)
		set my column2 to n as real
	end setColumn2_

so you wouldn’t have to go back and edit them all if you decide to add another column.

(If you want users to be able to add columns, though, I suspect you’ll have to use a different approach altogether. So I’ll pretend you didn’t say that for the moment :slight_smile: )

I’d put it in a separater file (File → New File), but it shouldn’t matter.

And removed the array controller’s binding to it, I hope.

You’ll need a bit more than that. You need to change this:

       set newData to {}
       repeat with i from 1 to theCount
           set newName to text item i of theContents
           set newData to newData & {{column1:newName}} -- arrangedObjects TableColumn and identifier
       end repeat
       set my theData to newData

into something like this:

set newData to {}
repeat with i from 1 to theCount
	set newName to text item i of theContents
	set end of newData to current application's OneRow's initWithData_({newName, 0, 0, 0})
	ac's addObjects_(newData)
end repeat

Although it might be quicker to skip an newData and simply use:

repeat with i from 1 to theCount
	set newName to text item i of theContents
	set aRow to current application's OneRow's initWithData_({newName, 0, 0, 0})
	ac's addObject_(aRow)
end repeat

See how that goes.

Actually, you can simplify things a bit by making column5 a pseudo-property. So OneRow would look like this:

script OneRow
	property parent : class "NSObject"
	property column1 : missing value
	property column2 : missing value
	property column3 : missing value
	property column4 : missing value
	
	on init()
		my initWithData_({"", 0, 0, 0})
		return result
	end init
	
	on initWithData_(aList)
		continue init()
		set {my column1, my column2, my column3, my column4} to aList as list
		return me
	end initWithData_
		
	on column5()
		set theVal to 0
		try -- in case column is empty
			set theVal to (column2 as real) + theVal
		end try
		try
			set theVal to (column3 as real) + theVal
		end try
		try
			set theVal to (column4 as real) + theVal
		end try
		return theVal
	end column5
	
end script

Shane wrote:

Because I’m a dilettante I have to develop my apps step by step and from down to top only. If I come to an ‘one-way street’ than I looked for a new way (I did this for thousends for one times - I think)

But now to the current problem.
I did as you said, but I get theCount times the same insertion with the last name; editing effects all lines identically.

I made some trials:


--set aRow to {}
		repeat with i from 1 to theCount
			set newName to text item i of theContents
			--log newName (OK!)
			--set aRow to aRow & current application's OneRow's initWithData_({newName, 0, 0, 0})
			set aRow to current application's OneRow's initWithData_({newName, 0, 0, 0})
			-- log aRow  (different things)
			theArrayController's addObject_(aRow)
			set aRow to {}
		end repeat
		-- log aRow (different things)
		--theArrayController's addObjects_(aRow)


All the same.

A question: Could it not be that there is something wrong in my bindings?
I tried binding column cells instead of the columns: rubbish.

Heiner

It could be the bindings. Why don’t you email me with what you have.

For anyone following along, we found the problem:

       set newRow to current application's OneRow's initWithData_({"Name", 1, 2, 3})

should be:

       set newRow to current application's OneRow's alloc()'s initWithData_({"Name", 1, 2, 3})

Why the former ever worked for me is still a mystery…