Scripting

Freeplane's builtin functionality can be extended by Groovy and JavaScript scripts. Starting with Freeplane 1.3.5_05 you can use many other languages, e.g Python. This page gives a first impression what you can do with Groovy 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,

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.

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.

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. Always press Ctrl-Z to revert the changes.

Hello Controller

Every script is given the variables

node

set to the currently selected node

c

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:

c.statusInfo = "Hello World!"

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 scripting API

The variables node and c are "objects" with a list of attributes (like "text", "details" or "style") and 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 defined by Freeplane's 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. Open it now and search for the statusInfo attribute at Scripting API->Proxy->Controller->statusInfo: String (w). The text means: The Controller has an attribute statusInfo that only can be written to (w), that is you can't find out what is currently displayed on the status bar. The attribute has type String (either use "double quotes" or 'single quotes'). If you unfold the node you see void setStatusInfo(String). That means that

c.statusInfo = 'Hello World!'

and

c.setStatusInfo('Hello World!')

are equivalent. But the first "attribute" style is preferable since it is clearer. The clickable links in the "Scripting API" map carry to the respective location in the detailed API description which might be a bit overwhelming at this point.

Setting links

In the "Scripting API" map, near to statusInfo you find the userDirectory attribute. You can use it to add a link to this directory to your map. Create a new script file addLink.groovy in the script directory with the following content:

node.link.file = c.userDirectory

Here an slightly extended version that adds an external link to the selected node(s) and creates a node with a local link back to its parent node:

In the next section we'll see what the "@ExecutionModes" line is about.

Execution modes

For each 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 the first version of "Hello World" then the text of all selected nodes changed. - Probably what you expect. By adding the line

// @ExecutionModes({ON_SELECTED_NODE})

all other choices would be suppressed.

The second "Hello World" version printed to the status bar. This only has to happen once so here Execute on one selected node is the right choice and we have to add the line

// @ExecutionModes({ON_SINGLE_NODE})

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.) ON_SELECTED_NODE_RECURSIVELY applies a script on any node in the branch that has a selected node as root. You can also enable more than one mode by concatening them with commas:

// @ExecutionModes({ON_SELECTED_NODE, ON_SELECTED_NODE_RECURSIVELY})

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:

node.icons.add("button_ok")// @ExecutionModes({ON_SELECTED_NODE})

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:

The method find has a closure argument which is applied to all nodes in the map. All nodes for which the closure returns true are returned as a new list which is assigned to the matches variable. In the closure the "current item" has a default name it. As c.find iterates over nodes it is a Node that has the attribute text which is a String that has a method contains() returning true if OK is contained somewhere in the text, like in "grok" or "it's ok".

Transformation

Many Groovy methods transform lists/collections into others:

def squares = children.collect{ it.to.num0 * it.to.num0}

and others transform lists into single values:

def sumOfSquares = children.sum(0){ it.to.num0 * it.to.num0}

When using sum() it's always a good idea to give it a start value since if the node had no children sumOfSquares would be null instead of 0.

TODO: Tutorial ends here...

Appendix

Using external libraries

Some libraries are already included, but almost all other available Java libraries can be used. Place them in the lib directory in the <freeplane_userdir> which is already included in the "Script classpath" (see also Tools->Preferences->Plugins). All .class files and the content of all .jar files are automatically available in scripts and formulas.

Starting with Freeplane 1.3 utility scripts on the script classpath are compiled automatically.

The add-on scriptlib contains some libraries you can load and install. They include some node operations missing in the scripting API, file operations and a method to play audio with a hidden player.

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 iteration

Groovy provides much improved ways to work on collections of data. This helps a lot in Freeplane scripting since most of the time you are working with collection of Node instances. From Java you might be used to this pattern:

But this code is not even ineffective in Freeplane scripting (since every "c.selecteds" call creates a new list with new wrapped Node instances!) but it might even lead to errors since the list might change on the way. The following is better...

c.selecteds is only evaluated once and there are no redundant variables and method calls.

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:

assert node.getId() == node.idprintln("ok")

This will print "ok" into the logfile since the assertion is valid.

Example of a read-write property:

println(node.text)
node.text = "please note!"println(node.text)

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

c.statusInfo = "Icons: " + node.icons.icons

instead of

c.setStatusInfo("Icons: " + node.getIcons().getIcons())

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():

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.

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: