Clipping script

(You need to install Satimage plugin for applescript to run this script)
Why does this script not work in TextEdit?¨
Secondly, why does it sometimes(always, in case of Firefox) throw a beep though it creates the clipping properly and also shows the Growl notification?
Lastly, it does not work properly if the selected text is less than 31 characters (which must be a result of my poor scripting knowledge)


tell application "System Events"
	delay 0.3
	keystroke "c" using command down
end tell

--get the clipboard
delay 0.2
set theClipboard to the clipboard as text
try
	if the number of characters of theClipboard is less than 32 then
		set theLimit to the number of characters
	else if the number of characters of theClipboard is greater than 31 then
		set theLimit to 31
	end if
on error
	set theLimit to 31
end try
set textString to characters 1 thru theLimit of theClipboard as string
set the textString to replaceCharsName(textString, "/", "-")
set the textString to replaceCharsName(textString, ":", ".")
on replaceCharsName(thisText, searchString, replacementString)
	set AppleScript's text item delimiters to the searchString
	set the itemList to every text item of thisText
	set AppleScript's text item delimiters to the replacementString
	set thisText to the itemList as string
	set AppleScript's text item delimiters to ""
	set theName to thisText as text
end replaceCharsName

property dragdata : «data drag01000000000000000000000000000700747874750000000100000000000000006C797473000000010000000000000000545845540000000100000000000000003866747500000001000000000000000020465452000000010000000000000000363174750000000100000000000000006C747375000000010000000000000000»


tell application "Finder" to set F to (make new clipping at desktop to the textString) as string


put resource (the clipboard as Unicode text) to file F type "utxt" index 256
try
	put resource (the clipboard as «class RTF ») to file F type "RTF " index 256
end try
put resource dragdata to file F type "drag" index 128

tell application "GrowlHelperApp"
	set the allNotificationsList to {"Clipping Notifier"}
	
	set the enabledNotificationsList to {"Clipping Notifier"}
	
	register as application "Clipping Notifier" all notifications allNotificationsList default notifications enabledNotificationsList icon of application "Finder"
	
	notify with name "Clipping Notifier" title "Clipping Notifier" description textString application name "Clipping Notifier"
end tell

Hi, Chris.

Yep. In that case, you’ve got ‘set theLimit to the number of characters’, without specifying characters of what. This causes an error, which is caught by your ‘try’ block and so theLimit gets set to 31. This in turn causes another error when you try to set textString in the following line. The whole section might look better like this:

try
	set textString to (the clipboard as text)
	if ((count textString) is greater than 31) then set textString to text 1 thru 31 of textString
on error errMsg
	display dialog errMsg buttons "Abort" default button 1 with icon stop
	error number -128 -- Stop the script.
end try

Thank you, Nigel.

I got carried away with the natural english-like behaviour of Applescript and failed to spot the mistake myself.
I incorporated the changes you suggested. Just to make sure that i have done it properly, I am pasting the script again.

tell application "System Events"
	delay 0.3
	keystroke "c" using command down
end tell
delay 0.2
try
	set textString to (the clipboard as text)
	if ((count textString) is greater than 31) then set textString to text 1 thru 31 of textString
on error errMsg
	display dialog errMsg buttons "Abort" default button 1 with icon stop
	error number -128 -- Stop the script.
end try

set the textString to replaceCharsName(textString, "/", "-")
.........

The script now works in TextEdit.

However, the beeping problem continues.
I removed all the try blocks. I cannot understand why Script Editor does not show any error in the “Result” pane.
Also, if i use this script by selecting some text in Script Editor, it only beeps for the first time and never beeps again.
With TextEdit or Firefox, it always beeps.

I’ve been comparing your hard-coded “drag” data with that produced when I drag clippings from documents in the normal way. This is one of my results (plain text dragged from a TextEdit document):

«data drag000000010000000000000000000000025445585400000100000000000000000075747874000001000000000000000000»

. which seems to break down like this:

«data drag
00000001000000000000000000000002Big-endian integer 1, big-endian short integer 2 (“2 other resources”).
54455854000001000000000000000000 – “TEXT” in hex, big-endian short integer 256 (resource ID)?
75747874000001000000000000000000 – “utxt”, 256.
»

A clipping from an RTF document has "RTF ", “utxt”, “ustl”, “TEXT”, “styl”, and “drag” resources, the “drag” data working analogously to the above. (A line ending with 5 and data relating to 5 other resources.)

The same analysis applied to your hard-coded data gives:

«data drag
01000000000000000000000000000700Little-endian integer 1, little-endian short integer 7 (“7 other resources”).
74787475000000010000000000000000 – “txtu” (“utxt” backwards) in hex, little-endian short integer 256?
6C797473000000010000000000000000 – “lyts”, 256.
54584554000000010000000000000000 – “TXET”, 256.
38667475000000010000000000000000 – “8tfu”, 256.
20465452000000010000000000000000 – " FTR", 256.
36317475000000010000000000000000 – “61tu”, 256.
6C747375000000010000000000000000 – “ltsu”, 256.
»

It seems that the drag data are supposed to refer to the other resources (and clippings where they don’t don’t work on my Jaguar machine). They’re also apparently endian, in the style of the processor (Intel or PPC) used to create them. I imagine that clippings made on a PPC machine are viable on an Intel and vice versa, but I don’t have an Intel machine to test this.

The script below creates functioning text clippings under Jaguar and Tiger on PPC machines. I’d be interested to know how it fares with an Intel processor:

on clipboardText2Clipping()
	set clipboardRec to (the clipboard as record)
	
	set tokenList to {}
	try
		set theText to Unicode text of clipboardRec
		tell application "Finder" to set clippingFile to (make new clipping at desktop to theText) as alias
		set clippingExists to true
		set end of tokenList to doResource(theText, "utxt", clippingFile)
	on error
		set clippingExists to false
	end try
	try
		set theText to string of clipboardRec
		if (not clippingExists) then tell application "Finder" to set clippingFile to (make new clipping at desktop to theText) as alias
		set end of tokenList to doResource(theText, "TEXT", clippingFile)
	end try
	try
		set end of tokenList to doResource(«class utf8» of clipboardRec, "utf8", clippingFile)
	end try
	try
		set end of tokenList to doResource(«class ut16» of clipboardRec, "ut16", clippingFile)
	end try
	try
		set end of tokenList to doResource(«class RTF » of clipboardRec, "RTF ", clippingFile)
	end try
	try
		set end of tokenList to doResource(uniform styles of clipboardRec, "ustl", clippingFile)
	end try
	try
		set end of tokenList to doResource(styled Clipboard text of clipboardRec, "styl", clippingFile)
	end try
	
	put resource makeDragData(tokenList) type "drag" index 128 to clippingFile -- Requires Satimage OSAX.
end clipboardText2Clipping

on doResource(theResource, theType, clippingFile)
	put resource theResource type theType index 256 to clippingFile -- Requires Satimage OSAX.
	
	set theToken to 0
	if ((system attribute "ascv") div 256 mod 16 < 2) then
		-- AppleScript versions before 2.0.
		repeat with thisChar in theType
			set theToken to theToken * 256 + (ASCII number thisChar)
		end repeat
	else
		-- AppleScript 2.0 and above. (Gleaned from Release Notes. Hope it's OK!)
		repeat with thisID in id of theType
			set theToken to theToken * 256 + thisID
		end repeat
	end if
	
	return theToken
end doResource

on makeDragData(tokenList)
	set fRef to (open for access file ((path to temporary items as Unicode text) & "Drag data.dat") with write permission)
	try
		set eof fRef to 0
		-- Write the first 16 bytes of "drag" data.
		write 1 as integer to fRef
		write 0 as double integer to fRef
		write 0 as short to fRef
		write (count tokenList) as short to fRef
		-- Write the 16 bytes referring to each of the other resources.
		repeat with thisToken in tokenList
			write thisToken as integer to fRef
			write 0 as short to fRef
			write 256 as short to fRef
			write 0 as double integer to fRef
		end repeat
		-- Read back the complete "drag" data.
		set dragData to (read fRef from 1 as «class drag»)
	end try
	close access fRef
	
	return dragData
end makeDragData

tell application "System Events" to keystroke "c" using {command down}
delay 0.2
clipboardText2Clipping()

Thanks again to both of you.
To Jacques,
(I used Butler for setting a hotkey)
Both your scripts solve my beeping problem in TextEdit(and all other apps that i tested) but the script does not even work with Firefox.(probably something to do with Firefox’s applescriptability). I need it to work with Firefox more than anywhere else. :frowning: :frowning:

To Nigel,
I get this error (on Mac OS 10.5.6, Intel):

and the word “integer” is highlighted on this line (in the “on makeDragData(tokenList)” handler):

To Jacques,
Sorry, the scripts you gave earlier work in Firefox also. I must have made some mistake while testing it earlier. Or may be it has something to do with the delay which I may consider adding if I have the problem again.

Hi, Chris.

Thanks for the feedback. Both ‘as short’ and ‘as short integer’ are accepted on earlier systems. Maybe ‘integer’ is a superfluity here that’s no longer tolerated. I’ve changed all instances to ‘as short’ above.

To Nigel,

The clipping is created correctly but I get the error:

You may want to look at this thread: http://tr.im/jARg

I don’t know how I can change the drag property in your script.

edited post above

Right. I see. It sounds as if my script’s writing the drag data in PPC order even on your machine, so you’re getting the error again. I wondered if that might happen, so I’ve been working on another version today that plugs in the correct data for the processor detected. I’ve used a ‘system attribute’ call to work out the processor ” that being the fastest way ” but I’m taking it on trust that it works on an Intel machine!

The commented-out code at the bottom is a method for making the previously frontmost app frontmost again when the script’s run from Script Menu. It activates the Finder to knock System Events off the frontmost slot, then hides the Finder and unhides it again, leaving the GUI target frontmost. (This isn’t a great method if you actually want to GUI the Finder…)

on clipboardText2Clipping()
	if ((system attribute "sysa") < 3) then -- Big-endian.
		set dataSegments to {line1:«data rdat00000001000000000000000000000000», Unicode text:«data rdat75747874000001000000000000000000», string:«data rdat54455854000001000000000000000000», «class utf8»:«data rdat75746638000001000000000000000000», «class ut16»:«data rdat75743136000001000000000000000000», «class RTF »:«data rdat52544620000001000000000000000000», uniform styles:«data rdat7573746C000001000000000000000000», styled Clipboard text:«data rdat7374796C000001000000000000000000»}
	else -- Little-endian (Intel).
		set dataSegments to {line1:«data rdat01000000000000000000000000000000», Unicode text:«data rdat74787475000000010000000000000000», string:«data rdat54584554000000010000000000000000», «class utf8»:«data rdat38667475000000010000000000000000», «class ut16»:«data rdat36317475000000010000000000000000», «class RTF »:«data rdat20465452000000010000000000000000», uniform styles:«data rdat6C747375000000010000000000000000», styled Clipboard text:«data rdat6C797473000000010000000000000000»}
	end if
	
	set clipboardRec to (the clipboard as record)
	
	set dataList to {line1 of dataSegments}
	try
		set theText to Unicode text of clipboardRec
		set clippingFile to makeClipping(theText)
		set clippingExists to true
		doResource(theText, "utxt", clippingFile)
		set end of dataList to Unicode text of dataSegments
	on error
		set clippingExists to false
	end try
	
	try
		set theText to string of clipboardRec
		if (not clippingExists) then set clippingFile to makeClipping(theText)
		doResource(theText, "TEXT", clippingFile)
		set end of dataList to string of dataSegments
	end try
	try
		doResource(«class utf8» of clipboardRec, "utf8", clippingFile)
		set end of dataList to «class utf8» of dataSegments
	end try
	try
		doResource(«class ut16» of clipboardRec, "ut16", clippingFile)
		set end of dataList to «class ut16» of dataSegments
	end try
	try
		doResource(«class RTF » of clipboardRec, "RTF ", clippingFile)
		set end of dataList to «class RTF » of dataSegments
	end try
	try
		doResource(uniform styles of clipboardRec, "ustl", clippingFile)
		set end of dataList to uniform styles of dataSegments
	end try
	try
		doResource(styled Clipboard text of clipboardRec, "styl", clippingFile)
		set end of dataList to styled Clipboard text of dataSegments
	end try
	
	put resource makeDragData(dataList) type "drag" index 128 to clippingFile -- Requires Satimage OSAX.
end clipboardText2Clipping

on makeClipping(theText)
	if ((count theText) > 31) then set theText to text 1 thru 31 of theText
	repeat with i from (count theText) to 1 by -1
		if (character i of theText is not space) then exit repeat
	end repeat
	set theText to text 1 thru i of theText
	tell application "Finder" to return (make new clipping at desktop to theText) as alias
end makeClipping

on doResource(theResource, theType, clippingFile)
	put resource theResource type theType index 256 to clippingFile -- Requires Satimage OSAX.
end doResource

on makeDragData(dataList)
	set fRef to (open for access file ((path to temporary items as Unicode text) & "Drag data.dat") with write permission)
	try
		set eof fRef to 0
		-- Write the collected data sequentially to the temporary file.
		repeat with thisData in dataList
			write contents of thisData to fRef
		end repeat
		-- Overwrite the 15th or 16th byte of the file with the number of "other resources".
		set n to (count dataList) - 1
		if ((system attribute "ascv") div 256 mod 16 < 2) then -- Pre AppleScript 2.0.
			set c to (ASCII character n)
		else -- AppleScript 2.0 or later.
			set c to character id n
		end if
		if ((system attribute "sysa") < 3) then -- PPC.
			set pos to 16
		else -- Intel.
			set pos to 15
		end if
		write c as string to fRef starting at pos
		-- Read back the entire file as "drag" data.
		set dragData to (read fRef from 1 as «class drag»)
	end try
	close access fRef
	
	return dragData
end makeDragData

(*
on copyToClipboard()
	set the clipboard to missing value
	tell application "System Events"
		tell application process "Finder" to set {frontmost, visible, visible} to {true, false, true}
		tell (first application process whose frontmost is true)
			set CopyMenuItem to first menu item of menu 1 of menu bar item 4 of menu bar 1 whose value of attributes contains "C" and value of attributes contains 0
			set wait to 0
			repeat until (value of attribute "AXEnabled" of CopyMenuItem) or (wait > 1)
				delay 0.2
				set wait to wait + 0.2
			end repeat
			if (wait > 1) then
				tell application (path to frontmost application as Unicode text)
					display dialog "No selection found!" buttons {"Cancel"} default button 1 with icon stop
				end tell
				error number -128
			end if
			click CopyMenuItem
		end tell
	end tell
	repeat while ((the clipboard) is missing value)
		delay 0.2
	end repeat
end copyToClipboard

copyToClipboard()
*)
clipboardText2Clipping()

Edit: Corrected the position of the resource-count low-byte in the drag data for Intel machines. Oops! :rolleyes:

Thanks, Nigel. It works fine.
(It will take me quite some time to read your code and then understand it. Till then, I will just use it. :slight_smile: :))