Make this script Faster?

Greetings. I am writing a simple drag/drop app that recieves one file, and changes it’s file type and creator codes. Nothing fancy, no other windows, dialogs or other processes. I accept both a drop on the icon of the app (which works flawlessly) and on an image view in a window (this is the problem code). The problem isn’t that it does not do it’s job… it modifies the codes. The problem is that it takes FOREVER! :cry: It slowly does it’s job while the rainbow twirls for probably 20-30 seconds, and then the app crashes…

Here’s the code…

on drop theObject drag info dragInfo
	set dataTypes to types of pasteboard of dragInfo
	if "file names" is in dataTypes then
		set preferred type of pasteboard of dragInfo to "file names"
		set droppedFile to contents of pasteboard of dragInfo as string
		set droppedSpec to (POSIX file droppedFile) as file specification
		set preferred type of pasteboard of dragInfo to ""

		tell application "Finder"
			set file type of droppedSpec to "TEXT"
			set creator type of droppedSpec to "????"
		end tell
	end if
	return true
end drop

As I said it works, it just takes forever and seems to be putting an awful strain on the finder/CPU. I’ve tried a number of combinations with similar or lesser results. I assumed that once I got the path to the file into the posix format (as it comes natively from the on open event from the drop onto the app’s icon) that the same code I use for the icon drop would work for the drag/drop event. This does not seem to be the case, though, as using the same code produces these poor results. I’ve trimmed some of the meat out to troubleshoot and weed out the problem code, and the above seems to be it. Any thoughts or edits to make this work are appreciated.

Thanks in advance…
j

I have modified code from an application of mine without testing the mods. But I see two differences to your code that may well be significant. The contents of the pasteboard is a list of files, even if it is a list of one file. Secondly I don’t think that you need to coerce the result of POSIX file to file specification, I think the result of POSIX file is recognized by Finder as a file specification or alias.

If there are any probs with the code try “copy (POSIX file thePath) to theFile” instead of “set theFile to (POSIX file thePath)”

using terms from application "Xcode" 
     set dataTypes to types of pasteboard of dragInfo 
     if "file names" is in dataTypes then 
          set preferred type of pasteboard of dragInfo to "file names" 
          set thePaths to contents of pasteboard of dragInfo 
          set preferred type of pasteboard of dragInfo to "" 
          repeat with thePath in thePaths 
               set theFile to (POSIX file thePath) 
               tell application "Finder" 
                    set file type of theFile to "TEXT" 
                    set creator type of theFile to "????" 
               end tell 
          end repeat 
     end if 
end using terms from

Kevin

If I use your code I get a "Stack Overflow error (-2706). If I modify it using your recommendation (copy…) or other things I try, I get the same results as my original code… Spin and Crash… :x

Also, I have had no luck using anything but a “as file specification” path. If I try anything else I get “Bad file name…” or “Can’t get class of…” errors.
j

This is untested, but I think it ought to work–the key is adding “as alias” to the end of the “set theFile to POSIX file thePath” line.

Also, you must turn the POSIX path of the file into the colon-separated version before you can have the Finder deal with it. [EDIT]–my bad, I thought ktam was saying that you didn’t have to coerce the POSIX version of the path to a POSIX file-version before handing it off to the Finder. What ktam said is right.

using terms from application "Xcode" 
     on drop theObject drag info dragInfo 
          set dataTypes to types of pasteboard of dragInfo 
          if "file names" is in dataTypes then 
               set preferred type of pasteboard of dragInfo to "file names" 
               set thePaths to contents of pasteboard of dragInfo 
               set preferred type of pasteboard of dragInfo to "" 
               repeat with thePath in thePaths 
                    set theFile to POSIX file thePath as alias 
                    tell application "Finder" 
                         set file type of theFile to "TEXT" 
                         set creator type of theFile to "????" 
                    end tell 
               end repeat 
          end if 
          return true 
     end drop 
end using terms from

Hope this helps…

I think we are perhaps on the wrong track of what the problem is.

I would try returning false, rather than true in your on drop handler. Otherwise the interface object that has had the file dropped on it will try and open the file, but in the meantime the file information has been modified by your code. Returning false will tell the object not to open the file. If you want to have the file opened then it is probably best to find a work around, like storing the file spec, and in your code add a idle handler that checks for the a list of specs that it has to open, or something along those lines.

I know my very similar code works, but I am not modifying the file information which makes me think that must be related to the problem.

Good Luck.
Kevin

