How can I turn this into a handler/subroutine?

Hi everybody, although I’ve been scripting for almost 10 years now, I’ve always managed to avoid the use of custom made handlers, I usually use nested scripts in my code and call them with the “run script” command. But the following code I wrote looks like it would benefit from a handler, I just don’t know where to start. It’s a simple script that will display a “progress bar” each time an InDesign document will close. So basically I would want the progress bar code to be part of a subroutine that I would call after the “close document 1 saving no” line. Thanks in advance for your help.

set my_ratio to 1
tell application “Adobe InDesign CS5”
activate
set my_item_count to (count every document)
if my_item_count is not 20 then
set my_ratio to (my_item_count / 20)
end if
set progress_full_character_count to 20
set my_progress to “”
set my_space_progress to “”
set my_star to ASCII character 240
set my_space to ASCII character 176
repeat with this_item from 1 to progress_full_character_count
set my_space_progress to my_space_progress & my_space
end repeat
if my_item_count is not 20 then
set my_ratio to (my_item_count / 20)
end if
repeat with this_item from 1 to my_item_count
–do lots of stuff in InDesign
close document 1 saving no

	--this would be the start of my progress bar handler
	tell me
		activate
		if (this_item / my_ratio) ≥ 1 then
			repeat round (this_item / my_ratio) rounding toward zero times
				set my_progress to my_progress & my_star
			end repeat
		end if
		if (this_item / my_ratio) < 1 then
			set my_progress to my_progress & my_star
		end if
		set my_length to length of my_progress
		if this_item < my_item_count then display dialog "0% " & my_progress & (characters 1 thru (progress_full_character_count - (my_length)) of my_space_progress) & " 100%" giving up after 1 with title "In progress...  processed " & this_item & " items out of " & my_item_count buttons {""}
		if this_item = my_item_count then display dialog "0% " & my_progress & " 100%" giving up after 2 with title "Complete!" buttons {""}
		set my_progress to ""
	end tell
	--this would be the end of my progress bar handler
	
end repeat

end tell

Hello.

Here’s the code you wrote as a subroutine:

	set my_ratio to 1
	tell application "Adobe InDesign CS5"
		activate
		set my_item_count to (count every document)
		if my_item_count is not 20 then
			set my_ratio to (my_item_count / 20)
		end if
		set progress_full_character_count to 20
		set my_progress to ""
		set my_space_progress to ""
		set my_star to ASCII character 240
		set my_space to ASCII character 176
		repeat with this_item from 1 to progress_full_character_count
			set my_space_progress to my_space_progress & my_space
		end repeat
		if my_item_count is not 20 then
			set my_ratio to (my_item_count / 20)
		end if
		repeat with this_item from 1 to my_item_count
			--do lots of stuff in InDesign
			close document 1 saving no
			
			--this would be the start of my progress bar handler
			on mySubroutine(this_item, my_ration, my_progress, my_star)
			tell me
				activate
				if (this_item / my_ratio) ≥ 1 then
					repeat round (this_item / my_ratio) rounding toward zero times
						set my_progress to my_progress & my_star
					end repeat
				end if
				if (this_item / my_ratio) < 1 then
					set my_progress to my_progress & my_star
				end if
				set my_length to length of my_progress
				if this_item < my_item_count then display dialog "0% " & my_progress & (characters 1 thru (progress_full_character_count - (my_length)) of my_space_progress) & " 100%" giving up after 1 with title "In progress...  processed " & this_item & " items out of " & my_item_count buttons {""}
				if this_item = my_item_count then display dialog "0% " & my_progress & " 100%" giving up after 2 with title "Complete!" buttons {""}
				set my_progress to ""
			end tell
			end mySubroutine
			--this would be the end of my progress bar handler
			
		end repeat
	end tell

(Change mySubroutine with whatever you want to name the subroutine.)

display dialog "By the way, remember to use the Applescript tags!" buttons {"OK"} default button 1 with icon note

This is one way you could do it.


property my_progress : ""

on processHandler(my_item_count, this_item, my_ratio)
	set progress_full_character_count to 20
	set my_progress to ""
	set my_space_progress to ""
	set my_star to ASCII character 240
	set my_space to ASCII character 176
	tell me
		activate
		if (this_item / my_ratio) ≥ 1 then
			repeat round (this_item / my_ratio) rounding toward zero times
				set my_progress to my_progress & my_star
			end repeat
		end if
		if (this_item / my_ratio) < 1 then
			set my_progress to my_progress & my_star
		end if
		set my_length to length of my_progress
		if this_item < my_item_count then display dialog "0% " & my_progress & (characters 1 thru (progress_full_character_count - (my_length)) of my_space_progress) & " 100%" giving up after 1 with title "In progress...  processed " & this_item & " items out of " & my_item_count buttons {""}
		if this_item = my_item_count then display dialog "0% " & my_progress & " 100%" giving up after 2 with title "Complete!" buttons {""}
		set my_progress to ""
	end tell
end processHandler

set my_ratio to 1
tell application "Adobe InDesign CS5"
	activate
	set my_item_count to (count every document)
	if my_item_count is not 20 then
		set my_ratio to (my_item_count / 20)
	end if
	repeat with this_item from 1 to progress_full_character_count
		set my_space_progress to my_space_progress & my_space
	end repeat
	if my_item_count is not 20 then
		set my_ratio to (my_item_count / 20)
	end if
	repeat with this_item from 1 to my_item_count
		--do lots of stuff in InDesign
		close document 1 saving no
		proccessHandler(my_item_count, this_item, my_ratio)
		
	end repeat
end tell


Let me explain.

