Populate combobox from Tab-delimited file

Hi all,

I have been trying forever and am getting nowhere for the moment “ maybe I can get someone to help me a bit, stir me in the right direction.

I have a tab-delimited text, that is a list of publications with sizes.

I would like to have something like this:
1- at launch, read the file and get its content
2- populate combobox with Publication Names (item 1 of each paragraph I guess)
3- based on the chosen publication, some forms will automatically fill up with the various sizes info (editable)

It seems simple enough “ but I have tried countless methods and am sure I came close at one point or another.
But I am pretty desperate.

Thanks for any help.

Here is the bit I have so far, in which I am trying to fill up the combobox with the item 1 of the tab-delimeted file, “adPublication”.
My combobox has it Content bound to ArrayControler with “adPublication” as Model Key Path.

    property parent : class "NSObject"
property combobox : missing value
    property theData : {}
property theArrayController : missing value
    property AdPublication : missing value


on applicationWillFinishLaunching_(aNotification)
    set theFile to (choose file with prompt "Choose tab-delimited file:")
	set theEntries to paragraphs of (read theFile as text)
	repeat with anEntry in theEntries
        set theValues to (tabbedText's componentsSeparatedByString_(tab)) as list
        set AdPublication to item 1 of theValues
        tell theArrayController to addObject_(AdPublication)
        end repeat

end applicationWillFinishLaunching_

Rather than trying to add entries one at a time, build a list of all of them and just “set my AdPublication to theList”.

Thanks Shane for your answer -

But after spending the week end trying to get a list of lists out of my file, I have to ask for some help:

I have a tab-delimited text file that contains: AdPublication, AdSPBLeedW, AdSPBleedH, AdSPTrimW, AdSPTrimH, AdSPSafeW, AdSPSafeH, AdDPSBLeedW, AdDPSBleedH, AdDPSTrimW, AdDPSTrimH, AdDPSSafeW, AdDPSSafeH, AdUnit

These are specs for single page ads and spread ads, with unit of choice, for a number of publications.

Ultimately I’d like to get a combobox automaticaly populated by the publication names, and, upon selection within combobox, 6 text fields will be displaying the specs of the chosen pub.

You are advising me to build a list of lists “ but I can’t seem to get it right. I have tried many permutations and combed through your book and tutorials - to no avail. Many times I thought I got it - but then failed at getting the list.

I feel like i am close “ but would need a kick start … any help would be appreciated!

Thanks!

I may be misunderstanding, but I suspect you really want a popup menu rather than a combobox. A combobox is a special kind of text field, which is a different thing – you can’t sue it in bindings like I think you’re trying.

Thank you Shane,

Ah! I have then been chasing for months for something than is not doable in the first place. Popup menu is fine.

From the get go I have tried with combobox because I wanted to eventually give the user the ability to type the first letters of the Publication so that they could choose to the right Publication faster…
If I have to give that up in order to finally move on, I guess I’ll do that.

You could probably do it with a combobox, but not via bindings – you’d need to write an action handler to do the work.

Thanks Shane - I’ll try that.
Actually it might be better to stick to comboBox as I forgot to mention the important fact that the name of the Publication needs to be editable as the user will need to be able to manually enter information if the Publication they are looking for is not in the list.

But in the meantime I am still having trouble defining the list of lists.
I have found many methods calling for list of lists so I am really confused right now “ to the point where I am just writing what must be non-sense.

The way I understand it is:

  • Upon launch my application will open a tab-delimited file.
    “ It will read the document and needs to understand that it is looking at a list separated by paragraphs then by tabs.
    Each tab representing a value.
  • It will create a list of lists. (at this point it knows what each item of the sub-list in every item of the list is.)
  • it will give this list of lists to theData
  • the array controller is feeding its information from theData
  • I can now link the combo box’s values to: adPublication of the ArrayControllers’ Data.

Now I have done this:

property parent : class "NSObject"
property comboBoxFile : ((path to desktop folder) as text) & "Exported tab-delimited text.txt"
property theData: {}
property theArrayControler : missing value


on applicationWillFinishLaunching_(aNotification)
    
   set oldDelims to AppleScript's text item delimiters
    set AppleScript's text item delimiters to {tab}
    
    repeat with anItem in paragraphs of current application's (read file comboBoxFile)
         set theList to {{"adPublication"}, {"adSPBW"}, {"adSPBH"}, {"adSPTW"}, {"adSPTH"}, {"adSPSW"}, {"adSPSH"}, {"adDPSBW"}, {"adDPSBH"}, {"adDPSTW"}, {"adDPSTH"}, {"adDPSSW"}, {"adDPSSH"}, {"adUnit"}}
        set theData to theList
        
    end repeat

    set AppleScript's text item delimiters to oldDelims

end applicationWillFinishLaunching_

The problem is, of course, that when I do all this and bind my ArrayControler to theData and then bind the combobox to “adPublication” from the ArrayControler, I get errors…

I think I get the logic right - but it is obvious that the programming is really wrong. Am I on the wring track?

No, you don’t want a list of lists – you want a list of records.

allright. I am totally lost.
I am trying to follow your advise, and here is what I have come up with:

on applicationWillFinishLaunching_(aNotification)
    set AdPublication to {}
    set AdSPBLeedW to {}
    set AdSPBleedH to {}
    set AdSPTrimW to {}
    set AdSPTrimH to {}
    set AdSPSafeW to {}
    set AdSPSafeH to {}
    set AdDPSBleedW to {}
    set AdDPSBleedH to {}
    set AdDPSTrimW to {}
    set AdDPSTrimH to {}
    set AdDPSSafeW to {}
    set AdDPSSafeH to {}
    set newPubUnit to {}
    set theFile to (choose file with prompt "Choose tab-delimited file:")
    set theValues to paragraphs of (read theFile as text)
    set oldDelims to AppleScript's text item delimiters
    set AppleScript's text item delimiters to {tab}
    
    
    repeat with anEntry in theValues
    set {adPub, adSPBW, adSPBH, adSPTW, adSPTH, adSPSW, adSPSH, adDPSBW, adDPSBH, adDPSTW, adDPSTH, adDPSSW, adDPSSH, adUnit} to text items of anEntry
    
        setAdPublication_(adPub)
        setAdSPBLeedW_(adSPBW)
        setAdSPBleedH_(adSPBH)
        setAdSPTrimW_(adSPTW)
        setAdSPTrimH_(adSPTH)
        setAdSPSafeW_(adSPSW)
        setAdSPSafeH_(adSPSH)
        setAdDPSBleedW_(adDPSBW)
        setAdDPSBleedH_(adDPSBH)
        setAdDPSTrimW_(adDPSTW)
        setAdDPSTrimH_(adDPSTH)
        setAdDPSSafeW_(adDPSSW)
        setAdDPSSafeH_(adDPSSH)
        setnewPubUnit_(adUnit)
    
        end repeat

Of course, this is not working and returning an error that unrecognized selector sent to object “SetAdPublication”

there must some simpler more direct way to set this up “ any suggestion ?

If you’re going to persist with a combobox, you can’t easily use an array controller bindings. What you could do is something like this:

property jobNames:{}
property jobData:{}
property oneJob:{}

    on applicationWillFinishLaunching_(aNotification)
    set theNames to {}
    set theData to {}
        set theFile to (choose file with prompt "Choose tab-delimited file:")
        set theValues to paragraphs of (read theFile as text)
        set oldDelims to AppleScript's text item delimiters
        set AppleScript's text item delimiters to {tab}
        repeat with anEntry in theValues
        set {adPub, adSPBW, adSPBH, adSPTW, adSPTH, adSPSW, adSPSH, adDPSBW, adDPSBH, adDPSTW, adDPSTH, adDPSSW, adDPSSH, adUnit} to text items of anEntry
set end of my jobNames to adPub
set end of my jobData to {adSPBW:adSPBW, adSPBH:adSPBH, ...} -- make a record
end repeat
end applicationWillFinishLaunching_ 

    on comboSelection_(sender) -- connected to combobox
        set theName to sender's stringValue() as text
        repeat with i from 1 to count of jobNames
            if item i of jobNames = theName then
                set my oneJob to item i of jobData
                exit repeat
                end if
            end repeat
        -- handle new value here if not found in jobNames
    end comboSelection_

You could then bind the combobox to jobNames directly in the app delegate (Model Key Path of self), and the other fields directly to oneJob with Model Key Paths matching the record labels.

This is all much harder than it needs to be. Throw away your combobox and use a popup menu; you can give it an extra entry, Custom, to allow people to add a custom value. Use this code:

script SMSAppDelegate
	property parent : class "NSObject"
	property theData : {}
	property ac : missing value -- the array controller
	
	on applicationWillFinishLaunching_(aNotification)
        -- Build your data as a list of records here.
        -- Add a final entry called Custom that the user can select to set manual values
        -- They should all include an extra property, isCustom, which is false except for the Custom entry
        -- These are manual values for testing:
		set my theData to {{Value1:"Number 1", Value2:1, Value3:10, isCustom:false}, {Value1:"Number 2", Value2:2, Value3:20, isCustom:false}, {Value1:"Number 3", Value2:3, Value3:30, isCustom:false}, {Value1:"Custom", Value2:0, Value3:0, isCustom:true}}
	end applicationWillFinishLaunching_
	
	on logIt_(sender) -- connected to button for testing
		log ac's selectedObjects()
	end logIt_
	
end script

Now in the interface have a popup, an array controller, and a text field for each field you want to display (including one for the name that appears in the menu; we will make it visible only when Custom is selected).

Select the array controller and bind its Content Array to the app delegate with a Model Key Path of theData. Also connect the ac property of the app delegate to it as an outlet. Add a button connected to the logIt_ action handler for testing.

Select the popup and make three bindings:

  • Bind its Content to the array controller with a Controller Key of arrangedObjects, no MKP. (This is the records themselves.)

  • Bind Content Values the same, except with an MKP of Value1 (or whatever label you used for the title). This provides the menu item names.

  • Bind the Selection Index (yes, index) to the array controller with a Controller Key of selectionIndex and no MKP.

Now select the text fields in turn, binding their Value to the array controller with a Controller Key of selection and an MKP that matches the label used in the records. Bind their Editable property to the array controller with a Controller Key of selection and MKP of isCustom. This means they can only be modified when the user selects Custom.

For the text field that contains the name of the chosen menu item, bind the Hidden property rather than Editable, and set the Value Transformer to NSNegateBoolean. That will hide it unless Custom is chosen.

Less code, more bindings goodness…

Thanks for your interest in my case - I am still trying to wrap my head around your previous answer.
I got my combobox to display the publication name list without any problem. But i can’t seem to be able to program a way to have a text box content automatically populated by any data corresponding to the chosen publication.

Bellow is the code as is right now (barely different from what you submitted):

property parent : class "NSObject"
property Thecombobox: missing value
property jobNames:{}
property jobData:{}
property oneJob:{}
property textfieldcontent : missing value

on applicationWillFinishLaunching_(aNotification)
    set theNames to {}
    set theData to {}
    set theFile to (choose file with prompt "Choose tab-delimited file:")
    set theValues to paragraphs of (read theFile as text)
    set oldDelims to AppleScript's text item delimiters
    set AppleScript's text item delimiters to {tab}
    repeat with anEntry in theValues
        set {adPub, adSPBW, adSPBH, adSPTW, adSPTH, adSPSW, adSPSH, adDPSBW, adDPSBH, adDPSTW, adDPSTH, adDPSSW, adDPSSH, adUnit} to text items of anEntry
        set end of my jobNames to adPub
        set end of my jobData to {adSPBW: adSPBW, adSPBH: adSPBH, adSPTW: adSPTW, adSPTH: adSPTH, adSPSW: adSPSW, adSPSH: adSPSH, adDPSBW: adDPSBW, adDPSBH: adDPSBH, adDPSTW: adDPSTW, adDPSTH: adDPSTH, adDPSSW: adDPSSW, adDPSSH: adDPSSH, adUnit: adUnit}
    end repeat
    
    tell TheComboBox
        addItemsWithObjectValues_(jobNames)
        setStringValue_("PUBLICATIONS") -- put something in the text field if desired
    end tell

    
end applicationWillFinishLaunching_


on comboSelection_(sender) -- connected to combobox
    set theName to sender's stringValue() as text
    repeat with i from 1 to count of jobNames
        if item i of jobNames = theName then
            set my oneJob to item i of jobData
            exit repeat
        end if
    end repeat
end comboSelection_


tell textfieldcontent
    addItemsWithObjectValues_(adSPBW)
end tell

Now you seem to suggest in your latest post that combobox are really not a good idea for what I am trying to do …

The idea it that when it is loaded in the program upon launch, whatever information retreived from this list has to be editable. For example, one should be able to select “ELLE” magazine, with the size data retreived automatically in a bunch of text fields or forms (I have forms for the moment in my UI)““ but those data must be editable (one should be able to ad “FRANCE” at the end of the publication Name for example, or change any number in the specs fields, without getting any interference from the program)

For this reason, I felt that combobox was a must… What are your thoughts? I am ok to go to popup if I have to - but my goal is to get something entirely editable.

Just because you want the values editable doesn’t mean a combobox is a good choice. You want to start off with a set of options – that’s what a menu can provide. You can then have every item a chosen menu item represents appear in individual text fields, and you can let users edit them to their heart’s content. The popup just becomes a way of setting initial values.

A menu gives the user a set of choices. A combobox is a text field; the entries are just hints for what it might be filled with, they aren’t choices. From the app’s perspective, it’s no more helpful than an ordinary text field.

Please tell me if I am wrong but I believe that what you are advising me to do would result in redundant information:
Let say I have a popup menu and I choose from it “ my choice would automatically populate some editable text fields “ like publication name, sizes, etc.
I would end up with a popup menu with my originally chosen Publication name, and then another the text field containing the same or an edited version of the name, and then all the information - is that right?

But I have to say that the exact reason I moved my program from AS to ASOC is that I am trying to avoid redundant information.
I have an Applescript version of my program that I have been using for two years now.

I believe what you are advising me is a variation of what I already have in AS:
When programing it, I ran quickly into 2 problems:

  • I had to build the list of Publication sizes internally “ I was unable to dynamically link to an external list.
  • I had to make a 2 stage program: stage one gives a window with a combobox containing a set list of publications that AS has a record of plus a blank field.
    Stage two presents a window with editable text fields of Publication name and corresponding sizes (already filled-up with the chosen publication, or empty when “custom” is chosen)

My naive expectation with ASOC was to be able to go beyond the limitations of AS and enter a world where everything is programable…

To go back to your comments in relation with my project:

  • Combobox is not impossible but complex
  • If I am going the combobox way, array controller is not applicable, all the bindings need to happen in written program

You propable are growing tired of my ignorance “ and I understand you might be loosing patience. So if you want to give up the thread I won’t hold a grudge :slight_smile: I am really thankfull for the feedback you have provided so far.

The only redundant information would be an editable text field containing the title of the menu item. Make a change, and the menu item title would change accordingly.

There’s nothing to stop you from adding a Load button to update the data.

Why not put them in the same window? You probably don’t need custom if you want everything to be editable. The user chooses ELLE, all the fields fill in with Elle’s values, including a title field. They can then change the title to ELLE-FR, or change some other value – you’re just providing the initial values for them. (You’d probably also need some kind of reset button).

Mostly that’s true. I’m just trying to suggest a technically simpler way, given that you are having problems.

Right. You’re going to have to get the value in the combobox, look up the matching values in your data, if any, and put the results in all the test fields. Most of that is pretty much straight AS.

Binding contents of a text-field can’t work with a combobox because the value entered in the combobox may not have a matching entry in your data. So you have to call setStringValue_ on every text field you want to change.

My suggestion: you make up a dummy program as I outlined earlier, without the isCustom stuff, and play with it. See what it does, and think about how you might use something like it.

Thanks for all your input Shane “ I will work on it a little bit and see what I can come up with.
I feel like this is an important subject to talk about - I have been intensely combing the net in search of similar conversation and I cannot find much “ of course every situation is unique in a way “ but this is way harder than I thought!

Baby steps!

I have been able to move forward with my script thanks to your feedback.
I now completely understand what you meant about the combobox not being the simple choice :slight_smile:

In your script about using the combo box, you get to a point where “OneJob” is a list extracted from the Master list, defined by the choice made in the combobox itself.
I am wondering if this is a list that can be attached to an array controller “ wouldn’t this simplify things a bit at the bindings level?

More importantly, I am looking for a way to tell the text fields attached to the combobox to “stop updating content” if the selected text in the combobox has been updated AND any of the fields has been updated … the command could also be “update the text fields only once, unless another existing value has been selected from the combo box”

Right now, my problem is that i can select a Publication, get its corresponding values in various text fields and edit these values. This works really well. But if after editing, I decide to go back to the combo box and update the Publication name, or if I cycle through the combobox by accident, the values in the text fields reset automatically to the OneJob value… I understand why it does that, but I am curious to know if there is a way to avoid it. I thought that maybe using an array controller bound to Onejob could help solve this problem…

:slight_smile:

If I understand correctly, that shouldn’t happen. Are you sure your text fields have their Value bound to the array controller with a controller key of selection and a model key path of the relevant field label? Such bindings should be two-way, and do what you want.

My problem might be that I am not using an array controller at all, but coded binding.

I have tried many times to bind the array controller to the list “ and I guess the closest I got was to get it to understand what to pick, through IB bindings (app controller, MKP of oneJob) but it gives me this error:

Cannot create NSArray from object {
adDPSBH = “11.125”;
adDPSBW = “16.25”;
adDPSSH = “10.375”;
adDPSSW = “15.5”;
adDPSTH = “10.875”;
adDPSTW = 16;
adSPBH = “11.125”;
adSPBW = “8.25”;
adSPSH = “10.375”;
adSPSW = “7.5”;
adSPTH = “10.875”;
adSPTW = 8;
adUnit = in;
} of class __NSCFDictionary

See my earlier posting of how to do it with bindings – it has the full details. Just leave out the custom stuff.