Thanks for your input so far guys, but this problem is still not any closer to being resolved. The problem lies with the Finder understanding the path to the file. I have a test text field set up which displays the path that the finder is trying to act on, and it is EXACTLY the same whether I drop a file on the app icon or the image view in the window. I use the same exact code to modify the file codes. I have checked and re-checked and I cannot find a combination that will yield good results. I have used so many combinations of your and my codes that I am literally banging my head. If I comment out the…

set file type of droppedSpec to "TEXT"
set creator type of droppedSpec to "????" 

…in the tell Finder statement it returns no errors, otherwise I get a stack overflow error or a crash, still. For some reason it just doesn’t feel that the path is the same the image-drop way as the icon-drop way, no matter what coercion scheme I use.

Please continue to give feedback, even though this is prbably frustrating you now, too.
j

I’m actually having the same problem myself, now that I’ve got to that point in my project.

It processes the files but seems to take for ever. Also, I think I’ve seen the icon representing the dragged-on files actually “snap” back to its original position, as if it got rejected.

I’m using an image in an NSImageView inside of an NSBox, inside of the main window. Like an image that says “Drag your fonts here” (and an icon of my app).

Also, while I’m only trying to process fonts, if I happen to drag a PDF or other image file onto the target, it loads that inside of the NSImageView, replacing mine! Any idea how to prevent that from happening? The “editable” attribute of the NSImageView is unchecked, only “enabled” is checked. Or is this what you were talking about with the “return true” or “return false” stuff? I’ve just got in the habit of adding the return false to anything that has to do with dragging and dropping because of that bug that causes a crash, maybe that’s only on the “on open” handler, I’m not sure.

Hey jobu, what is the object that you’re dropping your files onto? Maybe it’s a problem with using NSImageView?

Hey guys, thought everyone forgot about me :slight_smile:

I am working on a slightly older version of ASS (not xCode), but it would seem that this problem is an established one, because now that I think about it, I have heard of this problem arising for others in the past. I am using an image view that is enabled and not editable. The files I am dragging into it are just plain text files. My app is a utility that adds the resource codes I need to files that have been extracted from .zip archives (zipping files strips the resource fork from all files). The program this utility is for requires the codes to determine if it is a file type it can open. Most of the original files are created on pc anyways, so they never have a resource fork to begin with, but zipping them removes the resource fork if they did have it. As I said, I have no problem with the icon-drop code, only the image view drop. I have tried the same code with various other objects, and had the same results, so I doubt it is a problem with the image view.

I have tried using the terminal to do a setfile command but I cannot seem to get it to work, even from the command line. For some reason, it tells me that setfile is not a command. I’m no pro at using the command line though, so if anyone has a snippet on how to properly call the setfile to change file/creator types using a do shell… or whatever, please post it here, as it seems some of us could use it.

I am not sure I believe that it is exclusively a problem with the file type/kind issue, as I can delete that line and leave the creator type line and still get the same problem. So far it appears to be a bug with changing any resource codes via a drop event. I just tried setting the modification date of the file with the same results. For now I’m going to save a copy of my script for when apple DOES support it or I find a way, and pitch the drop event altogether so I can get the first version out. It’s a freeware utility anyways, so beggars can’t be choosers…right? :wink:

Take care, guys…
j

I think you’ve got the bug a little wrong. For me, anyway, it doesn’t apply to any instances of “file type” that are inside of a “tell application “Finder”” block. For those, file type remains unchanged. It’s when I try to use the “file type” of the “info for” record that the problem occurs.

I just ran into that that problem the other day, but I found a way around it, so I know my code is correct. The workaround is to compile your script, then replace all instances of “file kind” with “«class asty»” (using the Find and Replace panel) and then immediately save the script. Then build your app, and your code will be correct.

That “file kind” error produces an “NSBlahBlah” error message, I forget the number. In the case of my app, there are no errors, it’s just that it takes forever to process.

Jobu, have you specified the full path to the SetFile command? it should be “/Developer/Tools/SetFile”. I don’t think you can just do a “do shell script “SetFile -whateverOptions /path/to/file”” unless you change some UNIX shell settings to include that command in your commands.

I wonder if the problem with the script taking so long is caused by the script, or the “on drop” handler in particular, taking so long before it returns the “true” or “false” value, which is supposed to inform the app whether the drop should be accepted or denied.

While Apple’s sample “Drag and Drop” application seems to do okay, it really only deals with a single string of text or a few files.

