Make variables readable by different scripts

Hi,

I have three scripts {“A”, “B”, “C”} in the AppDelegate.applescript, where B and C control two different DropBoxes and A controls the rest of the UI. Basically when an item is dropped on the DropBoxes they return the path of the item dropped, setting respectively the URL in the variables theItem1 and theItem2.

I want to make those variables readable by the script A.

I tried with this:


global theItem1
global theItem2

script A
         property parent : class "NSObject"

         property theItem1 : missing value
         property theItem2 : missing value


         on applicationWillFinishLaunching_(aNotification)
                  my dropBox1's registerForDraggedTypes_({"public.file-url"})
                  my dropBox2's registerForDraggedTypes_({"public.file-url"})
         end applicationWillFinishLaunching_

end script

script B
    property parent : class "NSBox"
	
    property theItem1 : missing value
    
    on draggingEntered_(sender)
        log "entered"
        set pb to sender's draggingPasteboard()
        set theOptions to {NSPasteboardURLReadingFileURLsOnlyKey:1}
        return pb's canReadObjectForClasses_options_({current application's |NSURL|}, theOptions)
    end draggingEntered_
    
    on performDragOperation_(sender)
        log "perform"
        -- Get the file paths
        set pb to sender's draggingPasteboard()
        set theOptions to {NSPasteboardURLReadingFileURLsOnlyKey:1}
        set theItem1 to pb's readObjectsForClasses_options_({current application's |NSURL|}, theOptions)
        return true
    end performDragOperation_
end script

script C
    property parent : class "NSBox"

    property theItem2 : missing value
    
    on draggingEntered_(sender)
        log "entered"
        set pb to sender's draggingPasteboard()
        set theOptions to {NSPasteboardURLReadingFileURLsOnlyKey:1}
        return pb's canReadObjectForClasses_options_({current application's |NSURL|}, theOptions)
    end draggingEntered_
    
    on performDragOperation_(sender)
        log "perform"
        -- Get the file paths
        set pb to sender's draggingPasteboard()
        set theOptions to {NSPasteboardURLReadingFileURLsOnlyKey:1}
        set theItem2 to pb's readObjectsForClasses_options_({current application's |NSURL|}, theOptions)
        return true
    end performDragOperation_
end script

But when I try to get the variables by the script A I get this:


2014-08-31 19:32:35.314 App Name[3203:303] (null)
2014-08-31 19:32:35.314 App Name[3203:303] (null)

Where am I wrong?

You can’t use script objects and globals like that in an Xcode project.

When you define “script A”, what happens is that when the script loads, a new Objective-C class is created, called A, and set up so that it has methods that match and call your code. The same thing will happen with script B and script C – you will end up with three unrelated classes. And because you have effectively deleted AppDelegate.applescript, your links to the delegate in the interface will also be broken.

The fact that B and C are both subclasses on NSBox is a bit confusing – you probably only need one subclass, which you can use for multiple boxes.

To communicate between the classes, you will need either to make outlets in the interface, or perhaps talk to the App delegate class. Either way you need to use method calls, like theItem() and setTheItem_().

The problem is that if I use only one subclass how can I set a variable different for each drop box?

And I don’t understand if it is better whether I create new files called “B.applescript” and “C.applescript” or just leave them as they are? Namely all in a file?

You use separate outlets for them in your app delegate script. You only need one class file for multiple instances.

Let’s say you have a handler in your app delegate like this:

	on wasDropped_sender_(itemDropped, sender)
		log itemDropped
		if sender = dropBox1 then -- where dropBox1 is a property/outlet to a subclassed box
			log "dropBox1"
                else
                     -- whatever
		end if
	end wasDropped:sender:

And then in your box subclass you call:

current application’s NSApp’s delegate()'s wasDropped_sender_(theItem1, me)

I prefer them separate, just because it becomes easier if the file gets too big. But functionally it’s the same.

What you shouldn’t do, though, is give them names like A or B. Leave the appDelegate what it was originally, and use descriptive names like MyBoxSubclass or MyDropBox for others.

I haven’t given them names like those, I wrote so just for example…
Anyway I tried your handler and code looks like this:


script AppDelegate
	property parent : class "NSObject"
	
	-- IBOutlets
	property window : missing value
    property dropBox1 : missing value
    property dropBox2 : missing value
	
	on applicationWillFinishLaunching_(aNotification)
        my dropBox1's registerForDraggedTypes_({"public.file-url"})
        my dropBox2's registerForDraggedTypes_({"public.file-url"})
	end applicationWillFinishLaunching_
	
    on wasDropped_sender_(itemDropped, sender)
        log itemDropped
        if sender = dropBox1 then 
            log "dropBox1"
        else
            log "dropBox2"
        end if
    end wasDropped_sender_
    
	on applicationShouldTerminate_(sender)
		-- Insert code here to do any housekeeping before your application quits 
		return current application's NSTerminateNow
	end applicationShouldTerminate_
	
