Difference between revisions of "Scripting"
Line 1: | Line 1: | ||
− | Freeplane's builtin functionality can be extended by [http://groovy.codehaus.org/ Groovy] scripts | + | Freeplane's builtin functionality can be extended by [http://groovy.codehaus.org/ Groovy] scripts. This page gives a first impression what you can do with scripting and helps to get started. |
+ | With Freeplane scripting you can | ||
− | + | * write your own functions and use them from the menu or via keyboard shortcuts, | |
+ | * use [[Formulas|formulas]] in your map to compute stuff like in Excel, and | ||
+ | * create [[Add-ons|add-ons]] to share it with other users. | ||
− | + | Most people use scripting to automate otherwise tedious procedures like creating a node with a special style and some standard attributes. But much more is possible with scripting. | |
− | + | <br> __TOC__ | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | < | ||
− | |||
− | |||
− | |||
− | + | [[External script file execution|External Groovy scripts]] can be integrated simply by placing them in the ''scripts'' subdirectory of the Freeplane homedir. Such scripts can be used like any other built-in function of Freeplane. | |
− | + | After some preparation we'll create the first script. | |
− | + | === Preparation === | |
− | |||
− | + | A newly installed Freeplane installation is almost ready for scripting: | |
+ | * The <tt>scripts</tt> directory is created in the ''User Configuration Folder'' which you can open via ''Tools > Open user directory''. It's empty, initially. | ||
+ | * This directory is automatically searched for ".groovy" files on startup. | ||
+ | * Scripting is disabled by default, but we'll fix that in a minute. | ||
− | + | First create a new mindmap with this content (just copy 'n paste it into a new map): | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | First | ||
− | |||
− | |||
− | |||
− | |||
test | test | ||
Line 60: | Line 37: | ||
Then add some icons to the map - no matter how many and which icons. But we'll need them later. | Then add some icons to the map - no matter how many and which icons. But we'll need them later. | ||
− | |||
− | + | === Select an editor === | |
− | + | You will need a text editor. For the first steps presented on this page any editor will do, such as Notepad on Windows (though [http://notepad-plus-plus.org the free Notepad++]is much better), TextEdit on Mac OS X, or gedit on Ubuntu Linux. You can find an overview of editors with Groovy support [http://groovy.codehaus.org/Other+Plugins on the Groovy web site]. | |
− | |||
− | |||
+ | Freeplane also has a small script editor built into it. It is reached through ''Tools->Edit Script''. You can run the scripts directly in the editor and store them as attributes of the node you are working in. But such [[Map local scripts|map local scripts]] are most useful for quick tests since you can not write the scripts directly to ".groovy" files. | ||
− | + | For ambitious scripting projects or if you have Java/Eclipse know-how you should have a look at the page on [[Scripting environment setup]]. | |
− | |||
− | + | == The first script: HelloWorld.groovy == | |
− | + | "Hello World" is the traditional first program when taking up a programming language. Let's create a Groovy Freeplane version of it: | |
− | + | * Create an empty Groovy script file named <tt>HelloWorld.groovy</tt> in your scripts directory (remember that you can get there via ''Tools > Open user directory''). The suffix <tt>.groovy</tt> is mandatory. | |
− | + | * Open <tt>HelloWorld.groovy</tt> in an appropriate editor as detailed [[#Create_a_script_and_integrate_it_into_Freeplane|above]]. | |
− | + | * Copy the following script into the file and save it. | |
− | <groovy | + | <groovy> |
− | / | + | node.text = "Hello World!" |
− | + | </groovy> | |
− | + | * Now save your script in the editor and restart Freeplane since Freeplane will only find new scripts after a restart. Then you will find your new script in the Freeplane menu location ''Tools->Scripts->Hello World''. You see three sub menus ''Execute on one selected node'', ''Execute on all selected nodes'' and ''Execute on all selected nodes, recursively''. | |
− | + | * Enable scripting in the preferences at ''Tools->Preferences->Plugins->Scripting'': Set ''Script execution enabled'' to ''Yes''. These changes take effect without restarting Freeplane and only need to be done once. For more details see Scripting: Security considerations. | |
− | + | * Execute the script by selecting ''Tools->Scripts->Hello World->Execute on one selected node''. (Never mind the difference between the ''Execute ...'' variants; we'll come to that [[#Execution_modes|later]].) | |
+ | * The text of the selected node will be changed to "Hello World!". | ||
+ | * To restore the original, press Ctrl-Z. | ||
+ | * If you like try the other "Execute..." menu items. Test the influence of selecting multiple nodes. | ||
− | |||
− | + | == Hello Controller == | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
+ | Every script is given the variables | ||
+ | *<tt>node</tt> - set to the currently selected node. | ||
+ | *<tt>c</tt> - a tool box for various tasks relating to the map or Freeplane altogether. | ||
− | + | These give access to the two most important bits of a map. In HelloWorld we used ''node'', which gave access to the selected node. | |
− | + | Now we'll change HelloWorld.groovy to use the second, the Controller variable ''c'': | |
+ | * Copy the following script into the file and save it. | ||
+ | <groovy> | ||
+ | c.statusInfo = "Hello World!" | ||
+ | </groovy> | ||
+ | * Execute the script by selecting ''Tools->Scripts->Hello World->Execute on one selected node''. | ||
− | + | The "Controller" manages the status bar. By assigning "Hello World!" to the Controller attribute "statusInfo" we are able to print text to the status bar. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
+ | The variables ''node'' and ''c'' are "objects" with a list of attributes (like "text", "details" or "style") and a list of methods that operate on the object, like "addConnector()", "createChild()" or "moveTo()". The "type" of the object decides on the list of attribute of attributes and methods an object has. "node" is of type [http://freeplane.sourceforge.net/doc/api/org/freeplane/plugin/script/proxy/Proxy.NodeRO.html Proxy.Node] while "c" has the type [http://freeplane.sourceforge.net/doc/api/org/freeplane/plugin/script/proxy/Proxy.Controller.html Proxy.Controller]. | ||
− | + | To get started with Freeplane scripting you have to get slowly accustomed to the Groovy syntax and the Freeplane specialities too. The types and objects that Freeplane supports are described by the [[Scripting_API|scripting API]]. You can learn it step by step: Very little is required to write useful scripts. An important resource is the built-in scripting documentation that is available via ''Help->Scripting API''. | |
− | + | In the next section we'll see what the "@ExecutionModes" line is about. | |
− | |||
− | |||
+ | == Execution modes == | ||
− | + | For the "Hello World" script we had three submenu entries of "Hello World". These entries are different with respect to multiple selected nodes: | |
− | |||
− | |||
− | |||
− | |||
*In the case of ''Execute on one selected node'' a script is executed only once no matter how many nodes are selected. It's best to be used when only a single node is selected since in this case the <tt>node</tt> variable of the script is set to the selected node. If multiple nodes are selected then <tt>node</tt> is set to one of the nodes arbitrarily. That is, you shouldn't count on the selection if multiple nodes are selected. | *In the case of ''Execute on one selected node'' a script is executed only once no matter how many nodes are selected. It's best to be used when only a single node is selected since in this case the <tt>node</tt> variable of the script is set to the selected node. If multiple nodes are selected then <tt>node</tt> is set to one of the nodes arbitrarily. That is, you shouldn't count on the selection if multiple nodes are selected. | ||
Line 131: | Line 98: | ||
*''Execute on all selected nodes, recursively'' the selection will be implicitly extended by all child trees of the selected nodes. | *''Execute on all selected nodes, recursively'' the selection will be implicitly extended by all child trees of the selected nodes. | ||
− | If we chose ''Execute on all selected nodes'' for " | + | If we chose ''Execute on all selected nodes'' for "Hello World" then one dialog box would pop up for each selected node. - This clearly would not be intended. By adding the line |
<groovy> | <groovy> | ||
Line 137: | Line 104: | ||
</groovy> | </groovy> | ||
− | only one menu entry survives for " | + | only one menu entry survives for "Hello World". It's a good idea to put the "annotations" at the beginning of the script. (In section [[#Simple_text_replacement:_getIconName.groovy|Simple text replacement]] we will see an exception.) To get the opposite effect, i.e. to exclude the ''Execute on one selected node'' we would have to write: |
<groovy> | <groovy> | ||
Line 145: | Line 112: | ||
Note that for Groovy this is a comment. - This line is only interpreted by Freeplane. Omitting the <tt>//</tt> will result in a Groovy compilation error. | Note that for Groovy this is a comment. - This line is only interpreted by Freeplane. Omitting the <tt>//</tt> will result in a Groovy compilation error. | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
== Per node execution: addIcon.groovy == | == Per node execution: addIcon.groovy == | ||
− | + | Now let's use the <tt>node</tt> variable again in our next script, <tt>addIcon.groovy</tt> (restart Freeplane to see it in the menu). This script will add the "button_ok" icon to any selected node. Since the <tt>node</tt> variable references one selected node we don't have to navigate to them via the controller and we don't have to iterate over them: | |
<groovy> | <groovy> | ||
Line 169: | Line 121: | ||
</groovy> | </groovy> | ||
− | This will add the "check" icon to each selected node. Hopefully it's clear that the execution mode ''Execute on one selected node'' makes no sense | + | This will add the "check" icon to each selected node. Hopefully it's clear that the execution mode ''Execute on one selected node'' makes no sense in this case. So let's remove this from the "Extra" menu: |
<groovy> | <groovy> | ||
Line 176: | Line 128: | ||
</groovy> | </groovy> | ||
− | We will extend this script a little further to only set the icon if the node text contains the words "yes" or "OK" (case insensitively): | + | (To see the change in the menu you have to restart Freeplane.) |
+ | |||
+ | We will extend this script now a little further to only set the icon if the node text contains the words "yes" or "OK" (case insensitively): | ||
<groovy> | <groovy> | ||
Line 188: | Line 142: | ||
<br> | <br> | ||
− | == | + | == Status bar again: getIconName.groovy == |
Finding the proper name of an icon may be a bit difficult. One way is to use the wanted icon in some map and to look it up in the sources. The XML for a node with an icon might look like that: | Finding the proper name of an icon may be a bit difficult. One way is to use the wanted icon in some map and to look it up in the sources. The XML for a node with an icon might look like that: | ||
Line 201: | Line 155: | ||
c.statusInfo = "Icons: " + node.icons.icons | c.statusInfo = "Icons: " + node.icons.icons | ||
</groovy> | </groovy> | ||
+ | |||
+ | |||
+ | ==Iteration: sumNodes.groovy== | ||
+ | |||
+ | Create a script with the following content: | ||
+ | <groovy> | ||
+ | def sum = c.selecteds.sum{it.to.num0} | ||
+ | def sumFormatted = format(sum, "#.##").toString() | ||
+ | ui.informationMessage(ui.frame, sumFormatted, "Sum") | ||
+ | </groovy> | ||
+ | |||
+ | This is a short script which does quite a lot. Understanding what it does, and how, will show the power of Groovy in Freeplane. | ||
+ | |||
+ | # The first line adds all the numbers in the map together. | ||
+ | ## It makes use of the supplied variable ''c.selecteds'' which means all of the selected nodes on the map | ||
+ | ## And of the Groovy variable ''it'', which means the particular selected node | ||
+ | ## And of the Freeplane method ''to.num0'', which takes the numbers in the script and reads them as such, rather than as strings. | ||
+ | ## So at the end of the first line, the variable '''sum''' holds the value of all the individual numbers (c.selecteds.sum{'''it.to.num0'''}) in the selected nodes ('''c.selecteds'''.sum{it.to.num0}), added together (c.selecteds.'''sum'''{it.to.num0}) | ||
+ | # The second line takes this '''sum''', the result of the first line and [http://freeplane.sourceforge.net/doc/api/org/freeplane/plugin/script/FreeplaneScriptBaseClass.html#format(java.lang.Object,%20java.lang.String) formats] it with at most two fractional digits. It puts the result in a string called sumFormatted. | ||
+ | # The third line shows the result. | ||
+ | ## It makes use of the Freeplane helper class ui.informationMessage, and gives the resulting message box a title of "sum", | ||
== Formulas == | == Formulas == | ||
− | Starting with Freeplane 1.2 | + | Starting with Freeplane 1.2 one use scripts as [[Formulas]] directly in the node core like in Excel. Type this formula in the node core: |
<groovy> | <groovy> | ||
Line 216: | Line 191: | ||
*The equal sign has to be the very first character in the script. | *The equal sign has to be the very first character in the script. | ||
*On typing the equal sign as the first character a special script editor pops up which supports syntax highlighting. | *On typing the equal sign as the first character a special script editor pops up which supports syntax highlighting. | ||
− | |||
== Adding a local link == | == Adding a local link == | ||
− | To set the link for a node | + | To set the link for a node use the <tt>node.link</tt> attribute: |
<groovy> | <groovy> | ||
Line 232: | Line 206: | ||
<br> | <br> | ||
− | == Using external libraries == | + | == Apendix == |
+ | |||
+ | === Using external libraries === | ||
In Freeplane all external Java libraries can be used. The best way to do it is to create a <tt>lib</tt> directory in the <freeplane_userdir> and set the "Script classpath" to <tt>lib</tt> under Tools->Preferences->Plugins. All .class files and the content of all .jar files are automatically available in scripts and formulas. | In Freeplane all external Java libraries can be used. The best way to do it is to create a <tt>lib</tt> directory in the <freeplane_userdir> and set the "Script classpath" to <tt>lib</tt> under Tools->Preferences->Plugins. All .class files and the content of all .jar files are automatically available in scripts and formulas. | ||
− | If you want to create your own little library you have to compile a groovy script as described [http://sourceforge.net/apps/phpbb/freeplane/viewtopic.php?f=1&t=534&p=2692#p2692 in this forum post]. | + | If you want to create your own little library you have to compile a groovy script as described [http://sourceforge.net/apps/phpbb/freeplane/viewtopic.php?f=1&t=534&p=2692#p2692 in this forum post]. |
+ | Starting with 1.3.2 [[Scripting:_Freeplane_Utility_Classes|utility scripts]] are compiled automatically. | ||
− | == On Groovy == | + | === On Groovy === |
Although Groovy is more or less a superset of Java it would be a shame not to use the new opportunities Groovy provides. On the other hand there are notable differences between Groovy and Ruby. In this section some of the differences between Java, Groovy and Ruby will be listed. | Although Groovy is more or less a superset of Java it would be a shame not to use the new opportunities Groovy provides. On the other hand there are notable differences between Groovy and Ruby. In this section some of the differences between Java, Groovy and Ruby will be listed. | ||
− | === On Groovy properties and the Scripting API === | + | ==== On Groovy properties and the Scripting API ==== |
If an object, e.g. <tt>Node node</tt>, has a method <tt>getXyz()</tt> then groovy allows to use <tt>node.xyz</tt>. If it also has a proper <tt>setXyz()</tt> method (proper in the sense of the JavaBeans specification) then the property is writable. | If an object, e.g. <tt>Node node</tt>, has a method <tt>getXyz()</tt> then groovy allows to use <tt>node.xyz</tt>. If it also has a proper <tt>setXyz()</tt> method (proper in the sense of the JavaBeans specification) then the property is writable. | ||
Line 273: | Line 250: | ||
The menu item ''Help -> Scripting API'' shows the attributes instead of get/set methods where possible and indicates if the attributes are read-only, read-write or write-only. | The menu item ''Help -> Scripting API'' shows the attributes instead of get/set methods where possible and indicates if the attributes are read-only, read-write or write-only. | ||
− | === The operator == means equals() === | + | ==== The operator == means equals() ==== |
In Groovy the operator <tt>==</tt> is overridden to mean <tt>equals()</tt>. To check for identity use the method [http://groovy.codehaus.org/groovy-jdk/java/lang/Object.html#is%28java.lang.Object%20other%29 is()]: <groovy> | In Groovy the operator <tt>==</tt> is overridden to mean <tt>equals()</tt>. To check for identity use the method [http://groovy.codehaus.org/groovy-jdk/java/lang/Object.html#is%28java.lang.Object%20other%29 is()]: <groovy> | ||
Line 282: | Line 259: | ||
</groovy> | </groovy> | ||
− | === Caveat === | + | ==== Caveat ==== |
Note that - unlike in [http://www.ruby-lang.org/ Ruby] - it's not allowed to omit the parens of a function without parameters in Groovy. So to get the number of children a node has, use <tt>node.children.size()</tt>, not <tt>node.children.size</tt>. The latter would be OK if <tt>java.util.List</tt> had a method <tt>getSize()</tt>. | Note that - unlike in [http://www.ruby-lang.org/ Ruby] - it's not allowed to omit the parens of a function without parameters in Groovy. So to get the number of children a node has, use <tt>node.children.size()</tt>, not <tt>node.children.size</tt>. The latter would be OK if <tt>java.util.List</tt> had a method <tt>getSize()</tt>. | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
<br> | <br> | ||
Line 309: | Line 274: | ||
*To ask questions directly related to this page, use the [[Talk:Scripting|discussion]] page. | *To ask questions directly related to this page, use the [[Talk:Scripting|discussion]] page. | ||
+ | ==Further reading== | ||
+ | |||
+ | This guide should have given you a quick overview over what can be done with scripts in Freeplane. Of course we have only scratched the surface. Here are some suggestions to dig further into Groovy / Freeplane scripting: | ||
+ | |||
+ | *[http://groovy.codehaus.org/Beginners+Tutorial Groovy tutorials (Codehaus)] | ||
+ | *[http://www.asert.com.au/pubs/groovy/groovy.pdf Groovy presentation (Paul King)] | ||
+ | *[[Scripting: Freeplane Utility Classes|Freeplane utility classes]] | ||
+ | *[[Scripting: Included libraries|Libraries included in Freeplane]] | ||
+ | *[[Scripting: Example scripts|Example script collection]] | ||
+ | *[[Scripting: API Changes]] | ||
+ | *[[Scripting: Update Freeplane's Groovy version|Update Freeplane's Groovy version]] | ||
− | |||
− | |||
* For a list of all articles about Scripting click on "Category:Script" below. | * For a list of all articles about Scripting click on "Category:Script" below. | ||
[[Category:Script]] | [[Category:Script]] |
Revision as of 00:54, 15 March 2013
Freeplane's builtin functionality can be extended by Groovy scripts. This page gives a first impression what you can do with scripting and helps to get started.
With Freeplane scripting you can
- write your own functions and use them from the menu or via keyboard shortcuts,
- use formulas in your map to compute stuff like in Excel, and
- create add-ons to share it with other users.
Most people use scripting to automate otherwise tedious procedures like creating a node with a special style and some standard attributes. But much more is possible with scripting.
Contents
- 1 Preparation
- 2 Select an editor
- 3 The first script: HelloWorld.groovy
- 4 Hello Controller
- 5 Execution modes
- 6 Per node execution: addIcon.groovy
- 7 Status bar again: getIconName.groovy
- 8 Iteration: sumNodes.groovy
- 9 Formulas
- 10 Adding a local link
- 11 Apendix
- 12 Wanted: Your participation!
- 13 Further reading
External Groovy scripts can be integrated simply by placing them in the scripts subdirectory of the Freeplane homedir. Such scripts can be used like any other built-in function of Freeplane.
After some preparation we'll create the first script.
Preparation
A newly installed Freeplane installation is almost ready for scripting:
- The scripts directory is created in the User Configuration Folder which you can open via Tools > Open user directory. It's empty, initially.
- This directory is automatically searched for ".groovy" files on startup.
- Scripting is disabled by default, but we'll fix that in a minute.
First create a new mindmap with this content (just copy 'n paste it into a new map):
test numbers 1 2 3 text text text ok text okay
Then add some icons to the map - no matter how many and which icons. But we'll need them later.
Select an editor
You will need a text editor. For the first steps presented on this page any editor will do, such as Notepad on Windows (though the free Notepad++is much better), TextEdit on Mac OS X, or gedit on Ubuntu Linux. You can find an overview of editors with Groovy support on the Groovy web site.
Freeplane also has a small script editor built into it. It is reached through Tools->Edit Script. You can run the scripts directly in the editor and store them as attributes of the node you are working in. But such map local scripts are most useful for quick tests since you can not write the scripts directly to ".groovy" files.
For ambitious scripting projects or if you have Java/Eclipse know-how you should have a look at the page on Scripting environment setup.
The first script: HelloWorld.groovy
"Hello World" is the traditional first program when taking up a programming language. Let's create a Groovy Freeplane version of it:
- Create an empty Groovy script file named HelloWorld.groovy in your scripts directory (remember that you can get there via Tools > Open user directory). The suffix .groovy is mandatory.
- Open HelloWorld.groovy in an appropriate editor as detailed above.
- Copy the following script into the file and save it.
<groovy> node.text = "Hello World!" </groovy>
- Now save your script in the editor and restart Freeplane since Freeplane will only find new scripts after a restart. Then you will find your new script in the Freeplane menu location Tools->Scripts->Hello World. You see three sub menus Execute on one selected node, Execute on all selected nodes and Execute on all selected nodes, recursively.
- Enable scripting in the preferences at Tools->Preferences->Plugins->Scripting: Set Script execution enabled to Yes. These changes take effect without restarting Freeplane and only need to be done once. For more details see Scripting: Security considerations.
- Execute the script by selecting Tools->Scripts->Hello World->Execute on one selected node. (Never mind the difference between the Execute ... variants; we'll come to that later.)
- The text of the selected node will be changed to "Hello World!".
- To restore the original, press Ctrl-Z.
- If you like try the other "Execute..." menu items. Test the influence of selecting multiple nodes.
Hello Controller
Every script is given the variables
- node - set to the currently selected node.
- c - a tool box for various tasks relating to the map or Freeplane altogether.
These give access to the two most important bits of a map. In HelloWorld we used node, which gave access to the selected node.
Now we'll change HelloWorld.groovy to use the second, the Controller variable c:
- Copy the following script into the file and save it.
<groovy> c.statusInfo = "Hello World!" </groovy>
- Execute the script by selecting Tools->Scripts->Hello World->Execute on one selected node.
The "Controller" manages the status bar. By assigning "Hello World!" to the Controller attribute "statusInfo" we are able to print text to the status bar.
The variables node and c are "objects" with a list of attributes (like "text", "details" or "style") and a list of methods that operate on the object, like "addConnector()", "createChild()" or "moveTo()". The "type" of the object decides on the list of attribute of attributes and methods an object has. "node" is of type Proxy.Node while "c" has the type Proxy.Controller.
To get started with Freeplane scripting you have to get slowly accustomed to the Groovy syntax and the Freeplane specialities too. The types and objects that Freeplane supports are described by the scripting API. You can learn it step by step: Very little is required to write useful scripts. An important resource is the built-in scripting documentation that is available via Help->Scripting API.
In the next section we'll see what the "@ExecutionModes" line is about.
Execution modes
For the "Hello World" script we had three submenu entries of "Hello World". These entries are different with respect to multiple selected nodes:
- In the case of Execute on one selected node a script is executed only once no matter how many nodes are selected. It's best to be used when only a single node is selected since in this case the node variable of the script is set to the selected node. If multiple nodes are selected then node is set to one of the nodes arbitrarily. That is, you shouldn't count on the selection if multiple nodes are selected.
- With Execute on all selected nodes it is called once for each selected node (with node set to the respective node) and with
- Execute on all selected nodes, recursively the selection will be implicitly extended by all child trees of the selected nodes.
If we chose Execute on all selected nodes for "Hello World" then one dialog box would pop up for each selected node. - This clearly would not be intended. By adding the line
<groovy> // @ExecutionModes({ON_SINGLE_NODE}) </groovy>
only one menu entry survives for "Hello World". It's a good idea to put the "annotations" at the beginning of the script. (In section Simple text replacement we will see an exception.) To get the opposite effect, i.e. to exclude the Execute on one selected node we would have to write:
<groovy> // @ExecutionModes({ON_SELECTED_NODE, ON_SELECTED_NODE_RECURSIVELY}) </groovy>
Note that for Groovy this is a comment. - This line is only interpreted by Freeplane. Omitting the // will result in a Groovy compilation error.
Per node execution: addIcon.groovy
Now let's use the node variable again in our next script, addIcon.groovy (restart Freeplane to see it in the menu). This script will add the "button_ok" icon to any selected node. Since the node variable references one selected node we don't have to navigate to them via the controller and we don't have to iterate over them:
<groovy> node.icons.add("button_ok") </groovy>
This will add the "check" icon to each selected node. Hopefully it's clear that the execution mode Execute on one selected node makes no sense in this case. So let's remove this from the "Extra" menu:
<groovy> // @ExecutionModes({ON_SELECTED_NODE, ON_SELECTED_NODE_RECURSIVELY}) node.icons.add("button_ok") </groovy>
(To see the change in the menu you have to restart Freeplane.)
We will extend this script now a little further to only set the icon if the node text contains the words "yes" or "OK" (case insensitively):
<groovy> // @ExecutionModes({ON_SELECTED_NODE, ON_SELECTED_NODE_RECURSIVELY}) if (node.text.toLowerCase().matches(".*\\b(yes|ok)\\b.*"))
node.icons.add("button_ok")
</groovy>
One word about the node.text. This makes use of the special (compared to Java) property handling - see section On Groovy properties and the Scripting API.
Status bar again: getIconName.groovy
Finding the proper name of an icon may be a bit difficult. One way is to use the wanted icon in some map and to look it up in the sources. The XML for a node with an icon might look like that:
<node TEXT="done" ID="ID_789648746" CREATED="1239285242562" MODIFIED="1242658193277"> <icon BUILTIN="button_ok"/> </node>
This scripts writes the icon names of the selected node to the status bar:
<groovy> c.statusInfo = "Icons: " + node.icons.icons </groovy>
Iteration: sumNodes.groovy
Create a script with the following content: <groovy> def sum = c.selecteds.sum{it.to.num0} def sumFormatted = format(sum, "#.##").toString() ui.informationMessage(ui.frame, sumFormatted, "Sum") </groovy>
This is a short script which does quite a lot. Understanding what it does, and how, will show the power of Groovy in Freeplane.
- The first line adds all the numbers in the map together.
- It makes use of the supplied variable c.selecteds which means all of the selected nodes on the map
- And of the Groovy variable it, which means the particular selected node
- And of the Freeplane method to.num0, which takes the numbers in the script and reads them as such, rather than as strings.
- So at the end of the first line, the variable sum holds the value of all the individual numbers (c.selecteds.sum{it.to.num0}) in the selected nodes (c.selecteds.sum{it.to.num0}), added together (c.selecteds.sum{it.to.num0})
- The second line takes this sum, the result of the first line and formats it with at most two fractional digits. It puts the result in a string called sumFormatted.
- The third line shows the result.
- It makes use of the Freeplane helper class ui.informationMessage, and gives the resulting message box a title of "sum",
Formulas
Starting with Freeplane 1.2 one use scripts as Formulas directly in the node core like in Excel. Type this formula in the node core:
<groovy> = "Icons: " + node.icons.icons </groovy>
This will display the result of the formula instead of the formula itself.
Notes:
- The equal sign has to be the very first character in the script.
- On typing the equal sign as the first character a special script editor pops up which supports syntax highlighting.
Adding a local link
To set the link for a node use the node.link attribute:
<groovy> // Set a local link back to parent def newNode = node.createChild('child node') newNode.link.node = node // add a link to the selected node itself node.link.text = 'http://freeplane.org/wiki/index.php?title=Scripting' </groovy>
Apendix
Using external libraries
In Freeplane all external Java libraries can be used. The best way to do it is to create a lib directory in the <freeplane_userdir> and set the "Script classpath" to lib under Tools->Preferences->Plugins. All .class files and the content of all .jar files are automatically available in scripts and formulas.
If you want to create your own little library you have to compile a groovy script as described in this forum post.
Starting with 1.3.2 utility scripts are compiled automatically.
On Groovy
Although Groovy is more or less a superset of Java it would be a shame not to use the new opportunities Groovy provides. On the other hand there are notable differences between Groovy and Ruby. In this section some of the differences between Java, Groovy and Ruby will be listed.
On Groovy properties and the Scripting API
If an object, e.g. Node node, has a method getXyz() then groovy allows to use node.xyz. If it also has a proper setXyz() method (proper in the sense of the JavaBeans specification) then the property is writable.
Example of a read-only property: <groovy> assert node.getId() == node.id println("ok") </groovy>
This will print "ok" into the logfile since the assertion is valid.
Example of a read-write property: <groovy> println(node.text) node.text = "please note!" println(node.text) </groovy>
The second println will print the changed node text.
It's considered better style in Groovy if you use the properties instead of getters and setters. So better use <groovy> c.statusInfo = "Icons: " + node.icons.icons </groovy> instead of <groovy> c.setStatusInfo("Icons: " + node.getIcons().getIcons()) </groovy>
The menu item Help -> Scripting API shows the attributes instead of get/set methods where possible and indicates if the attributes are read-only, read-write or write-only.
The operator == means equals()
In Groovy the operator == is overridden to mean equals(). To check for identity use the method is(): <groovy> Integer i = new Integer(3) Integer j = new Integer(3) assert i == j assert ! i.is(j) </groovy>
Caveat
Note that - unlike in Ruby - it's not allowed to omit the parens of a function without parameters in Groovy. So to get the number of children a node has, use node.children.size(), not node.children.size. The latter would be OK if java.util.List had a method getSize().
Wanted: Your participation!
It's very likely that scripting support lacking some functionality that would be useful for a large number of users. For this reason you are strongly encouraged to give feedback on issues you are having with scripting and on things you are missing.
- For discussions use the Freeplane open discussion forum.
- For bugs and feature requests use the Mantis issue tracker.
- Please add useful scripts to the Scripting examples Wiki page.
- To ask questions directly related to this page, use the discussion page.
Further reading
This guide should have given you a quick overview over what can be done with scripts in Freeplane. Of course we have only scratched the surface. Here are some suggestions to dig further into Groovy / Freeplane scripting:
- Groovy tutorials (Codehaus)
- Groovy presentation (Paul King)
- Freeplane utility classes
- Libraries included in Freeplane
- Example script collection
- Scripting: API Changes
- Update Freeplane's Groovy version
- For a list of all articles about Scripting click on "Category:Script" below.