Batch Edit Macro Properties [1.3b56+]

Show off your RPG maps, campaigns, and tokens. Share your gaming experiences and stories under General Discussion and save this forum for things you've created that may help others run their own games. Use the subforums when appropriate.

Moderators: dorpond, trevor, Azhrei, Gamerdude

Post Reply
User avatar
biodude
Dragon
Posts: 444
Joined: Sun Jun 15, 2008 2:40 pm
Location: Montréal, QC

Batch Edit Macro Properties [1.3b56+]

Post by biodude »

I found myself wanting to change properties on multiple macros, but wasn't looking forward to manually changing properties on all desired macros. So, I wrote a macro to do it for me :D

UPDATE
version 2 is described in another post due to (code) length limitations.
The Lib token can be downloaded with the macros (the code was getting too long to post, sorry)
Lib-jaw-Macro-Utilities-1.3b56.rptok
Batch Edit Macros v2, Set Macro Properties
(63.92 KiB) Downloaded 274 times
LEGACY - v1
I just threw this together over the past couple of hours, so it's basic and pretty hack, but feel free to improve / modify it if you think it's useful (I'd bet good money zEal has one of these in his "Macro Toolkit" already, but I'll never learn if I don't try ;-) )

If you use it, leave "Apply to Selected Tokens" unchecked: the input includes a list of tokens to work on (starts with selected, then tries impersonated, and finally the location of the macro).
BatchEditMacrosV1

Code: Select all

/self <!-----===== Batch Edit Macro Properties v1 =====----->
[H: jaw.tokenConditions = json.set("", "selected", 1 )]
[H, IF( isGM()==0 ): jaw.tokenConditions = json.set( jaw.tokenConditions, "owned", 1 )]
[H: targetLocations = getTokens( ",", jaw.tokenConditions )]
[H, IF( targetLocations=="" ): targetLocations = currentToken() ]    <!-- getImpersonated() fails ungracefully (what about hasImpersonated?).  This will return the impersonated token if there is one -->
[H, IF( targetLocations=="" ): targetLocations = getMacroLocation() ]    <!-- Final option -->
<!-- assemble lists for input -->
[H: macroLocation.list = "" ]    <!-- macro property retrieval doesnt seem to work with "Global" or "Campaign", but geMacroLocation should still allow this to be used in these locations -->
[H, FOREACH( loc, targetLocations ):  macroLocation.list = listAppend( macroLocation.list, getName( loc ) +" "+ getImage( getName( loc ) ) )]
[H: macroProps.db = json.set("",
    "applyToSelected", "Apply To Selected",
    "autoExecute", "Auto Execute",
    "color", "Button Color",
    "command", "Command (Contents)",
    "fontColor", "Font Color",
    "fontSize", "Font Size",
    "includeLabel", "Include Label",
    "group", "Group",
    "sortBy", "Sorting Prefix",
    "label", "Label (Name)",
    "maxWidth", "Max. Width",
    "minWidth", "Min. Width",
    "playerEditable", "Allow Players to Edit Macro",
    "tooltip", "ToolTip"
)]
[H: macroPropNames = json.fields( macroProps.db ) ]
[H: macroProps.list = "" ]
[H, FOREACH( field, macroPropNames ): macroProps.list = listAppend( macroProps.list, json.get( macroProps.db, field ) )]
<!-- User Input -->
[H: inputLine = "%s | %s | %s | %s | %s" ]
[H: go = input( 
    strformat( inputLine, "macroLocation", macroLocation.list, "Macro Location", "LIST", "icon=true iconsize=60" ),
    strformat( inputLine, "propName", macroProps.list, "Macro Property to edit", "LIST", "" ),
    "value.old |  | Old Value | TEXT | width=20",
    "value.new |  | New Value | TEXT | width=20"
)]
[H: abort(go)]
[H: propName = listGet( macroPropNames, propName )]
[H: macroLocation = listGet( targetLocations, macroLocation )]
[H: updateMacros.list = "" ]
[H: loc = macroLocation ]
<!-- H, FOREACH( loc , targetLocations ), code:    -->
    [H: macros.list = getMacros( "json", loc ) ]
    [H, FOREACH( macro, macros.list ), code:{
        [ macroIndex.list = getMacroIndexes( macro, "json", loc )]    <!-- what about macros with duplicate names? -->
        [FOREACH( macroIndex, macroIndex.list ), code:{
            [ macroProps = getMacroProps( macroIndex, "json", loc )]
            [if( value.old==""): editProp = 1 ; editProp = if( json.get( macroProps, propName ) == value.old, 1, 0 )]
            [if( editProp ): macroProps.new = json.set( macroProps, propName, value.new ) ; macroProps.new = macroProps ]
            [if( editProp ): updateMacros.list = listAppend( updateMacros.list, macro )]
            [if( editProp ): setMacroProps( macroIndex, macroProps.new, "json", loc )]
        }]
    }]
<!--    
}]    -->
<!-- Output -->
<strong>Macros Updated:</strong> {if( updateMacros.list=="" , "NONE" , updateMacros.list )}
<br><strong>@</strong> { getName( macroLocation ) }
 