You want the value of my_progress to be remembered across calls. In order for that to happen, it needs to be a global variable. The property makes it a global variable.

Next I took your block of code for your progress bar handler and I put in in the “on-end” block that you see. Notice the Handler takes three variables. These are set in the main part of your code, but are used in the handler code. If the values are changed in the handler, the updated values would not be passed back out.

Remember that my_progress is global variables. It can be used anywhere, with a minor exception explored below. For well written code, try to avoid global variables. It produces sloppy code and often times is harder to debug. There are some instances were some of the Applescript variables need to be defined globally though. That is were property comes in handy.

Here is that exception example I promised:


property a : 0

on doThis()
	local a
	set a to 4
	display dialog "a is " & a
end doThis

on doThisToo()
	set a to a + 8
end doThisToo

display dialog "a is " & a
doThis()
display dialog "a is " & a
doThisToo()
display dialog "a is " & a
set a to a - 4
display dialog "a is " & a

Notice the local “a” did not effect the value of the global “a”.

Okay, After a couple of sidebars, I then worked with this block of code from your code.


	set progress_full_character_count to 20
	set my_progress to ""
	set my_space_progress to ""
	set my_star to ASCII character 240
	set my_space to ASCII character 176

I removed the set my_progress to “” because this is handled by the global variable. Additionally, setting this value at the beginning of the handler would clear your progress every time.

The other part of that block were stuff specifically for the handler, so I moved them to the top of the handler.

Okay, the handler is now pulled out and ready to be used. It needs to be called.

That is what this line does:


	proccessHandler(my_item_count, this_item, my_ratio)

This calls the handler proccessHandler and passes the three variables my_item_count, this_item, my_ratio.

As I stated, If those values are altered in the handler, the changed values are not passed out.

For the sake of easy editing, I did not change the variable names that you used in you handler. Take a look at this block of code. This also illustrates the unchanging nature of passed variables.


on addTwoNumbers(sum, numberOne, numberTwo)
	set sum to numberOne + numberTwo
end addTwoNumbers

on returnTheSum(numberOne, numberTwo)
	return numberOne + numberTwo
end returnTheSum

set firstSum to 0

addTwoNumbers(firstSum, 4, 3)

set secondSum to returnTheSum(7, -3)

set a to 3
set b to 8
set thirdSum to returnTheSum(a, b)

display dialog "First sum is " & firstSum & " and second sum is " & secondSum& " and third sum is " & thirdSum

Notice how the first sum did not change its value? Additionally, variable names don’t have to line up nicely like they are in your example. In the second call to returnTheSum, the variables a and b are passed in, but the name of the variables in the handler are firstNumber and secondNumber.

Additionally, the second handler shows how results can be passed out of a handler. Notice the return on the end of the handler? the return will return the result that followed. Don’t try to return a local variable, though. It result can change as program processes.

DO NOT DO THIS


on returnTheSum(numberOne, numberTwo)
	set theSum to numberOne + numberTwo
	return theSum
end returnTheSum



The value theSum is a local variable and when the handler returns, it is possible the memory spot held by theSum may be overwritten by something else.

I hope this helps on getting you on your way for using handlers in your code. Sometimes they can be very handy. Additionally They can really improve the readability of your code, especially when handlers and variables are given descriptive names.

@ DevEd2

Your code didn’t work as is but it helped me enough (thank you for that) to make the following code work:

set my_ratio to 1
tell application “Adobe InDesign CS5”
activate
set my_item_count to (count every document)
if my_item_count is not 20 then
set my_ratio to (my_item_count / 20)
end if
set progress_full_character_count to 20
set my_progress to “”
set my_space_progress to “”
set my_star to ASCII character 240
set my_space to ASCII character 176
repeat with this_item from 1 to progress_full_character_count
set my_space_progress to my_space_progress & my_space
end repeat
if my_item_count is not 20 then
set my_ratio to (my_item_count / 20)
end if
repeat with this_item from 1 to my_item_count
–do lots of stuff in InDesign
close document 1 saving no
mySubroutine(this_item, my_ratio, my_progress, my_star, my_item_count, my_space_progress, progress_full_character_count) of me
end repeat
–this would be the start of my progress bar handler
end tell
on mySubroutine(this_item, my_ratio, my_progress, my_star, my_item_count, my_space_progress, progress_full_character_count)
tell me
activate
if (this_item / my_ratio) ≥ 1 then
repeat round (this_item / my_ratio) rounding toward zero times
set my_progress to my_progress & my_star
end repeat
end if
if (this_item / my_ratio) < 1 then
set my_progress to my_progress & my_star
end if
set my_length to length of my_progress
if this_item < my_item_count then display dialog “0% " & my_progress & (characters 1 thru (progress_full_character_count - (my_length)) of my_space_progress) & " 100%” giving up after 1 with title “In progress… processed " & this_item & " items out of " & my_item_count buttons {”“}
if this_item = my_item_count then display dialog “0% " & my_progress & " 100%” giving up after 2 with title “Complete!” buttons {”"}
set my_progress to “”
end tell
end mySubroutine

@ Stephen

Thank you very much for taking the time to explain so well the different alternatives I can use to make this work. You would make a great teacher!

It’s not neccaserry. You can send a variable to an handler and let the handler change the variable. Let me explain with a little code:

set total to 0
addTwoNumbers(a reference to total, 4, 3)
return total --result: 7

on addTwoNumbers(sum, numberOne, numberTwo)
	set contents of sum to numberOne + numberTwo
end addTwoNumbers

As you can see the variable total has changed by the handler. Sometimes you want this behavior but most of the times you wont. It’s sometimes useful with in parenting chain when you want in the child object to change something in the parent but don’t want a global.