In my app, I’m expecting to be able to drop a folder with a few hundred fonts in it onto the drop “target” and have that processed. But doing so would probably take a good 30 seconds or more. So, the way it is now, my app is sitting there for 30 seconds trying and trying to figure out whether it should accept the file or folder that was dropped onto it, and never finding that out until all is said and done.

I wonder if using a very simple “on drop” handler to simply determine whether the dropped objects should be accepted or denied, and then adding the majority of the processing code to an “on conclude drop” handler might help things to proceed a bit more smoothly.

For example, something like the following:

using terms from application "Xcode" 
     on drop theObject drag info dragInfo 
          set dropped_ to false 
          set dataTypes to types of pasteboard of dragInfo 
          if "file names" is in dataTypes then 
               set dropped_ to true 
          end if 
          return dropped_ 
     end drop 
end using terms from 

using terms from application "Xcode" 
     on conclude drop theObject drag info dragInfo 
          set preferred type of pasteboard of dragInfo to "file names" 
          set thePaths to contents of pasteboard of dragInfo 
          set preferred type of pasteboard of dragInfo to "" 
          repeat with thePath in thePaths 
               set theFile to POSIX file thePath as alias 
               tell application "Finder" 
                    set file type of theFile to "TEXT" 
                    set creator type of theFile to "????" 
               end tell 
          end repeat 
     end conclude drop 
end using terms from

Hope this helps–I’ll know better for sure in a little bit after I’ve had a chance to test it more fully…

Well, I tried recoding my app to use the code I theorized, but it doesn’t seem to process it any faster or smoother. And the dropped files or folders still seem to “snap back” to their original position.

Current build is: dfontifier2.sitx (.sitx, ~876 KB) (has sample fonts so you don’t have to mess around with any of your own).

Not sure what else to try. Might try extending some of Apple’s examples so that they’d process more files and see if that cause the same behavior.

I think I am getting close to figuring out a workaround to this problem. The problem, from what I can tell after some experimentation, is that the drag and drop feature is not well supported in applescript. It has some serious memory issues. What is happening when your drop something on an object, is that it is for whatever reason demanding a lot of system resources throughout the duration of the drop handler (i.e. everything you have between ‘on drop’ and ‘end drop’). The errors and crashes we’ve been getting are caused by an overload of the system. I’m sure you’ve found that removing any ‘tell application’ statements solves the crashes, because telling another application to do something is just too much to do, with the added overhead of having your app caught in a fatal loop. Unfortunately, removing the tell statement leads to losing the mechanism for doing your processing :cry:

The ‘solution’ I have found, does require an extra step on the part of the user, but the way I’m doing it now makes it pretty easy. First, I have switched to a rounded bevel button instead of an image view to drop objects onto. You can still use an image view if you want, but a button is nice because it also allows you to customize the picture in the “Drop Zone” while giving you an added bonus which I’ll describe later. What you use does not matter, the code works either way, but you’ll see why the button is helpful. Remove the ‘troublesome code’ from the drop handler. The next part is easy and relatively attractive for me because I only accept one item at a time, but if you have MANY items, you’ll probably want to find another way. I set up a visible text field in the window that displays the list of files that were dropped. In my case, I just get the first item and ignore any extras, but this method will save a list too. In your on drop handler, retrieve the files list, and then set the list to the contents the text field…

on drop theObject drag info dragInfo
	set dataTypes to types of pasteboard of dragInfo
	if "file names" is in dataTypes then
		set preferred type of pasteboard of dragInfo to "file names"
		set thePaths to contents of pasteboard of dragInfo

		-- For just the first file
		set thePath to (item 1 of thePaths)
		set theFile to thePath as POSIX file
		set preferred type of pasteboard of dragInfo to ""
		set contents of text field "text" of window "test" to theFile as string

		-- OR --

		-- For the whole list (Untested, but should be something like this)
		-- set contents of text field "text" of window "test" to thePaths as list
	end if
end drop

If you’re using a huge list, you may want to make the text field invisible and stick it off to the side somewhere.

Now you’ll need a button to trigger the ‘troublesome code’, and this is where using the button for the drop zone really shines. Place the ‘troublesome code’ in an on clicked handler, and point your drop zone button at it. After you drop the files, you’ll just need to click…one easy little extra step.

on clicked theObject
	if name of theObject is "dropButton" then
		set theFile to ((contents of text field "text" of window "test") as string) as file specification
		tell application "Finder"
			set file type of theFile to "TEXT"
			set creator type of theFile to "TEST"
		end tell
	end if
end clicked

As you can see, I am changing the file codes to the ones I want. You can replace the “tell finder” statement with whatever code you’d like…‘troublesome’ or not. Set a variable to the contents of the text field we created, and then have your way with the value of that variable. If your using a list of files, you’ll need to adjust the “set theFile…” line to extract the list rather than a string. Assuming your code is good, when you click the button the code is executed with no problems, errors, or twirling rainbows. :lol:

Some other thoughts…
If you’re not using the button, you’ll have to set up one, perhaps just off to the side. You could also use a menu/popup button, a checkbox, etc…anything that can receive a click or similar change of state. I would recommend alerting the user somehow that the drop was only the first step, and that they need to click something to finish the process. Whether you enable/make visible a button, change an image or text field value, etc, you should make it clear that just dropping does not do the whole job.

I have been trying to find a way to get the code in the on clicked handler to execute automatically, but any reference I can think of that is made to it from inside the on drop handler waits until it is executed before exiting the drop handler, which just puts us back where we started. If anyone can suggest a solid method of calling a second handler without waiting for a return before exiting the current handler, it would probably solve this problem for us.

Hope this makes sens and helps you too…
j

jobu,

I don’t really think the problem is with the way AppleScript (or AppleScript Studio) handles drag and drop, but how Finder is a terribly inefficient process. In doing some experimentation with your code, this works nearly instantaneously on my system:

on drop the_object drag info drag_info 
     set data_types to types of pasteboard of drag_info 
     if "file names" is in data_types then 
          set dropped_items to {} 
          set preferred type of pasteboard of drag_info to localized string "file names" 
          set dropped_items to contents of pasteboard of drag_info 
     end if 
     my update_dropped_items(dropped_items) 
     set preferred type of pasteboard of drag_info to "" 
     return true 
end drop 

on conclude drop the_object drag info drag_info 
     --do nothing 
end conclude drop 

on update_dropped_items(dropped_items) 
     repeat with the_item in dropped_items 
          try 
               set the_item to (contents of the_item as string) 
               log ("updating " & the_item) 
               set the_item to ((the_item as POSIX file) as alias) 
               ignoring application responses 
                    tell application "Finder" 
                         set file type of the_item to "TEXT" 
                         set creator type of the_item to "????" 
                    end tell 
               end ignoring 
               log ("finished updating " & the_item) 
          on error the_error 
               log (the_error) 
          end try 
     end repeat 
end update_dropped_items

If you guys want (what I think) is a nice implementation of drag and drop, see my demo app “dropper”:

http://homepage.mac.com/jonn8/as/dist/dropper.hqx

Jon

Hmm, I swear the code I posted above with the ‘tell app “Finder”’ code worked fine and then I had to restart and now I’m getting errors. I have a work around that always works (still using the Finder) but does result in a several second delay even with the “ignoring” block. This is a puzzler. I’m still working…

Still, the “dropper” demo app I posted is relatively speedy mostly because it doesn’t use the Finder.

Jon

OK, there is something screwy in the communication between AS Studio and the Finder. I’ve worked around it by using a run script. You can see the code in action here:

http://homepage.mac.com/jonn8/as/dist/Drop_File_Typer.hqx

Jon

While I haven’t taken a chance to completely read through your assessment, I guess I still think my initial theory had some truth to it.

I’ve done a lot of experimentation and watching what’s going on in Terminal (with ‘top’), Activity Viewer, etc., and the script (or my app) doesn’t actually take up all that many/much system resources. Allocated memory holds steady at roughly 20 - 30 MB throughout the entire (dropped-on-target) process. The memory management behavior is identical to that for when I drop the items onto the application’s icon or open them through the Open dialog.

The only noticeable difference in behavior I can see is the initial “hang” in the application from the moment I finish dropping the items onto the target , until the application actually starts “processing” those items. During that time, its %CPU usage is absolutely 0, and Process Viewer regards it as “Hung”. Eventually, the application will suddenly kick into gear and process the items. And as I’ve mentioned before, the files still seem to “snap back” to their original location. That doesn’t make a whole lot of sense to me, as I have returned true, almost immediately.

As usual, you’ve helped again, Jon. It’s the little things that you always catch that make all the difference. Thanks a million. I found a really quick, no overhead way that works for me and is as instantaneous as my other code. It was the ‘ignoring application responses’ code that I was missing. I created a subroutine as a home for my mod code, and then called it in the ignoring statement. I’ve only tried it with 2 files, but this at least shows it works with multiple files. It’s very stripped down, so anyone using it will have to add in error handling and such. Since I know there would be no problems with the files I was dropping on it, I just left out the extras to show the workhorse…