It basically performs a batch "find/replace" on an entire property on all macros that match the "find" ("Old Value") string. So, you can change all your red macros to blue, or whatever. I also like using it to change the Group Name for an entire group of macros (I couldn't find an easy way to do this in the MapTool GUI, but this works well enough).

EDIT: Group filter removed, as it was unreliable - see v2 for more advanced filtering features
The "Group" filter doesn't seem to be 100% reliable, but it's past my bedtime, so I might look at this later (the Group Property is matched reliably, but the Group filter uses Wiki: getMacroGroup(), which sometimes does not return all indices of macros in that group)...


Enjoy.
Last edited by biodude on Wed Dec 09, 2009 5:29 pm, edited 7 times in total.
"The trouble with communicating is believing you have achieved it"
[ d20 StatBlock Importer ] [ Batch Edit Macros ] [ Canned Speech UI ] [ Lib: Math ]

User avatar
Azhrei
Site Admin
Posts: 12086
Joined: Mon Jun 12, 2006 1:20 pm
Location: Tampa, FL

Re: Batch Edit Macro Properties

Post by Azhrei »

Yeah, I find these kinds of macros to be very useful.

Even a simple one that lets you enter a property name and a value, then loops through all selected tokens and sets the property to the given value using Wiki: eval() is quite handy sometimes... Although yours is a much bigger production. :)

User avatar
biodude
Dragon
Posts: 444
Joined: Sun Jun 15, 2008 2:40 pm
Location: Montréal, QC

Re: Batch Edit Macro Properties [1.3b56]

Post by biodude »

UPDATE - v2
After sleeping on it, I realized I could take a more direct approach with the interface and matching. This version is a tad slower, but it can do even more. The first line lets you set the number of filters you want to use (defaults to 2). The input dynamically generates filter inputs, which are checked against all macros (this is slow part). Group filtering is not as fast as before, but now 100% reliable.
For wholesale batch replacement of macro properties, see "Batch Set Macro Properties", below.
New Features in 2:
  • Multiple matching & filtering options by macro properties
  • This version can now do batch renames, even changing parts of names, by using regex matching.
  • The input window is considerably prettier :P (see Attachments for screenshot).
  • More Verbose output (some of it was for debugging, but I left it in for information purposes. It can be easily removed if you don't like it).
  • a little slower, due to all the processing. Any tips to optimize are most welcome!
Lib Token available for download (attached to first post in this thread).
MapTool-BatchEditMacros-v2.png
MapTool-BatchEditMacros-v2.png (167.74 KiB) Viewed 6398 times
BatchEditMacrosV2
Unfortunately, I couldn't paste the code here, because it's too darn long! See Attachments in the top post for Token with macros.
I just noticed it doesn't work on Lib: Tokens, unless you impersonate them first (not just select).
It seems Wiki: getTokens() doesn't return Lib: Tokens, even if they are selected. Oh well, minor bug: GELMO (Good Enough, Let's Move On).

Batch Set Macro Properties
I liked the simplicity of version 1 so much that I decided to tweak it: it can now do wholesale replacement of macro properties on multiple selected tokens, unlike the batch editing macro, which only works on selected tokens due to macro processing length and code nesting limits (but can do more fine-scale editing and filtering).
SetmacroProps

Code: Select all

<!-----===== Batch Set Macro Properties on Multiple Tokens =====----->
<!-- Simple, but fast, batch replacement of Macro properties on ALL selected tokens -->
[H: jaw.tokenConditions = json.set("", "selected", 1 )]
[H, IF( isGM()==0 ): jaw.tokenConditions = json.set( jaw.tokenConditions, "owned", 1 )]
[H: targetLocations = getTokens( ",", jaw.tokenConditions )]
[H, IF( targetLocations=="" ): targetLocations = currentToken() ]	<!-- getImpersonated() fails ungracefully (what about hasImpersonated?).  This will return the impersonated token if there is one -->
[H, IF( targetLocations=="" ): targetLocations = getMacroLocation() ]	<!-- Final option -->
<!-- assemble lists for input -->
[H: macroLocation.list = "" ]	<!-- macro property retrieval doesn't seem to work with "Global" or "Campaign", but geMacroLocation should still allow this to be used in these locations -->
[H, FOREACH( loc, targetLocations ):  macroLocation.list = listAppend( macroLocation.list, getName( loc ) +" "+ getImage( getName( loc ) ) )]
[H: macroProps.db = json.set("",
	"applyToSelected", "Apply To Selected",
	"autoExecute", "Auto Execute",
	"color", "Button Color",
	"command", "Command (Contents)",
	"fontColor", "Font Color",
	"fontSize", "Font Size",
	"includeLabel", "Include Label",
	"group", "Group",
	"sortBy", "Sorting Prefix",
	"label", "Label (Name)",
	"maxWidth", "Max. Width",
	"minWidth", "Min. Width",
	"playerEditable", "Allow Players to Edit Macro",
	"tooltip", "ToolTip"
)]
[H: macroPropNames = json.fields( macroProps.db ) ]
[H: macroProps.list = "" ]
[H, FOREACH( field, macroPropNames ): macroProps.list = listAppend( macroProps.list, json.get( macroProps.db, field ) )]
<!-- User Input -->
[H: inputLine = "%s | %s | %s | %s | %s" ]
[H: go = input( 
	strformat( inputLine, "macroLocation", macroLocation.list, "Set Macro Properties at Location", "LIST", "icon=true iconsize=60" ),
	"allLocations | 0 | Apply to ALL locations above? | CHECK ",
	strformat( inputLine, "propName", macroProps.list, "Macro Property to edit", "LIST", "" ),
	"value.old |  | Old Value | TEXT | width=20",
	"value.new |  | New Value | TEXT | width=20"
)]
[H: abort(go)]
[H: propName = listGet( macroPropNames, propName )]
<!-- Prep variables for loop processing -->
[H: updateTokens.list = "" ]
[H: allOutput = "" ]
[H: outputMessage = "<strong>Macros Updated</strong><br><span style='font-size: 90%%'>%s</span>" ]
[H: outputFrom = 
		"<table cellpadding='0'>
			<tr>
				<td valign='top' width='40' style='padding-right: 5px'><img src='%s'></img></td>
				<td valign='top' style='margin-right: 5px'>%s:</td>
				<td valign='top'> %s </td>
			</tr>
		</table>"
]
[H: macroLocation = listGet( targetLocations, macroLocation )]
[H, if( allLocations ): loc = macroLocation ; targetLocations = macroLocation ]
<!-- 	Set matching macro properties in each selected token / location	 -->
[H, FOREACH( loc , targetLocations ), code:{
	[H: switchToken( loc )]	<!-- Not entirely necessary, but helpful -->
	[H: updateMacros.list = "" ]
	<!--	Build list of macro INDICES (needed for getMacroProps )	-->
	[H: macros.list = getMacros( "json", loc ) ]
	[H: macroName.index = "" ]	<!-- to allow later lookup of name by index ( for output ) -->
	[H: macroIndex.list = "[]" ]	<!-- empty json array -->
	[H, FOREACH( macro, macros.list ), code:{
		[ index.macro = getMacroIndexes( macro, "json", loc )]
		[foreach( macroIndex, index.macro ): macroName.index = json.set( macroName.index , macroIndex , macro )]
		[ macroIndex.list = json.merge( macroIndex.list, index.macro )]
	}]
	[H: macroIndex.list = json.unique( macroIndex.list )]
	[H: editMacro = 0 ]	<!-- default: do not edit macro.  Check if conditions match to approve editing -->
	[FOREACH( macroIndex, macroIndex.list ), code:{
		[ macroProps = getMacroProps( macroIndex, "json", loc )]
		[if( value.old==""): editMacro = 1 ; editMacro = if( json.get( macroProps, propName ) == value.old, 1, 0 )]
		[if( editMacro ): macroProps.new = json.set( macroProps, propName, value.new ) ; macroProps.new = macroProps ]
		[if( editMacro ): updateMacros.list = listAppend( updateMacros.list, json.get( macroName.index, macroIndex ) )]
		[if( editMacro ): setMacroProps( macroIndex, macroProps.new, "json", loc )]
	}]
	[H: messageOut = strformat( outputMessage, if( updateMacros.list=="" , "NONE" , updateMacros.list ) )]
	[H: outputText = strformat( outputFrom , getTokenImage( 40 ) , getName( loc ) , messageOut )]
	[H: allOutput = allOutput + outputText + "<br>" ]

}]
<!-- Output -->
[H: assert( 0, allOutput, 0 )]	<!-- This is an ugly hack that sends all output to chat at once (to self), with no 'source',
			while simultaneously aborting macro execution and preventing any other macro output to chat -->
<!-- END -->
"The trouble with communicating is believing you have achieved it"
[ d20 StatBlock Importer ] [ Batch Edit Macros ] [ Canned Speech UI ] [ Lib: Math ]

User avatar
biodude
Dragon
Posts: 444
Joined: Sun Jun 15, 2008 2:40 pm
Location: Montréal, QC

Re: Batch Edit Macro Properties [1.3b56+]

Post by biodude »

Sorry, the last post got a bit messy as I was trying to update things.

I've been obsessing over these macros and tweaking them all day :evil:, but I think I'm satisfied with these new versions. I made slight changes to the output to v2, and even cleaned up v1 to make it useful again, as a new macro that does less to the macros, but can do it on multiple tokens, and much faster than the full "editing" macro.

I had trouble pasting in the new code, because it was so long! (too long even for a Global macro without deleting some of the comments, at least on a Mac). I put them all on a lib-token, even though they are self-contained and can ultimately live anywhere. I now keep copies on my Global panel.
"The trouble with communicating is believing you have achieved it"
[ d20 StatBlock Importer ] [ Batch Edit Macros ] [ Canned Speech UI ] [ Lib: Math ]

User avatar
booga
Dragon
Posts: 365
Joined: Fri Dec 14, 2007 9:00 am

Re: Batch Edit Macro Properties [1.3b56+]

Post by booga »

Hi biodude,

nice work. Does it work only for Token Macros, or also for Campaign and Global Macros ? Being able to quickly move around/rename/modify macro properties between the various Macro containers would be wonderful.

User avatar
biodude
Dragon
Posts: 444
Joined: Sun Jun 15, 2008 2:40 pm
Location: Montréal, QC

Re: Batch Edit Macro Properties [1.3b56+]

Post by biodude »

booga wrote:Hi biodude,

nice work. Does it work only for Token Macros, or also for Campaign and Global Macros ? Being able to quickly move around/rename/modify macro properties between the various Macro containers would be wonderful.
I couldn't agree more. When I started writing it, that was my intention, but I couldn't figure out how to get the macro functions to retrieve / set macros at those locations. Maybe in the next few days, I'll run some more tests, but I'm rather busy with real life at the moment. If anybody knows how to make Wiki: getMacros() or Wiki: setMacroProps() to work on Global & Campaign macros, let us know!
"The trouble with communicating is believing you have achieved it"
[ d20 StatBlock Importer ] [ Batch Edit Macros ] [ Canned Speech UI ] [ Lib: Math ]

User avatar
booga
Dragon
Posts: 365
Joined: Fri Dec 14, 2007 9:00 am

Re: Batch Edit Macro Properties [1.3b56+]

Post by booga »

Cool,

thanks for the update, I'm afraid I'm of no help in the topic, being a very lousy scripter. Hopefully someone will have some insight on this.

Bon courage !

User avatar
biodude
Dragon
Posts: 444
Joined: Sun Jun 15, 2008 2:40 pm
Location: Montréal, QC

Re: Batch Edit Macro Properties [1.3b56+]

Post by biodude »

One more addition to the Macro Toolkit. This self-contained macro displays a table of macro properties (it currently omits the actual command, since this tends to be rather long). I'm using it to try to organize all the macros on my lib:Token. You can do whatever you want with it (check settings on many macros at once, for instance).
DisplayMacroProps

Code: Select all

<!-----===== Display All Macro Properties  =====----->
[H: macros.list = getMacros('json') ]
[H: macroProps.db = json.set( "",
	"label", "Label (Name)",
	"tooltip", "ToolTip",
	"group", "Group",
	"sortBy", "Sorting Prefix",
	"color", "Button Color",
	"fontColor", "Font Color",
	"fontSize", "Font Size",
	"maxWidth", "Max. Width",
	"minWidth", "Min. Width",
	"applyToSelected", "Apply To Selected",
	"autoExecute", "Auto Execute",
	"includeLabel", "Include Label",
	"playerEditable", "Allow Players to Edit Macro",
	"command", "Command (Contents)"
)]
[H: macroFields = json.fields( json.remove( macroProps.db , 'command' ) )]
[H: fieldLabels = "" ]
[H, foreach( fieldName, macroFields ): fieldLabels = json.append( fieldLabels , json.get( macroProps.db, fieldName ) )]
[H: macro.table = "<table>" ]
[H: macro.table = macro.table + listFormat( json.toList( fieldLabels ), "<tr valign='bottom'>%list</tr>", "<th>%item</th>", "" )]
[H, FOREACH( macro , macros.list ), code:{
	[H: oddRow = if( floor(roll.count/2)==(roll.count/2), 0, 1 )]
	[H: rowClass = if( oddRow, "odd", "even" )]
	[H: indexes = getMacroIndexes( macro )]
	[H, foreach( index,indexes ), code:{
		[H: macroProps = getMacroProps( index, 'json' )]
		[H: macroValues = "" ]
		[H, foreach( fieldName, macroFields ): macroValues = json.append( macroValues , json.get( macroProps, fieldName ) )]
		[H: macro.table = macro.table + listFormat( json.toList( macroValues ), "<tr valign='top' class='"+ rowClass +"'>%list</tr>", "<td>%item</td>", "" )]
	}]
}]
[H: macro.table = macro.table + "</table>" ]

[dialog( "Macros on: "+ getName() ):{
	<html>
		<head>
			<style>
				[R: decode("
					td%2C+th+%7B+text-align%3A+left%26semi%3B+vertical-align%3A+top%26semi%3B+%7D
					th+%7B+vertical-align%3A+bottom+%26semi%3B+%7D
					tr+%7B+color%3A+%23000000%26semi%3B+background%3A+%23FFFFFF%26semi%3B+%7D
					tr.even+%7B+background%3A+%23DDDDFF+%7D
				")]
			</style>
		</head>
		<body>
			[R: macro.table ]
		</body>
	</html>
}]
"The trouble with communicating is believing you have achieved it"
[ d20 StatBlock Importer ] [ Batch Edit Macros ] [ Canned Speech UI ] [ Lib: Math ]

User avatar
Doc_Waldo
Giant
Posts: 108
Joined: Wed Sep 08, 2010 11:41 pm
Location: Boise, ID

Re: Batch Edit Macro Properties [1.3b56+]

Post by Doc_Waldo »

Can you give a quick run down of how you would set this up. I couldn't get it to work. Now given I am as dumb as a rock, but I dragged the token into my campaign, I ran the "onCampaignLoad", then I tried to run from token, nothing, couldn't find my other tokens. I then tried dragging macro's to global, and although the batch edit v.1 could see the token, it didn't do anything. It wouldn't even let me drag over the batch edit macro v2. Any suggestions would be very helpful.

Also, did you ever find a way to group adjust the campaign or global macros?
--DOC

User avatar
biodude
Dragon
Posts: 444
Joined: Sun Jun 15, 2008 2:40 pm
Location: Montréal, QC

Re: Batch Edit Macro Properties [1.3b56+]

Post by biodude »

Doc_Waldo wrote:Can you give a quick run down of how you would set this up. I couldn't get it to work. Now given I am as dumb as a rock, but I dragged the token into my campaign, I ran the "onCampaignLoad", then I tried to run from token, nothing, couldn't find my other tokens. I then tried dragging macro's to global, and although the batch edit v.1 could see the token, it didn't do anything. It wouldn't even let me drag over the batch edit macro v2. Any suggestions would be very helpful.
You have to select tokens before activating the macro, or you have to impersonate them if they are Lib: tokens.

I haven't actually looked at these in a while, and I'm a little rushed at the moment, but as far as I can remember:

Batch Edit Macro Properties allows you to make more complex changes to all macros on a single token (impersonate or select).
Batch Set Macro Properties allows you to make simple changes (replace entire properties) to all macros on multiple selected tokens.

I hope that solves your problem.

[quote="Doc_Waldo"Also, did you ever find a way to group adjust the campaign or global macros?[/quote]
Unless this has been changed in the macro script language, these macros are still inaccessible to automated macro changes, as far as I know.
"The trouble with communicating is believing you have achieved it"
[ d20 StatBlock Importer ] [ Batch Edit Macros ] [ Canned Speech UI ] [ Lib: Math ]

Post Reply

Return to “User Creations”