Let Enhanced Applet process Clipboard

Is there a way for a droplet to call the open handler by pressing Cmd+V and pass the clipboard content (files or text) to it. Currently, I’m watching the clipboard content with an idle handler which does not work well.

Not a good way, but there is a crufty way.

You can set the idle for a short time, like five seconds. Then every five seconds the script can look at the clipboard, figure out if it’s got what it’s looking for and if so execute. If not just wait another five seconds.

Thanks, that’s what I’m actually doing, but it’s clumsy. Idle will show repeatedly the Droplet window and it often can’t be closed with the mouse.

As it is possible to overwrite menu commands, I thought someone can tell me, how to call the open handler. I tried something like this:

-- …

on run
	set appMenu to (current application's NSApp's mainMenu()'s itemAtIndex:2)'s submenu()
	set menuItem to appMenu's itemAtIndex:5
	menuItem's setTitle:"Paste"
	menuItem's setAction:"useClipboard:"
	menuItem's setTarget:me
end run

-- …

on useClipboard:sender
	if current application's NSThread's isMainThread() then
		my performUseClipboard:(missing value)
	else
		its performSelectorOnMainThread:"performUseClipboard:" withObject:(missing value) waitUntilDone:true
	end if
end useClipboard:

on performUseClipboard:sender
	open (the clipboard)
end performUseClipboard: 

In this context it seems not to be possible to call the open handler.

Well, I don’t understand what the code does, I just use it. :grin:

Try this:

on performUseClipboard:sender
	set clipboardContents to theClipboard
	open clipboardContents
end performUseClipboard:

Thanks, I think you’re speaking of “the clipboard” instead of “theClipboard”, right?

Well, the problem is, that my open handler is displaying an alert dialog. If I use open from performUseClipboard, the dialog is not shown. Also the Applet can’t show error messages when I call open from a menubar command.

When calling the open handler from idle, it works as expected.

Interestingly, this lets the open handler show dialogs:

on performUseClipboard:sender
	run script "set clipboardContents to the clipboard
	open clipboardContents"
end performUseClipboard:

But then the Applet seems to crash and I have to force quit it.

You’ll need to show us more of your script for us to help.

To make things easy, here’s a simplified version:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
use framework "Foundation"

on run
	initialize()
end run

on initialize()
	if name of current application is name of me then
		set appMenu to (current application's NSApp's mainMenu()'s itemAtIndex:2)'s submenu()
		set menuItem to appMenu's itemAtIndex:5
		menuItem's setTitle:"Paste into Droplet"
		menuItem's setAction:"pasteClipboard:"
		menuItem's setTarget:me
		
		set appMenu to (current application's NSApp's mainMenu()'s itemAtIndex:2)'s submenu()
		set menuItem to (current application's NSMenuItem's alloc()'s initWithTitle:"Paste with “run script”" action:"alternativePasteClipboard:" keyEquivalent:"V")
		menuItem's setTarget:me
		appMenu's insertItem:menuItem atIndex:6
	end if
end initialize

on open theFiles
	beep
	display alert "You've pasted/dropped:" message (item 1 of theFiles as string)
	beep
end open

on pasteClipboard:sender
	if current application's NSThread's isMainThread() then
		my performpasteClipboard:(missing value)
	else
		its performSelectorOnMainThread:"performpasteClipboard:" withObject:(missing value) waitUntilDone:true
	end if
end pasteClipboard:

on performpasteClipboard:sender
	set clipboardContents to the clipboard
	open clipboardContents
end performpasteClipboard:

on alternativePasteClipboard:sender
	if current application's NSThread's isMainThread() then
		my performalternativePasteClipboard:(missing value)
	else
		its performSelectorOnMainThread:"performalternativePasteClipboard:" withObject:(missing value) waitUntilDone:true
	end if
end alternativePasteClipboard:

on performalternativePasteClipboard:sender
	run script "set clipboardContents to the clipboard
	open clipboardContents"
end performalternativePasteClipboard:

Save this as an Enhanced Applet with the option “stay open”.

When you drop a file into its window, you’ll hear a beep, see an alert with the filename and after pressing OK, you’ll hear a second beep.

Then try this:

  • Copy a file from Finder into the clipboard
  • Open the Droplet
  • Perform: Edit > Paste
  • Result:
    • You’ll hear only one beep
    • the code stops at “display alert”
    • the Edit menu does not reset its highlight color, so it seems display alert caused a crash.

I’ve also integrated the alternative way:

  • Copy a file from Finder into the clipboard
  • Open the Droplet
  • Perform: Edit > Paste with “run script”
  • Result:
    • You’ll hear only one beep.
    • “display alert” is executed and shown
    • The whole Applet is now frozen, you can’t press OK.

At some point you need to see what format and class the clipboard is that you’re sending to the open handler.

The variable: “theFiles” must be a list, and the list should consist of text or something automatically coerced into text by the display dialog command.

If you’re not using Script Debugger, add a “log theFiles” command at the start of your open handler, and run from script editor using the clipboard. This should tell you what’s going on.

The clipboard type doesn‘t matter. It even does not work with a simple handler with display alert "test".

Look at this sample. I selected two files in the Finder and did a command-c.

Then ran this appleScript.

The clipboard is text with two paragraphs, each with the name of one of the files.

When I run the script below I get too alerts. One from the run handler and one from the open handler.

The run handler alert has both files. The open handler alert only has the first file.

set myFiles to the clipboard
display alert myFiles
open myFiles

on open myFiles
	set myFiles to paragraphs of myFiles
	display alert item 1 of myFiles
end open

if I change the open handler to this the two alerts are identical:

on open myFiles
	display alert myFiles
end open

Thanks Ed for your investigations. The clipboard handling is not of interest for me at this point. First I would like to know, how to call the open (or any other) handler from a menu command, so it behaves normally like in standard AppleScript with dialogs and real error messages. It seems that calling handlers from a menu command are running in a different context. I want the normal context.

Currently, my solution is to open a new instance of the App with “open -n” from shell and then quit the currently running process. But that looks like a bad hack and the behavior in the dock is irritating.

Maybe the Enhanced Applet could be enhanced(SIC!) with a pasteToWindow handler or something like that. That would be great and I don’t have to deal with workarounds and bugs (enhanced applet doesn’t like Idle handlers very much :wink:).

Inspired by the docs, I’ve tried some stuff. When I use performSelectorInBackground: I can call the open handler which is then be able to use display alert. But now the progress bar does not work show up in the applet window. Any ideas?

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
use framework "Foundation"

on run
	initialize()
end run

on initialize()
	if name of current application is name of me then
		set appMenu to (current application's NSApp's mainMenu()'s itemAtIndex:2)'s submenu()
		set menuItem to appMenu's itemAtIndex:5
		menuItem's setTitle:"Paste 'Mail.app' into Droplet"
		menuItem's setAction:"pasteClipboard:"
		menuItem's setTarget:me
		
	end if
end initialize

on open theFiles
	beep
	set progress total steps to -1
	set progress description to "Description text"
	display alert "You've pasted:" message (item 1 of theFiles as string)
	beep
end open

on pasteClipboard:sender
	if current application's NSThread's isMainThread() then
		my performpasteClipboard:(missing value)
	else
		its performSelectorOnMainThread:"performpasteClipboard:" withObject:(missing value) waitUntilDone:true
	end if
end pasteClipboard:

on performpasteClipboard:sender
	my performSelectorInBackground:"backgroundHandler:" withObject:(missing value)
end performpasteClipboard:

on backgroundHandler:sender
	open {"Macintosh HD:System:Applications:Mail.app"}
end backgroundHandler:

When I change the last handler to this, it seems to work:

on backgroundHandler:sender
	run script "open {\"Macintosh HD:System:Applications:Mail.app\"}"
end backgroundHandler: