Drag and drop questions

Hi Everybody!
I have 2 drag and drop questions I’m hoping someone can help me with.

  1. I have an application with a simple gui interface consisting of a box, a text view and a cancel button. The user drops files onto the box and then a “do shell script” runs in the background for what could be a long period of time. For some reason, after files are dropped onto the box, the finder locks up for a few seconds until I see a transparent image of the files shoot back from the box to the files in the finder. Does anyone know what is going on? I’m using an “on drop” handler in the script.

  2. The “on drop” handler contains a loop that runs until the background process is complete. Am I correct in assuming that as long as the “on drop” handler is busy, that the user is unable to do anymore “drag and drop”? That seems to be the behavior I’m observing (I only have the one object accepting drops), though it appears that an “on clicked” handler from the cancel button can interrupt the loop.

Thanks in advance for any assistance.

-Mike

Hi Mike,

I recently had almost the same poblem. A Drag&Drop application as frontend for a shell script (a lengthy conversion task). Like yours the GUI was blocked until the shell script was finished and no further drag&drop was possible in between. (since AppleScript runs synchonous and waits for the end of each command until it executes the next one).

I solved like so:

I redirect the output of my shell script to /dev/null - so now AppleScript no longer waits for the shell script.
But now I need to find out from my AppleScript when she shell script is finished - to avoid starting parallel shell script tasks (don’t know if this is a problem in your case?). My solution for this is simple: I use a small text file as external variable. It has three possible values:
At program start it is set to “idle”, Just before starting the shell script it is set to “running” and from the shell script i write “done” (as last command line of the shell script).
The drop handler I only use to add it’s contents to an array. Then I used an on idle loop which regularly checks the contents of this array. If there is something to process, it reads the hard disk variable to synchronize both processes (main application and shell script). If it is “idle” or “done” the next shell script is started, if it is “running” it waits.

As I wrote above - since it was a lenghty task, it wasn’t time critical in my case and I didn’t care about writing to disk - if it was in yours, it might be possible to use the memory to hold the variable instead.

If you need the output of the shell script for further processing in your application, then you can’t redirect to /dev/null/ of course. You should write to a temp file instead.

D.

Hi Dominik!
Thanks for the response. The “do shell script” is not the problem. I’ve already got the process running in the background outputing to a text file. I then read from the growing text file in a loop (inside the drop handler) so I can display the output (% completion information) periodically in a text view.

I’ve narrowed down the problem to the loop in the drop handler. If, for example, I run the following with a window (“main”), a box (“dropBox”) to drop the files in, and a text view (“tv”):


on awake from nib theObject
	tell theObject to register drag types {"file names"}
end awake from nib

on drop theObject drag info dragInfo
	set preferred type of pasteboard of dragInfo to "file names"
	repeat with i from 1 to 30
		delay 1
		set contents of text view "tv" of scroll view "tv" of the window of theObject to i as string
	end repeat
	return true
end drop

the script itself works fine, displaying a counter in the text view for 30 seconds, but the finder freezes right after th drop even though I can access other running applications. After about 20 seconds, the file icons that I dropped onto theObject snap back to their finder locations and the finder works again. It’s as if the drop handler won’t release the files until it’s done or some timeout is exceeded.

I’ve tried putting the loop in the “on conclude drop” handler but I get the same response. I’ve used “ignore application resonses” which was suggested in a previous thread I found in my searches, but again no luck. I tried a demo application from John8’s website (“Dropper”) and it behaved the same way when I dropped a folder with a lot of files in it. I’d be curious if others get the same delay.

Obviously the above example isn’t realistic, but one of the benfits of “drag and drop” is the ease of handling multiple files, and these would typically be processed in a repeat loop. The solution would seem to be to put the loop somewhere else, maybe an idle handler, which to be honest I haven’t had much experience with. This seems like an akward work around, however. Perhaps the “timeout” before the files snap back to the finder can somehow be changed.

Hopefully I’m not being dense here and missing something obvious, but any feedback or ideas would be appreciated.

-Mike

P.S. Just to share so I’m not always leeching at this site, here is an alternative method to determine if a background process from a “do shell script” is running. Maybe this will be usefull to someone (unless of course someone finds a problem with the code). It uses the unix command “ps -p pid” where pid is a process id. If the process is running, the command returns a small table with the pid in it. If the process is not running, then the retruned table is empty.

This starts the process in the background, writes output to a temporary file, and saves the process id of the process.

set procID to do shell script "your process " &> " & "tempfile" & " & echo $!"

the this checks to see if the process id is in the table returned as text by the “ps -p pid” command. Use it to check periodically if the background process is still running.


on processRunning(processID)
	set checkID to do shell script "ps -p " & processID
	if (offset of processID in checkID) is equal to 0 then
		return false
	else
		return true
	end if
end processRunning

Hello Mike,

the annoying ‘freeze effect’ is because you are doing the whole processing in the on drop handler. Then of course the handler waits for the execution of these processing commands until they are done (-> freezing effect) . Here an example (based on your script example above) how to separate the processing from on drop:

global sthDropped

on awake from nib theObject
   tell theObject to register drag types {"file names"}
   set sthDropped to false
end awake from nib

on drop theObject drag info dragInfo
	set preferred type of pasteboard of dragInfo to "file names"
	set sthDropped to true
	return true
end drop

...

on idle theObject
	if sthDropped then
		repeat with i from 1 to 30
			delay 1
 		      set contents of text view "tv" of scroll view "tv" of the window of theObject to i as string
		end repeat
		set sthDropped to false
	end if
	return 1
end idle

Now the the drop handler is finished instantly after dropping and the processing is done in the on idle handler. There should be no freeze anymore …

Hope this helps?

D.

Hi Dominik!
It’s kind of funny. I finally got around to doing some research on idle handlers and I came up with a script that is very similar to your suggestion. It’s nice to get confirmation that I atleast have some idea of what I’m doing :). I take it that returning every second to check an if statement in the idle handler isn’t going to bog down the application.

For completeness and for newbies like me who might be reading this thread in the future, it seemed to me that the next thing I needed to do was to make sure that no more “drops” are accepted while the repeat loop in the idle handler does its thing. This seemed to me to be the purpose of the “prepare drop” handler so I added the following code:

on prepare drop theObject drag info dragInfo
	if sthDropped is false then
		return true
	else
		return false
	end if
end prepare drop

Unfortunately, this didn’t work. It seemed that while the repeat loop was active, the prepare drop handler and drop handler wouldn’t be called correctly.

The solution that seems to work is to add:

tell box "db" of window "main" to register drag types {}

just before the repeat loop in the idle handler. This in effect turns off access to the box I used for dropping. Then add:

tell box "db" of window "main" to register drag types {"file names"}

to again allow drops of type “file names”.

Thanks again for your time and effort Dominik.

-Mike

I thought I had this licked and now another problem related, I believe, to the repeat loop in the idle handler has cropped up. My repeat loop writes the log of a “do shell script” process in a text veiw. For numerous files, the text view eventually needs scroll bars to view the text. However, clicking on the downward scroll button as the loop in the idle handler runs will often resart the repeat loop in the idle handler.

Here is a sample script:


global sthDropped

on awake from nib theObject
	tell box "db" of window "main" to register drag types {"file names"}
	set sthDropped to false
end awake from nib

on drop theObject drag info dragInfo
	set preferred type of pasteboard of dragInfo to "file names"
	set sthDropped to true
	return true
end drop

on idle theObject
	if sthDropped is true then
		tell box "db" of window "main" to register drag types {}
		repeat with i from 1 to 200
			delay 0.5
			set oldContent to contents of text view "tv" of scroll view "tv" of the window of theObject
			set contents of text view "tv" of scroll view "tv" of the window of theObject to (oldContent & return & i as string)
		end repeat
		tell box "db" of window "main" to register drag types {"file names"}
		set sthDropped to false
	end if
	return 1
end idle

This modified version of the code from previous posts will display a series of lines “1, 2, 3, 4, …” in a text view. This works without a problem unless the down scroll button is pressed to see the last line of the text view. The repeat loop starts all over again and the run log displays the following:

2006-08-02 16:08:01.180 drag testing[3085] *** Assertion failure in +[NSEvent startPeriodicEventsAfterDelay:withPeriod:], AppKit.subproj/NSEvent.m:1446
2006-08-02 16:08:01.192 drag testing[3085] *** NSTimer discarding exception ‘Periodic events are already being generated’ that raised during firing of timer with target 3dc7b0 and selector ‘idleUsingTimer:’

Anyone have and ideas? I’m beginning to wonder if there is another solution to my original problem that I should be considering. Thanks in advance for any responce.

-Mike

Hi Mike,

the problem is the repeat loop in the on idle handler. (I thought this repeat loop was only an example for a lengthy task).
Here an example how to solve the same job without a repeat (and without this scrolling error):

D.

property sthDropped : false
property idleInterval : 1.0
property dropcounter : 0
property endValue : 200

on idle theObject
	if sthDropped is true then
		if dropcounter = 0 then
			set idleInterval to 0.5
			tell box "db" of window "main" to register drag types {}
			set dropcounter to dropcounter + 1
		else if dropcounter ≤ endValue then
			set oldContent to contents of text view "tv" of scroll view "tv" of the window of theObject
			set contents of text view "tv" of scroll view "tv" of the window of theObject to (oldContent & return & dropcounter as string)
			set dropcounter to dropcounter + 1
		else if dropcounter > endValue then
			tell box "db" of window "main" to register drag types {"file names"}
			set sthDropped to false
			set idleInterval to 1
			set dropcounter to 0
		end if
	end if
	return idleInterval
end idle

Dominik-
Thanks again for your help. I don’t know why I never thought of using the idle handler itself for my progress loop. I guess I was so focused on using the repeat loop I had originally written.

I have a couple of conceptual questions for you if you don’t mind. There are many aspects of applescript studio that I’m still trying to understand.

  1. I was under the impression that the idle interval had to be an integer value. I tried using 0.5 like your example, and though it ran, the interval seemed still be 1 second. I can live with one second, but I’m just wondering for future reference.

  2. I’ve been confused about the use of “property” in applescript studio (I noticed you changed from using global to property). From what I understand, property values don’t persist in applescript studio scripts like they do in script editor scripts. Is there some other advantage to using properties that I’m unaware of?

Thanks!

-Mike

my mistake, sorry - I guess you are absolutely right …

hmm - I used property here as a kind of predefined global (that’s the advantage - it saves the code in awake from nib). This might be considered as lazy/bad style scripting - I don’t know - but i think I have seen it in Apple’s example scripts as well … and it works as far as I can say from my experience.

Dominik-
After a lot of hair pulling I just now finished recoding my script to incorporate your suggestion. I just wanted to let you know that it now works like a charm, and your idea even seems to have solved issues I was having with cancelling and quitting while the do shell script process was running. Thanks again for your time and effort.

-Mike