on drop theObject drag info dragInfo
	set dataTypes to types of pasteboard of dragInfo
	if "file names" is in dataTypes then
		set preferred type of pasteboard of dragInfo to "file names"
		set thePaths to contents of pasteboard of dragInfo
		set preferred type of pasteboard of dragInfo to ""
		
		ignoring application responses -- Added this (VERY important)...
			my doModification(thePaths)
		end ignoring
	end if
end drop

on doModification(thePaths)
	repeat with thePath in thePaths
		set theFile to (POSIX file thePath as string) as file specification
		tell application "Finder"
			set file type of theFile to "TEXT"
			set creator type of theFile to "JOBU"
		end tell
	end repeat
	return true
end doModification

I don’t know why we’re seeing the dead CPU time after dropping. This seems wierd to me. I still don’t understand why it eventually works, but takes so long…I can only attribute this problem to the drop handler and how it’s interpretted by the os at the most basic levels. When I get a Stack Overflow error, I think the os for whatever reason perceives the drop as a recursive process and creates an infinite loop of data being added to the memory stack. You are right though, I didn’t really think about it, but CPU usage probably does not show much change when allocating memory to the stack because it’s only processing the same application code over and over. I think it’s a memory problem not a CPU problem. There would be a transparent CPU usage, while a blatant memory problem placing so much data into the ‘stack’. Regardless of the source of the errors, it would seem that we are at, or EXTREMELY near the end of this quest. I hope you meet with the same success I have, using my code or some combination of the code posted above.

For me, I think I’ve found my solution, so everyone…thanks for your input. :smiley:
j

Jon, Thanks so much for your help.

Can I make one suggestion about that “Drop_File_Typer”? :smiley:

Maybe put a text field above the File Type and Creator Code row that says something to the effect of “Change the File Type and Creator for dropped files to:”.

Or maybe I’m just a moron, lol. It took me a couple of PDF and application files to figure out that it was setting the info rather than telling me the info (for the dropped file).

I guess I missed the ‘r’ at the end of Typer.

:lol:

Yeah, I should have been more clear about what that one does, especially since it modifies files. It was late last night (early this morning?) and I just pounded it out quickly. While I think “Drop File Typer” is a decent demo app, I really like the other app I mentioned in this thread, and I think it is the far more useful bit of code (since this thread grew quickly, the post toward the bottom of the first page where I mentioned this may have been overlooked):

http://homepage.mac.com/jonn8/as/dist/dropper.hqx

Jon

I’m in the midst of revamping my code to use the Finder as little as possible.

I still don’t quite understand why the sluggishness of my app when I drop files onto the NSImageView rather than its icon or selecting them through the File->Open menu is caused by the Finder being an inefficient process.

My application has 3 ways that you can initiate the processing of a list of files: first is by dropping them onto its icon (using an “on open” handler), second is by choosing them through the File->Open command (using an “on choose menu item” handler), and third, by dropping them onto an NSImageView in the window of my application (using an “on drop” handler). All 3 methods use the same subroutines to process the items inside, and many of those subroutines use the Finder during processing. Yet it’s only the “on drop” method that causes the sluggishness.

Also, I’m not that familiar with using the “ignoring application responses” command. If, in my “on drop” handler, I simply try wrapping the several subroutines I call inside of an “ignoring application responses” block, I get errors about things not being defined.

I guess I understand how it’d be okay to use a “ignoring application responses” block around a “tell application “Finder”” block if all I’m doing with the Finder is setting the File Type and Creator Code of a file, but what about if I’m asking it to get something and return it to me?

For example, one of the first subroutines my app calls is the following, to get the entire list of files that it’s going to process (so that I can accurately make use of a progress bar):

on get_all_items_(files_) 
     set fonts_ to {} 
     repeat with file_ in files_ 
          set file_info_ to info for file_ 
          if (alias of file_info_ is false) and (folder of file_info_ is true) then 
               set folder_items_ to process_folder_(file_) 
               repeat with folder_item_ in folder_items_ 
                    set end of fonts_ to (folder_item_ as alias) 
               end repeat 
          else if (alias of file_info_ is false) then 
               set end of fonts_ to (file_ as alias) 
          end if 
     end repeat 
     return fonts_ 
end get_all_items_ 

on process_folder_(folder_) 
     tell application "Finder" 
          set folder_items_ to (items of (entire contents of folder_) whose name is not ".DS_Store" and kind is not "Folder" and kind is not "alias") 
     end tell 
     return folder_items_ 
end process_folder_

Any ideas? Thanks in advance…