end script



script MSDropBox
	property parent : class "NSBox"
    property theItem1 : missing value
    
    on draggingEntered_(sender)
        log "entered"
        set pb to sender's draggingPasteboard()
        set theOptions to {NSPasteboardURLReadingFileURLsOnlyKey:1}
        return pb's canReadObjectForClasses_options_({current application's |NSURL|}, theOptions)
    end draggingEntered_
    
    on performDragOperation_(sender)
        log "perform"
        -- Get the file paths
        set pb to sender's draggingPasteboard()
        set theOptions to {NSPasteboardURLReadingFileURLsOnlyKey:1}
        set theItem1 to pb's readObjectsForClasses_options_({current application's |NSURL|}, theOptions)
        log theItem1
        log sender
        current application's NSApp's delegate()'s wasDropped_sender_(theItem1, me)
        return true
    end performDragOperation_
	
end script

But doesn’t work (probably I put the handler in the wrong place).
The subclass doesn’t recognize the sender, in fact the App Delegate returns always “dropBox2”.

I get this:
2014-09-01 09:06:06.233 Test[748:303] entered
2014-09-01 09:06:07.423 Test[748:303] perform
2014-09-01 09:06:07.423 Test[748:303] (
“file:///Users/Matto/Documents/Home/”
)
2014-09-01 09:06:07.424 Test[748:303] NSDraggingInfo: draggingSequenceNumber=0x6c000000 draggingDestinationWindow=<NSWindow: 0x6000001ff900> draggingSourceOperationMask=0x37 draggingLocation={136.03515625, 284.7265625} draggingPasteboard=<NSPasteboard: 0x6000000b2120>
2014-09-01 09:06:07.424 Test[748:303] (
“file:///Users/Matto/Documents/Home/”
)
2014-09-01 09:06:07.425 Test[748:303] dropBox2

Change the handler to use:

 if sender's title() as text = "Box 1" then

And change the title accordingly.

Worked!! Thank you Shane!!

Last question:

How can I coerce the file path as text?
Because If I try to coerce it as text I get “Can’t make «class ocid» id «data optr00000000B0BB040000600000» into type text. (error -1700)”

You don’t have a path – you have an array of NSURLs. So you need to call path() on each of them, and coerce them using “as text”.

I tried with:


set theItem1 to theItem1's path()

But doesn’t work; How I call path()?

theItem1 is an array.

I still don’t understand how to coerce them…

You can either coerce the array to a list, and then get the path of each item, or you can use valueForKey:“path”, and coerce the result to a list after.

OK thanks, now I understood. :wink:

Hi Shane,
today, checking that everything was working well, I noticed that one thing doesn’t.
I have this:


on wasDropped_sender_(itemDropped, sender)
        log itemDropped
        if sender's title() as text = "Box1" then
            log "dropBox1"
            set theItem1 to itemDropped as text
            set theItem1 to theItem1 & "/"
            log theItem1
            set theItem1 to posix file theItem1 --this gives an error
            log theItem1
            tell application "Finder" to set labelSource to label index of theItem1 --this doesn't work because requires an alias
        else
            log "dropBox2"
            set theItem2 to itemDropped as text
        end if
    end wasDropped_sender_

I need to convert that text as alias, in order to make the Finder working with the label.
The first error is <AppDelegate @0x60000022ffc0: OSAID(8) ComponentInstance(0x810000)>
and the second is *** -[AppDelegate wasDropped:sender:]: Can’t get «class labi» of class “NSObject”. (error -1728)

How can I do that?

If you read my book, you’d save yourself a lot of time. Just sayin’.

You can’t use certain terms like POSIX file like that – you have to use as POSIX file.

But why bother with all that, when you can use NSURL?

	set anNSURL to current application's |NSURL|'s fileURLWithPath:theItem1
	set {theResult, labelSource} to anNSURL's getResourceValue:(reference) forKey:NSURLLabelNumberKey |error|:(missing value)

And you only need half that code because you threw the NSURL away in the first place.

Thank you!

I haven’t bought your book so far because I’m italian and i’m afraid that I’m buying a book I can’t understand…
However I’m gonna try this and give you some feedback.

Si, capisco. I’m sure it must be much harder doing this stuff for non-English-speakers. But for what it’s worth, the book does come with a lot of code, which is the same in any language (although again, it must be hard when the method names are often meaningless).

And you’re free to ask questions about the book here, too. Maybe Stefano will step in and answer.

Sooner or later I’ll buy that book (Even if it’ll get AppleScriptSwft Explored - I hope).

Anyway I tried your script and worked as always!
Thanks again!