Scripts collection

From Freeplane - free mind mapping and knowledge management software
Revision as of 20:59, 24 September 2012 by DimitryPolivaev (talk | contribs) (Text editing scripts :: bzr -> git)

The first version of this Scripting article mainly consists/consisted of a port of the FreeMind script collection. Some of those scripts got a general rewrite; - especially those that were marked buggy or even broken. But I took the freedom to (hopefully) improve every script where it made sense. With one exception ("Sum up all subnodes...", a very long script) I have ported all scripts from the FreeMind page, regardless of how useful I found them since I believe that you will be able to learn from all of them.

Unfortunately I do not know some authors of the scripts, so I can't give them proper reference. Please help identify them.

-Volker


Text editing scripts :

Later scripts, mostly for text editing, have been hived off to a separate page

Feel free to go there and add your own scripts. If you give script a name using wiki text like

<groovy name="yourScriptName">
 your script
</groovy>

an extra download button is created for it, and it can be downloaded directly from this page.

For larger scripts there is a special git repository [1].

A node with the last modification date of the map

This script sets the text of the status bar to the last modification timestamp of the whole map.

<groovy name="lastModificationDate"> // @ExecutionModes({ON_SELECTED_NODE}) // find all nodes, collect their lastModifiedAt date and get the latest def lastModifiedAt = c.find{true}.collect{it.lastModifiedAt}.max() def f = new java.text.SimpleDateFormat("yyyy-MM-dd hh:mm:ss") c.statusInfo = "The map was last modified at: " + f.format(lastModifiedAt) </groovy>

Author: User:Boercher

Prepend the modified date at the beginning of the node text

This is a port of the respective script on the FreemindPage. Note that node.lastModifiedAt requires Freeplane 1.0.40 or later

<groovy> =new java.text.SimpleDateFormat("M/d/yyyy").format(node.lastModifiedAt) +

   " " + node.text

// @ExecutionModes({ON_SELECTED_NODE}) </groovy>

Add up attribute values of subnodes.

This script adds up the "AMOUNT" attributes of all child nodes of a node and sets the "AMOUNT" attribute of the selected node to the sum.

<groovy name="sumAttributeValues"> import java.text.NumberFormat;

// global binding attrib = "AMOUNT"

def get(node) {

   def text = node.getAttributes().get(attrib)
   if (text != null && text.isDouble())
       return text.toDouble()
   return 0

} node.getAttributes().set(attrib, NumberFormat.getInstance().format(node.children.sum{get(it)})) // @ExecutionModes({ON_SELECTED_NODE}) </groovy>

With Freeplane 1.2 series and above this script boils down to two lines: <groovy name="sumAttributeValues"> node['VALUE'] = java.text.NumberFormat.getInstance().format(children.sum{it['VALUE'].num0}) // @ExecutionModes({ON_SELECTED_NODE}) </groovy>

The following script does the same but recursively, i.e. called for the root node it updates the complete map so that each parent node has the "AMOUNT" attribute set to the sum of all children, grandchildren and so on.

<groovy name="sumAttributeValuesRecursively"> import java.text.NumberFormat;

// global binding attrib = "AMOUNT"

def get(node) {

   def text = node.getAttributes().get(attrib)
   if (text != null && text.isDouble())
       return text.toDouble()
   return 0

}

def set(node, amount) {

   node.getAttributes().set(attrib, NumberFormat.getInstance().format(amount))

}

def setAndReturnSum(node) {

   if (node.isLeaf()) {
       return get(node)
   }
   else {
       def total = node.children.sum{ setAndReturnSum(it) }
       set(node, total)
       return total
   }

}

setAndReturnSum(node) // @ExecutionModes({ON_SELECTED_NODE}) </groovy>

Author: User:yubrshen, general rewrite by User:Boercher for Freeplane

Sort child nodes alphabetically or by length

<groovy name="sortChildren"> def sorted = new ArrayList(node.children).sort{ it.text } def i = 0 sorted.each {

   it.moveTo(node, i++)

} // @ExecutionModes({ON_SELECTED_NODE, ON_SELECTED_NODE_RECURSIVELY}) </groovy>

To sort by length: Change "it.text" to "it.text.length()".

To reverse either of these sorts: Add a minus sign before "it", i.e. "-it.text.length()".

Author: User:Boercher

Set up a logger

The following code makes use of the application logger provided by LogTool. Note that name and package of this class has changed since version 1.2.

<groovy> import org.freeplane.core.util.LogTool; LogTool.info(" ******* Logger set up for script"); </groovy> In Freeplane 1.2 this simply reads (no import, no static access): <groovy> logger.info(" ******* Logger in 1.2"); </groovy>

Import the structure of a LaTeX document as a mind map

Resulted from discussion in this forum discussion.

Note: This script is not tested - please review it and remove this note if it works.

Author: Cyril Crassin, adapted for Freeplane by User:Boercher <groovy> import javax.swing.JFileChooser

import org.freeplane.plugin.script.proxy.Proxy

/**

* @author Cyril Crassin
* adapted from http://www.icare3d.org/Blogger/Files/FreeMindLatexImportScript.groovy
* by Volker Boerchers
*/

JFileChooser chooser = new JFileChooser('.') chooser.showOpenDialog() File file = chooser.getSelectedFile()

if ( ! file) return

def sectionhierarchy = [ "\\part", "\\chapter", "\\section", "\\subsection", "\\subsubsection", "\\paragraph", "\\subparagraph" ]

Proxy.Node prevNode = node.map.root prevLevel = 0 String curNoteText = "" String lastNoteTextAdd = ""

prevNode.text = "PhDThesis"

file.eachLine{ secFound = false for (i = 0; i<sectionhierarchy.size(); i++) { if (it.contains(sectionhierarchy[i])) { //Flush text if (curNoteText.length() > 0) { //curNoteText = curNoteText+lastNoteTextAdd curNoteText = curNoteText+"</body>\n</html>"

prevNode.text = curNoteText //c.setXmlNoteText(prevNode, curNoteText) } lastNoteTextAdd = "" curNoteText = ""

//Deal with new node openAcc = it.indexOf('{') closeAcc = it.indexOf('}')

curLevel = i sectionName = it.substring(openAcc+1, closeAcc)

while (prevLevel>=curLevel && prevNode != node.map.root) { prevNode = prevNode.parent prevLevel-- } def newNode = prevNode.createChild(sectionName)

prevNode = newNode prevLevel = curLevel

secFound = true break } }

if (!secFound){ if (it.length() > 0 || curNoteText.length() > 0 || lastNoteTextAdd.length() > 0){ if (curNoteText.length() == 0){ curNoteText += "<html>\n<head>\n<style type=\"text/css\">\n \n</style>\n</head>\n<body>" } curNoteText += lastNoteTextAdd

lastNoteTextAdd = "

" + it + "

\n"

} } }</groovy>

Export to BibTeX

A working script for creating BibTeX files from a special formatted mindmap. A BibTeX node has an attribute "bibtex" with the kind of article as value (e.g. "article", "note"). Bibtex entry properties are given as attributes as well. Unfortunately it's currently not possible to write the attribute names other than in lower case.

The resulting BibTeX file is shown in a popup window that supports scrolling and cut 'n paste.

Tested with Freeplane 1.1 and 1.2.

<groovy name="exportToBiBTeX"> import javax.swing.*; import org.freeplane.core.ui.components.UITools;

// for cut 'n paste: def showDialog(String text) {

   def dialog = new JDialog(UITools.getFrame())
   dialog.setSize(350, 450)
   dialog.setLocationRelativeTo(UITools.getFrame())
   dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE)
   dialog.add(new JScrollPane(new JTextArea(text)))
   UITools.addEscapeActionToDialog(dialog)
   dialog.visible = true

}

def processAttribute(attr, attr_name) {

   return (attr.findAttribute(attr_name) != -1) ?
       attr_name + " = \"" + attr.get(attr_name) + "\"" :
       null

}

def process(type, attr, index) {

   def all_tags = ['address', 'author', 'booktitle', 'chapter', 'edition', 'editor',
       'howpublished', 'institution', 'isbn', 'journal', 'month', 'note', 'number',
       'organization', 'pages', 'publisher', 'school', 'series', 'title', 'type',
       'volume', 'year']
   // note for Freeplane version < 1.2: there's no getAttributeNames() yet so
   //  - we can not iterate over *existing* attributes
   //  - we can not match case insensitive
   // -> should be fixed ASAP
   def tags = all_tags.collect{ processAttribute(attr, it) }
   // compact: erase nulls
   return "@" + type + "{${index.toString()}, \n  " +
       tags.grep{ it }.join(",\n  ") +
       '\n}\n\n'

}

index = 1; // merge everything into one string def result = node.find{ true }.sum {

   def attr = it.attributes;
   // valid values would be: article, book, booklet, conference, inbook,
   // incollection, inproceedings, manual, mastersthesis, misc, phdthesis,
   // proceedings, techreport, unpublished
   if (attr.get('bibtex'))
       return process(attr.get('bibtex'), attr, index++);
   else
       return ;

}; showDialog(result) </groovy>

Author: User:Leonardo, general rewrite for Freeplane by User:Boercher


Use the following script to create a map for testing purposes. It adds some "BibTeX nodes" to selected nodes: <groovy name="prepareBiBTeXTestMap"> i = 1 def makeBibEntry(node, type, tags) {

   def child = node.createChild()
   child.attributes.set('bibtex', type);
   tags.each {
       child.attributes.set(it, it + "_" + i++)
   }

}

makeBibEntry(node, 'article', ['author', 'title', 'journal', 'year', 'url']) makeBibEntry(node, 'techreport', ['author', 'title', 'institution', 'year']) makeBibEntry(node, 'misc', ['author', 'url']) makeBibEntry(node, 'proceedings', ['author', 'title', 'year', 'url']) </groovy>

Export Node children as table for latex import

Simple script to export node children organised as two column tabular environment in latex. First Column is filled by child text, second column by latex equation attached to each. I assume you use \import command like:

\begin{table}[H] \input{filename.tex} \end{table}

Requires Freeplane 1.2.

<groovy name="node2LatexTable"> // @ExecutionModes({ON_SINGLE_NODE})

// we can't use LatexExtension.class directly since the class is not public

def buildLatexRow(node,latexExtension){ def result = node.text result = result + " & " + "\$" + latexExtension.equation + "\$" + "\\\\" return result } def findLatexExtension(node) { def result = null node.delegate.extensions.values().each{ if (it.getClass().simpleName == 'LatexExtension') result = it } return result }

def fos= new FileWriter(node.text+'.tex') fos.write("\\begin{tabular}{ll}\n") fos.write("\\hline\n") node.children.each {fos.write(buildLatexRow(it,findLatexExtension(it))+"\n")} fos.write("\\hline\n") fos.write("\\end{tabular}") fos.close() </groovy>

Author: User:catamik,

Sum up all subnodes recursively for attributes

Did not try to port this script due to its length. Tell me if you would like to see it here.

Output A Map to CSV

For export to a spreadsheet for inclusion in a larger project plan. The following script creates CSV output from a mindmap and shows it in a dialog window for cut 'n paste. <groovy name="exportToCSV"> import javax.swing.*; import org.freeplane.core.ui.components.UITools;

// for cut 'n paste: def showDialog(String text) {

   def dialog = new JDialog(UITools.getFrame())
   dialog.setSize(350, 450)
   dialog.setLocationRelativeTo(UITools.getFrame())
   dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE)
   dialog.add(new JScrollPane(new JTextArea(text)))
   UITools.addEscapeActionToDialog(dialog)
   dialog.setVisible(true)

}

def process(thisNode, childPosition) {

   def result = childPosition, thisNode.text
   thisNode.children.each {
       result += process(it, childPosition + 1)
   }
   return result

}

def result = process(node, 0); showDialog(result.collect{ "," * it[0] + it[1] }.join("\n")) </groovy>

Author: User:Boercher

Output A Map to CSV Part 2

The second iteration of the export to CSV script allows different item types to be denoted by icons (here the number icons ("1", "2", ...) which then get mapped to attribute values. That way I can do things like estimate the complexity of a task by adding a number icon and then have that mapped to text like "Very Complex" in my CSV file. Here is the code:

<groovy name="exportToCSV_2"> import org.freeplane.core.util.HtmlTools

class TreeWalker {

   def rootNode;
   def minFontSize;
   private iconMappings = [:]
   private attributeMappings = [:]
   private attributeNames = []
   def visit(thisNode, childPosition) {
       def attributes = [:]
       def nodeName = HtmlTools.htmlToPlain(thisNode.getPlainTextContent())
       def nodeLevel = thisNode.getNodeLevel(true)
       thisNode.icons.icons.each { iconName ->
           if (iconMappings[iconName]) {
               def attributeName = iconMappings[iconName]
               def attributeValue = attributeMappings[attributeName][iconName]
               attributes[attributeName] = attributeValue
           }
       }
       def attributeString = ""
       attributeNames.each {
           attributeString += ",${attributes[it]?attributes[it]:}"
       }
       def nodeType = ""
       if (new Integer(thisNode.style.font.size) < minFontSize) {
           nodeType = "Note"
       } else {
           if (attributes.Complexity) {
               nodeType = "Widget"
           } else {
               nodeType = "Heading"
           }
       }
       println "$nodeType,$nodeLevel,$childPosition$attributeString,\"$nodeName\""
       def i = 0
       if (thisNode.children) {
           thisNode.children.each {
               i++
               visit(it, i)
           }
       }
   }
   def walk() {
       visit(rootNode, 0)
   }
   /**
    * Add a set of mappings from icon names to values for a given attribute
    */
   def addAttributeMapping(attributeName, mappings) {
       attributeMappings[attributeName] = mappings;
       mappings.each {
           iconMappings[it.key] = attributeName;
       }
       attributeNames << attributeName;
   }

}

def walker = new TreeWalker(rootNode:node, minFontSize:12); walker.addAttributeMapping("Complexity",

   [   "full-1":"Very Simple",
       "full-2":"Simple",
       "full-3":"Medium",
       "full-4":"Complex",
       "full-5":"Very Complex",
       "full-6":"Extremely Complex"]);

walker.addAttributeMapping("Type",

   [   "connect":"Interface",
       "page_white_cup":"Java Code",
       "html":"Screen",
       "report":"Report",
       "page_white_text":"Document",
       "cog":"Configuration/Infrastructure Setup"]);

walker.walk(); </groovy>

Author: User:Yellek, prepared for Freeplane by User:Boercher

Daves massivly cheezy Api Generator

If you are lazy (like me) and don't wan't to look through the source code to find the api for the node and c variables, use this to make a somewhat cheezy mindmap/api.

Note: An extended version of this script is part of the Freeplane standard installation.

<groovy name="apiGenerator"> // @ExecutionModes({ON_SINGLE_NODE}) def lastSection(string){

   if (string == null)
       return null
   String[] p = string.split("\\.");
   return p[p.size()-1];

}

def removeModifiers(String[] parts) {

   def result = parts.toList()
   result.remove("final")
   result.remove("abstract")
   return result

}

def newChildNode(node, method){

   def apiNode    = node.createChild(0);
   def returnType = apiNode.createChild(0);
   def iProto     = apiNode.createChild(1);
   def text = method.toString();
   List parts = removeModifiers(text.split(" "));
   StringBuilder sb = new StringBuilder();
   sb.append(parts[0]);
   sb.append(" ");
   sb.append(lastSection(parts[1]));
   returnType.setText(parts[1].replace('Proxy$', ));
   sb.append(" ");
   sb.append(method.getName());
   sb.append("(");
   def Class[] parms = method.getParameterTypes();
   def protoTxt = new StringBuffer();
   if(parms.size() >0){
       for(i in 0..parms.size()-1){
           protoTxt.append(parms[i].toString());
           sb.append(lastSection(parms[i].toString()));
           if(i<parms.size()-1){
               protoTxt.append("\n");
               sb.append(",");
           }
       }
   }
   else{
       protoTxt.append("*none*");
   }
   sb.append(")");
   apiNode.text = sb.toString().replace('Proxy$', );
   apiNode.folded = true;
   iProto.text = protoTxt.toString().replace('Proxy$', );

}

def makeApi(node, clazz) {

   def child = node.createChild()
   child.text = clazz.name.replace('Proxy$', )
   def methods = clazz.getMethods()
   for(i in 0..<methods.size()){
       newChildNode(child, methods[i]);
   }
   child.folded = true

}

makeApi(node, Class.forName('org.freeplane.plugin.script.proxy.Proxy$Attributes')) makeApi(node, Class.forName('org.freeplane.plugin.script.proxy.Proxy$Connector')) makeApi(node, Class.forName('org.freeplane.plugin.script.proxy.Proxy$Controller')) makeApi(node, Class.forName('org.freeplane.plugin.script.proxy.Proxy$Edge')) makeApi(node, Class.forName('org.freeplane.plugin.script.proxy.Proxy$ExternalObject')) makeApi(node, Class.forName('org.freeplane.plugin.script.proxy.Proxy$Font')) makeApi(node, Class.forName('org.freeplane.plugin.script.proxy.Proxy$Icons')) makeApi(node, Class.forName('org.freeplane.plugin.script.proxy.Proxy$Link')) makeApi(node, Class.forName('org.freeplane.plugin.script.proxy.Proxy$Map')) makeApi(node, Class.forName('org.freeplane.plugin.script.proxy.Proxy$Node')) makeApi(node, Class.forName('org.freeplane.plugin.script.proxy.Proxy$NodeStyle')) </groovy>

Author: User:Dke211 (Dave), prepared for Freeplane by User:Boercher

Simple bookmark implementation using scripts

This is a rudimentary implementation of bookmarks. If set up as described below you can use a self chosen key combination to memorize the currently selected node and return to it at any later time using another key combination. A third key combination takes you to the subtree containing the recently stored bookmark and allows you to rename it. This is the basis of managing and accessing multiple named bookmarks.

Step 1: put these three scripts in your ~/.freeplane/scripts directory (or whereever you tell freeplane to look for groovy scripts)

<groovy name="setBookmark"> // @CacheScriptContent(true) def bookmarksNode if((dummyList = c.find{ it.text.equals("Bookmarks")}).isEmpty()) {

   (bookmarksNode = node.getMap().getRootNode().createChild()).setText("Bookmarks")

} else {

   bookmarksNode=dummyList.get(0)

}

if((dummyList = bookmarksNode.find{ it.text.equals("recent")}).isEmpty()) {

   (recentBookmarkNode = bookmarksNode.createChild()).setText("recent")

} else {

   recentBookmarkNode=dummyList.get(0)

}

recentBookmarkNode.getLink().set("#"+c.getSelected().getNodeID()) </groovy>

<groovy name="gotoRecentBookmark"> // @CacheScriptContent(true) def dummyList1 def dummyList2 if(!(dummyList1 = c.find{ it.text.equals("Bookmarks")}).isEmpty()) { if(!(dummyList2 = dummyList1.get(0).find{ it.text.equals("recent")}).isEmpty()) { if((linkString=dummyList2.get(0).getLink().get())!=null){ c.select(node.getMap().node(linkString.substring(1))) } } } </groovy>

<groovy name="gotoBookmarks"> // @CacheScriptContent(true) def dummyList1 def dummyList2 if(!(dummyList1 = c.find{ it.text.equals("Bookmarks")}).isEmpty()) { if(!(dummyList2=dummyList1.get(0).getChildren()).isEmpty()){ c.select(dummyList2.get(0)) } } </groovy>

Step 2: (Re)start freeplane. Choose menu Tools/Scripts/SetBookmark and Ctrl-click on "Execute SetBookmark on one selected node". You are asked to choose a new shortcut key (combination). Do similar for the gotoRecentBookmark.groovy and gotoBookmarks.groovy scripts. Voilà.

Once setBookmark.groovy is executed, a child node named "Bookmark" will be added to the map root node. In the beginning it has one child named "recent". The latter has a local hyperlink pointing to the currently selected node. Executing gotoRecentBookmark.groovy simply follows this link. Everytime you execute setBookmark.groovy this link will be overwritten. You can rename the "recent" node to conserve the link. This is the basis of managing multiple bookmarks. For convenience, executing gotoBookmarks.groovy will take you to the first child of the "Bookmarks" node to quickly access your "Bookmark manager" subtree. From here you can manually follow the links as usual, pressing 'Ctrl-Enter' or klicking the arrow icon. Please feel free to improve the scripts and post improvements here!

Author: User:philshvarcz

Create a map with Firefox bookmarks

This script asks for a Firefox bookmarks-<date>.json file and presents its content as a new mindmap. Requires at least Freeplane 1.2.7

<groovy name="firefoxBookmarks"> // @ExecutionModes({ON_SINGLE_NODE}) import groovy.io.FileType import groovy.json.JsonSlurper import javax.swing.JFileChooser

def filename = ui.showInputDialog(node.delegate

  , "Provide a bookmark-<date>.json file" +
    "\n(see folder bookmarkbackups in Firefox profile directory)",
  , findBookmarkFile())

if (! filename)

   return

def file = new File(filename) if (!file.exists()) {

   ui.errorMessage(filename + " does not exist." +
       "\nIn Firefox 'about:support' shows your profile directory")
   return

} def json = new JsonSlurper().parse(file.newReader('UTF-8'))

def map = c.newMap() map.root.text = file.name def handleEntry(parentNode, Map m) {

   def title = m["title"]
   def children = m["children"]
   if (! title && ! children)
       return
   def child = parentNode.createChild(title ? title : "")
   m.each{ key, value ->
       if (key != "children") {
           child[key] = value
       }
   }
   def uri = m["uri"]
   try {
       // special URIs parts are not properly encoded in Firefox bookmarks
       if (uri != null)
           child.link.text = uri.replace("%s", "%25s").replace("|", "%7C")
   } catch (Exception e) {
       logger.warn("cannot convert " + uri, e)
   }
   if (children)
       children.each{ handleEntry(child, it) }

}

def children = json["children"] if (children != null)

   children.each{ handleEntry(map.root, it) }


def findBookmarkFile() {

   def bookmarkFiles = new TreeSet<File>()
   def locations = [
       System.getenv("APPDATA") + "\\Mozilla\\Firefox\\Profiles"
       , System.getProperty('user.home') + "/.mozilla/firefox"
   ]
   locations.each{ dir ->
       def location = new File(dir)
       if (location.exists()) {
           location.eachFileRecurse{ f ->
               if (f.name.matches("bookmarks[\\d-]*.json"))
                   bookmarkFiles.add(f)
           }
       }
   }
   if (bookmarkFiles)
       return String.valueOf(bookmarkFiles.last()) // youngest
   return ""

} </groovy>

Author: User:boercher


Find all connected nodes

This script will find all nodes below the current node that have connections and list them under a new node created to hold the results called "connected nodes". I found this extremely useful when I have multiple connections reaching across subtrees in a large map. <groovy name="listConnectedNodes"> import java.text.NumberFormat; import org.freeplane.plugin.script.proxy.NodeProxy;

// @ExecutionModes({ON_SELECTED_NODE}) def createNewNode (node, text) {

   def child = node.createChild()
   child.text = text
   return child

}

def getTargetNodes (connectors) {

   connectors.each {
       def targetNode = it.getTarget()
       def sourceNode = it.getSource()
       result = createNewNode(resultNode, sourceNode.text + " --> " + targetNode.text)
   }

}

def getChildrenRecursive(node) {

   if (node.isLeaf()) {
       return [node];
   }
   else {
       // father and sons
       return [node] + node.children.collect{ getChildrenRecursive(it) }.flatten();
   }

}

//******************************************************

thisNode = node resultNode = createNewNode(thisNode,"Connected Nodes") nodeList = getChildrenRecursive(thisNode) nodeList.each {

   if (it.getConnectorsOut().isEmpty()) {
        // do nothing
   }
   else {
       def targets =  it.getConnectorsOut()
       getTargetNodes(targets)
   }

}

</groovy> Author: User:ccrick

Find changed nodes

This script was suggested by Tobias Brown. It creates a new child node of the root node with shallow copies of the first maxCount changed nodes, ordered by modification time, starting with the most recently changed node. Note that the values in the first lines are meant for adjustment to your own preferences. <groovy name="listChangedNodes"> // @ExecutionModes({ON_SINGLE_NODE}) // == Config BEGIN // find all nodes changed since 1970-01-01. Limit the search to the last 48 hours by: new Date() - 2 def changedSince = new Date(0L) // maximum number of displayed nodes def maxCount = 50 // maximum length of cited text def maxLength = 50 // add modification time to the node reference? def addTimestamp = true // add connector to the changed node? def addConnector = false // == Config END

def df = new java.text.SimpleDateFormat("yyyy-MM-dd hh:mm")

// find() returns a unmodifiable list so we have to copy it in order to copy it. // The minus sign reverts the order. def matches = new ArrayList(c.find{ it.lastModifiedAt.after(changedSince) }).sort{ -it.lastModifiedAt.time }

def historyNode = node.map.root.createChild() historyNode.text = matches.size() + " nodes were changed since " + changedSince c.statusInfo = historyNode.text

if (matches.size() > maxCount)

   matches = matches[0..maxCount-1]

matches.each{ node ->

   def child = historyNode.createChild()
   def text = (addTimestamp ? df.format(node.lastModifiedAt) + " " : "") + node.plainTextContent
   child.text = text.length() > maxLength ? text.substring(0, maxLength-1) : text
   if(addConnector)
       child.addConnectorTo(node)
   child.link.set('#' + node.nodeID)

} c.select(historyNode) </groovy> Author: User:boercher


Pomodoro Timer

To encourage focusing on doing concrete tasks, and accumulate Pomodoro credits, this script provides a Pomodoro timer against a node in mind-map. When the time reaches the break time, plays sound of applause for encouragement, and when the time is up, add the time Pomodoro time credit to the current node's attribute with the name of 'Pomodoro', and add to the root node's attribute with name of today's date.

Need to customize the path to the audio file "APPLAUSE.WAV" in function playSound.

<groovy name="pomodoro-timer"> import groovy.swing.SwingBuilder import java.awt.FlowLayout as FL import javax.swing.BoxLayout as BXL import javax.swing.JFrame import groovy.beans.Bindable import java.util.timer.* import java.applet.Applet; import java.applet.AudioClip; //import org.freeplane.plugin.script.proxy.Node

/*

To encourage focusing on doing concrete tasks, and accumulate Pomodoro credits, this script provides a Pomodoro timer against a node in mind-map. 
When the time reaches the break time, plays sound of applause for encouragement, 
and when the time is up, add the time Pomodoro time credit to the current node's attribute with the name of 'Pomodoro', 
and add to the root node's attribute with name of today's date. 
Need to customize the path to the audio file "APPLAUSE.WAV" in function playSound
Developed by Yu Shen based on examples of SwingBuilder in Freeplane Forum, and other examples of timer tasks, and @Bindable found in Internet
2012-01-01
  • /

class CountDown { // resolution: second

 int delay = 1 // seconds
 int period = 1 // seconds
 int countDownValue = 25*60 // seconds, the total value of the countDown, per Pomodoro Techniques: http://www.pomodorotechnique.com/
 int remainingTime = countDownValue // seconds, multiple of 60 (for minute)
 int breakTime = (int) (countDownValue/5) // seconds, time for break
 int countDownTime = remainingTime
 boolean running = true
 //Node node
 def node
 TimerTaskCountDown task
 Timer timer
 CountDown (// Node
   nodeIn) { 
   node = nodeIn
   task = new TimerTaskCountDown(this)
   timer =  new Timer()
   timer.scheduleAtFixedRate(task, delay*1000, period*1000)
 }
 
 // adaptive display for count down time, and available operation
 @Bindable String timeStr = "25:00"
 @Bindable String buttonName = 'Pause'
 public int getElapsedTime () {
   (int) ((countDownTime - remainingTime)/60) // convert to minutes
 }
 def getValue(node, attrib) {// get valid attribute value
   def value = node.getAttributes().get(attrib)
   (value != null) ? value : 0
 }
 void accumulateTime () { // add Pomodoro credit against current node and the root node for today's attribute
   int elapseTime =  getElapsedTime()
   if (elapseTime > 0) {
     node['Pomodoro'] = getValue(node, 'Pomodoro') + elapseTime ;
     def rootNode = node.getMap().getRootNode()
     def today = new Date()
     def formattedDate = today.format('yyyy-MM-dd')
     rootNode[formattedDate] = getValue(rootNode, formattedDate) + elapseTime
   }
 } 
 public String secondToString(x) {
   int seconds = x % 60 ;
   int minutes =(int) (x / 60);
   return String.format("%02d:%02d", minutes, seconds)
 }
 public start() {
   // println "Start"
   remainingTime = countDownValue
   countDownTime = remainingTime
   running = true
   // no cancel, thus no restart
   // timer =  new Timer()
   // task = new TimerTaskCountDown(this)
   // timer.scheduleAtFixedRate(task, delay*1000, period*1000)
   setButtonName('Pause')
 }
 void end () {
   accumulateTime ()
   // it would be more saving of CPU, slightly, to cancel the timer, and tasks. 
   // But my experiment didn't work. It caused the Swing Widget disappear. I'm not sure why. 
   //task.cancel()
   //timer.cancel()
   setButtonName('Start');
   running = false
 }
 private static void playSound () {
   // the clip is a bit too long, annoying.
   // TODO: wish to find a way to set the current directory to the script directory, so that the script can be applicable without modification 
   System.setProperty("user.dir", "d:\\yushen\\scripts");
   java.io.File file = new java.io.File("APPLAUSE.wav");
   AudioClip sound = Applet.newAudioClip(file.toURL());
   sound.play();
   println "Played sound."
 }
 
 public void update() {// for timer task
   if (running) {
     if (remainingTime >= period) {
       remainingTime -= period
     } else { 
       end()
     }
     setTimeStr(secondToString(remainingTime))
     // println "remainingTime: $remainingTime and breakTime: $breakTime"
     if (remainingTime == breakTime) {// play sound to congratulate
       playSound ()
     }
   }
 }
 public void react () {
   if (running) {// pause
     end ();
   } else {// start
     start()}}

}

class TimerTaskCountDown extends TimerTask {

 CountDown model
 public TimerTaskCountDown (CountDown model) {
   // super() // not neccessary
   this.model = model
 }
 public void run() {
   model.update()
 }  

}

CountDown model = new CountDown(node)

def s = new SwingBuilder() // s.setVariable('myDialog-properties',[:]) // seems not neccessary def dial = s.dialog(title:'Pomodoro', id:'pormodoro', modal:false, defaultCloseOperation:JFrame.DISPOSE_ON_CLOSE, pack:true, show:true) {

 panel() {
   boxLayout(axis:BXL.Y_AXIS)
   panel(alignmentX:0f) {
     flowLayout(alignment:FL.LEFT)
     label text: node.text + ': '
     label text: bind {model.timeStr }
   }
   panel(alignmentX:0f) {
     flowLayout(alignment:FL.RIGHT)
     button (text: bind {model.buttonName}, actionPerformed: {model.react(); }) 
   }
 }

}

</groovy>

Author: User:yubrshen

Kill Empty Nodes

When pasting text from clipboard, Freeplane would split the content by newline, thus it may produce some empty node without text at all for those multiple newlines. This script removes all those empty nodes.

You can execute the script at an empty node, or at a node and recursively for its sub-nodes.

<groovy name="KillEmptyNode">

/*	Test the current node if it's empty, and it does not have any sub-nodes, 
 	then delete it.

The node is empty, if it has no non-blank text.*/

def killEmptyNode (aNode){

 nonEmpty = /\w+/
 nodeText = aNode.getPlainText()
 // println nodeText
 if ((aNode.children.size () <= 0) &
     !(nodeText =~ nonEmpty)) {
   // println "Found an empty node!"
   aNode.delete()

} }

killEmptyNode (node) </groovy>

Author: User:yubrshen

Create a Today GTD simple framework

discussion here : The Freeplane Task Time Tracker

<groovy name="CreateTodayGTD"> // @ExecutionModes({ON_SINGLE_NODE}) //ui.informationMessage(ui.frame, temp)

//import java.awt.Color; import org.freeplane.plugin.script.proxy.Proxy.Node

// ========================= Config BEGIN ========================= // create as child of selected node or child of root node ? "true" = selected node / "false" = root node def fromSelectedNode = "false" // Name of containing node def newNodeName = "Agenda" // Number of days to create def daysToCreate = 1 // Maximum number of nodes to remain unfolded def unfoldedNodes = 1 // choose your prefered date format : (ex : "yyyy-MM-dd hh:mm") def dateFormat = "dd/MM/yy" // define edge color def String edgeColor = "#006699" // define edge width def edgeWidth = 2 // assign edge color like parent node ? "true" / "false" parentEdgeColor = "false" // assign edge width like parent node ? "true" / "false" parentEdgeWidth = "true" // define recurrent items within each day def daysCategories = ["Todo" , "Delegated To", "Phone Calls", "Appointments", "Meetings", "Events", "New Inbox"] // ========================= Config END =========================

// color information : http://chir.ag/projects/name-that-color/#6183ED // Colors codes used: clr = [ "BahamaBlue":"#006699"

       , "WildSand":"#f5f5f5"
       , "Gray":"#999999"
       , "VerdunGreen":"#336600"
       , "Orange":"#ff9933"

, "Black":"#000000" , "Yellow":"#ffff00" , "White":"#ffffff" , "Red":"#ff0000"

       , "Pink":"#ff9999"]

// Functions def void formatNode(Node node, String nodeTextColor, String nodeBgColor, nodeFontSize, String edgeColor, edgeWidth) {

   node.style.font.bold = 0
   node.style.setTextColorCode(nodeTextColor)
   node.style.setBackgroundColorCode(nodeBgColor)
   node.style.font.size = nodeFontSize
   node.style.edge.setColorCode(edgeColor)
   node.style.edge.setWidth(edgeWidth)

}

def today = new Date() def f = new java.text.SimpleDateFormat(dateFormat) todaySimple = f.format(today)

def nextday = today

if ( parentEdgeColor == "true") edgeColor = c.selected.style.edge.getColorCode()

if ( parentEdgeWidth == "true") edgeWidth = c.selected.style.edge.getWidth()

def newNode if ( fromSelectedNode == "true"){ newNode = c.selected.createChild() } else { newNode = node.map.root.createChild() }

newNode.text = newNodeName //newNode.style.name = "Step"

formatNode(newNode, clr.White, clr.Red, 14, edgeColor , edgeWidth)

i = 0 while ( i++ < daysToCreate ) { def child = newNode.createChild() child.text = f.format(nextday) child.attributes = [ "totalJournee" : ""] daysCategories.each() { def cat = child.createChild() cat.text = it }

if (i > unfoldedNodes) child.setFolded(true) nextday++ } def todayCats c.find{

   it.text == todaySimple

}.each { formatNode(it, clr.Black, clr.Yellow, 13, edgeColor , edgeWidth) c.select (it) it.setFolded(false) todayCats = it.getChildren() }

todayCats.each{ if (it.text == "Todo") it.text = "ToDoNow" }

</groovy>

Author: User:Megatop


The Freeplane Task Time Tracker

This script opens a form with

discussion here : The Freeplane Task Time Tracker

condition : you have to run the CreateTodayGTD.groovy script first (see above)

<groovy name="FreeplaneTaskTimeTracker"> // @ExecutionModes({ON_SINGLE_NODE})

import groovy.time.* import java.util.Timer; import javax.swing.BorderFactory import java.awt.Color import groovy.swing.SwingBuilder import java.awt.FlowLayout as FL import javax.swing.BoxLayout as BXL import javax.swing.JFrame import javax.swing.JScrollPane import javax.swing.JTabbedPane import javax.swing.JOptionPane import java.awt.Font import javax.swing.ImageIcon

def colorWhite = new Color(255, 255, 255) // white def colorBlack = new Color(0, 0, 0) // black def colorRed = new Color(255, 0, 0) // red def colorBlue = new Color(225, 235, 254) // light blue def colorYellow = new Color(240, 250, 208) // light yellow def colorRose = new Color(245, 230, 229) // light rose def colorGreen = new Color(206, 253, 218) // light green def colorOrange = new Color(253, 233, 206) // light orange def colorPurple = new Color(246, 192, 251) // light purple def colorGray = new Color(255,255,255) // gray

def tabCenter = 28

def f = new java.text.SimpleDateFormat( "dd/MM/yy HH:mm:ss" ) def fDay = new java.text.SimpleDateFormat( "dd/MM/yy" ) def fTime = new java.text.SimpleDateFormat( "HH:mm:ss" ) def fDuree = new java.text.SimpleDateFormat( "HH:mm" ) def fMoisAnnee = new java.text.SimpleDateFormat( "MMMM yyyy", new Locale('FR'))

dateJour = fDay.format(new Date())

c.find{ it.text == dateJour }.each { today = it} today.setFolded(false)

todayChillen = today.getChildren() todayChillen.find{it.text == "ToDoNow"}.each { ToDoNow = it c.select(it) it.setFolded(false) }

listeNextActions = ToDoNow.getChildren()

listeNextActions.each {

it['debut'] = "" it['arret'] = "" it['fin'] = "" it['total'] = "" it['totalJour'] = ""

}

def totalJournee = today['totalJournee'] def tempsTotalJournee = elapsedToMillis(totalJournee)

c.find{ it.text == "ToDos"}.each{inbox = it}

def elapsedTime(diff, precision, format){

def time

long secondInMillis = 1000; long minuteInMillis = secondInMillis * 60; long hourInMillis = minuteInMillis * 60; long dayInMillis = hourInMillis * 24; long yearInMillis = dayInMillis * 365;

long elapsedYears = diff / yearInMillis; diff = diff % yearInMillis; long elapsedDays = diff / dayInMillis; diff = diff % dayInMillis; long elapsedHours = diff / hourInMillis; diff = diff % hourInMillis; long elapsedMinutes = diff / minuteInMillis; diff = diff % minuteInMillis; long elapsedSeconds = diff / secondInMillis; diff = diff % secondInMillis;

elapsedSecondsString = String.format("%02d", elapsedSeconds) elapsedMinutesString = String.format("%02d", elapsedMinutes) elapsedHoursString = String.format("%02d", elapsedHours)

if (precision == "sec" && format == "long") time = elapsedHoursString + " heures, " + elapsedMinutesString + " minutes, " + elapsedSecondsString + " secondes" if (precision == "sec" && format == "court") time = elapsedHoursString + " h " + elapsedMinutesString + " m " + elapsedSecondsString + " s"

if (precision== "min" && format == "long") time = elapsedHours + " heures, " + elapsedMinutes + " minutes" if (precision== "min" && format == "court") time = elapsedHours + " h " + elapsedMinutes + " m"

if (precision== "pomodoro" && format == "court") time = elapsedMinutesString + " m " + elapsedSecondsString + " s" return time } def elapsedToMillis(elapsed){

def elapsedInMillis if (elapsed == ""){ elapsedInMillis = 0 }else{ def elapsedHours = Integer.parseInt(elapsed[0..1]) def elapsedMinutes = Integer.parseInt(elapsed[5..6]) def elapsedSeconds = Integer.parseInt(elapsed[10..11]) elapsedInMillis = (elapsedSeconds * 1000) + (elapsedMinutes * 60 * 1000) + (elapsedHours * 60 * 60 * 1000) } return elapsedInMillis }

Font font = new Font("Serif", Font.BOLD, 13) Font pom = new Font("Serif", Font.BOLD, 28)

def s = new SwingBuilder() s.setVariable('myDialog-properties', [:]) def vars = s.variables def dial = s.dialog(title: 'Freeplane Task Time Tracker', id: 'myDialog', size: [930, 542], locationRelativeTo: ui.frame, owner: ui.frame, defaultCloseOperation: JFrame.DISPOSE_ON_CLOSE, visible:true, show: true ) {

tabbedPane(id: 'tabs', opaque : false, tabLayoutPolicy:JTabbedPane.SCROLL_TAB_LAYOUT) { tabs.setFont(font)

def actionsTabName = "Actions : " + listeNextActions.size().toString() panel(name: actionsTabName.center(tabCenter) , background: colorWhite, foreground: colorBlue) { boxLayout(axis: BXL.Y_AXIS)

def totalTachesVal = "Total : " + listeNextActions.size().toString() + " actions"

def totalJour def total

def initNextActionID def initPanelID

def lastAction = "Stop"

j = -1 while ( j++ < listeNextActions.size() -1 ) { initNextActionID = listeNextActions[j].getId() initPanelID = "panel$initNextActionID"

c.find{ it.getId() == initNextActionID }.each { totalJour = elapsedToMillis(it['totalJour']) tempsTotalJournee += totalJour } }

tempsTotalJourneeVal = (elapsedTime(tempsTotalJournee, "sec", "long")).toString()

panel(id : 'summary', alignmentX: 0f, preferredSize: [900, 40]){ boxLayout(axis: BXL.X_AXIS) panel(alignmentX: 0f, background: colorWhite) { flowLayout(alignment: FL.LEFT) label ( dateJour, preferredSize: [104, 24]).setFont(font) totalTaches = label (totalTachesVal, preferredSize: [150, 24]).setFont(font) }

panel(alignmentX: 0f, background: colorWhite) { flowLayout(alignment: FL.RIGHT) label (id :'totalTemps',text: "Temps total : $tempsTotalJourneeVal", preferredSize: [270, 24]).setFont(font) } } scrollPane( verticalScrollBarPolicy:JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED ,background: colorWhite) { vbox { i = -1 while ( i++ < listeNextActions.size() -1 ) {

def nextAction = listeNextActions[i].text def nextActionID = listeNextActions[i].getId() def thisNode = listeNextActions[i] def panelID = "panel$nextActionID" def doneID = "donedone$nextActionID"

def debutID def finID def diffID

c.find{ it.getId() == nextActionID }.each { if( it['totalJour'] != "") diffID = it['totalJour'].toString()

if( it['fin'] != "" ){ s."$doneID" = "checked" }else{ s."$doneID" = "unchecked" } }

thisActionOriginId = thisNode['ID'] c.find{ it.getId() == thisActionOriginId.toString() }.each { def note = it.note def details = it.details.getPlain() }

def debutID_Val = "debut" + "$i" + "Val" def debutID_ValTime = "debut" + "$i" + "Time"

def finID_Val = "fin" + "$i" + "Val" def finID_ValTime = "fin" + "$i" + "Time"

panel( id: '$panelID' , alignmentX: 0f ) { boxLayout(axis: BXL.X_AXIS)

def pane = panel(id: panelID , alignmentX: 0f , preferredSize: [100, 35], background: colorBlue , border: BorderFactory.createMatteBorder(0, 0, 1, 0, colorGray)) { flowLayout(alignment: FL.LEFT) checkBox(id:'check$i', preferredSize: [20, 24], opaque: false, selected: (s."$doneID" == "checked" ? true : false), actionPerformed: {

c.find{ it.getId() == nextActionID }.each { c.select(it) if (it['fin'] == ""){ s."$doneID" = "checked" it['fin'] = it['arret'] s."$panelID".background = colorGreen }else{ it['fin'] = "" s."$doneID" = "unchecked" s."$panelID".background = colorBlue } } }) if (s."$doneID" == "checked"){ s."$panelID".background = colorGreen }else{ s."$panelID".background = colorBlue } label(id :'todo$i', text: nextAction, preferredSize: [530, 24]) button( id:'startButton$i', text:'Start', preferredSize: [60, 24], actionPerformed: {

if (lastAction == "Stop" && s."$doneID" == "unchecked"){

lastAction = "Start" activeID = nextActionID

s."$panelID".background = colorYellow

debutID_Val = new Date() debutID.text = fTime.format(debutID_Val) debutID_ValTime = debutID_Val.getTime() c.find{ it.getId() == nextActionID


}.each { c.select(it) if( it['debut'] == "") it['debut'] = f.format(debutID_Val) finID.text = "" } } })

button( id:'stopButton$i', text:'Stop', visible: true, preferredSize: [60, 24], actionPerformed: {

if (lastAction == "Start" && activeID == nextActionID && s."$doneID" == "unchecked"){

lastAction = "Stop" currentActionID = nextActionID j = -1 while ( j++ < listeNextActions.size() -1 ) { initNextActionID = listeNextActions[j].getId() initPanelID = "panel$initNextActionID" if (s."$doneID" == "checked"){ s."$panelID".background = colorGreen }else{ s."$panelID".background = colorBlue } } s."$panelID".background = colorRose

finID_Val = new Date() finID.text = fTime.format(finID_Val) finID_ValTime = finID_Val.getTime()

c.find{ it.getId() == nextActionID }.each { c.select(it) difference = finID_ValTime - debutID_ValTime it['arret'] = f.format(finID_Val)

totalJour = elapsedToMillis(it['totalJour']) totalJour += difference it['totalJour'] = (elapsedTime(totalJour, "sec", "court")).toString()

diffID.text = elapsedTime(totalJour, "sec", "court")

total = elapsedToMillis(it['total']) total += difference it['total'] = (elapsedTime(total, "sec", "court")).toString()

tempsTotalJournee += difference tempsTotalJourneeVal = (elapsedTime(tempsTotalJournee, "sec", "long")).toString() totalTemps.text = "Temps total : $tempsTotalJourneeVal" } } })

debutID = label(debutID , preferredSize: [50, 24]) finID = label(finID , preferredSize: [50, 24]) diffID = label(diffID , preferredSize: [70, 24]) } } } } } } panel(name: 'Delegated To'.center(tabCenter)) { borderLayout() splitPane() { panel(constraints: 'left') { label(text: 'Left') } panel(constraints: 'right') { label(text: 'Right') } } } panel(name: 'Phone Calls'.center(tabCenter)) { textField(text: 'Some text', columns: 15) scrollPane() { textArea(text: 'Some text', columns: 15, rows: 4) } } panel(name: 'Appointments'.center(tabCenter)) { borderLayout() splitPane() { panel(constraints: 'left') { label(text: 'Left') } panel(constraints: 'right') { label(text: 'Right') } } } panel(name: 'Meetings'.center(tabCenter)) { borderLayout() splitPane() { panel(constraints: 'left') { label(text: 'Left') } panel(constraints: 'right') { label(text: 'Right') } } } panel(name: 'Events'.center(tabCenter)) { borderLayout() splitPane() { panel(constraints: 'left') { label(text: 'Left') } panel(constraints: 'right') { label(text: 'Right') } } }

panel(name: 'Inbox'.center(tabCenter)) { boxLayout(axis: BXL.Y_AXIS) panel( ){ label('new entry').setFont(font) button('ajout TodoNow', actionPerformed:{ // deactivated }) input = textField(columns:87, background: colorYellow, actionPerformed: { output.text += input.text + "\n" input.text ="" }) } panel(alignmentY: 0f,){ scrollPane(verticalScrollBarPolicy:JScrollPane.VERTICAL_SCROLLBAR_ALWAYS) { output = textArea(id="liste", text: , columns: 110, rows: 18) } } } } def togglePomPanel def task

boxLayout(axis: BXL.Y_AXIS) panel(id:'Pomodoro', background: colorWhite){

togglePomPanel = "OFF" //button('Pomodoro',icon:new ImageIcon("pomodoro30x30-tr.png"), actionPerformed:{

button('Pomodoro', actionPerformed:{ if (togglePomPanel == "OFF"){ Pomodoro.background = colorRed pomodoroLabel.setForeground(colorWhite) togglePomPanel = "ON"


def pomodoroVal = pomodor.selectedItem def pomodoro = pomodoroVal*60000

use (TimerMethods) { def timer = new Timer() def fPomo = new java.text.SimpleDateFormat( "mm:ss" )

pomodoroLabel.text = ' ' +(elapsedTime(pomodoro, "pomodoro", "court")).toString() pomodoro -= 1000

task = timer.runEvery(1000, 1000) {

while ( pomodoro > -1000 ){ pomodoroLabel.text = ' ' + (elapsedTime(pomodoro, "pomodoro", "court")).toString() break } pomodoro -= 1000

if (pomodoro < 0 && togglePomPanel == "ON"){ Pomodoro.background = colorWhite togglePomPanel = "OFF" pomodoroLabel.setForeground(colorBlack) task.cancel() } } } } }) comboBox( id: 'pomodor', items: [60,45,25,15,10,5,2,1], selectedIndex: 2, preferredSize: [50, 24] ) label(id : 'pomodoroLabel' , ' 00 m 00 s').setFont(pom) label(icon:new ImageIcon("Progress_quarter_03.svg")) }

panel(id:'secondPanel', background: colorWhite){ button('End of session : update and quit', actionPerformed:{ ui.informationMessage(ui.frame, "this should update each tasks duration attributes and manage archiving of past tasks" ) // dispose() }) } }

// Mr Haki File: newtimer.groovy // http://mrhaki.blogspot.com/2009/11/groovy-goodness-run-code-at-specified.html

class GroovyTimerTask extends TimerTask {

   Closure closure
   void run() {
       closure()
   }

}

class TimerMethods {

   static TimerTask runEvery(Timer timer, long delay, long period, Closure codeToRun) {
       TimerTask task = new GroovyTimerTask(closure: codeToRun)
       timer.schedule task, delay, period
       task
   }

} </groovy> Author: User:Megatop

Scripts that need external libraries

With external Java libraries you can explore completely new application fields with Freeplane and accomplish amazing things. Remember: Groovy is fully Java compatible, i.e. you can use nearly any available Java library.

General installation instructions for external Java libraries

Java libraries normally come in form of .jar files which you have to make known to the application that wants to use them. Here's the way to do that in Freeplane:

1. Create a folder lib in your Freeplane user directory

2. Copy the .jar file to that directory.

3. Start Freeplane and add lib to Preferences -> Plugins -> Scripting -> Script -> Script classpath

4. Save and restart Freeplane

To install further .jar files in the future you just have to copy the .jar files to that lib directory and restart Freeplane.

Database access: Oracle

Test script that shows how to connect to an Oracle database. Thanks to Pascal for asking.

Installation instructions:

1. Save the script to the scripts folder in your Freeplane user directory and edit USERNAME/PASSWORD and the rest of the database url appropriately.

2. Follow the instructions at the beginning of this chapter to install ojdbc14-10.1.0.4.0.jar (or later) to the lib directory in your Freeplane user directory

Required permissions: Execute script, read file, network access. Requires Freeplane 1.2.

<groovy name="databaseTestOracle"> // @ExecutionModes({ON_SINGLE_NODE}) import java.sql.Connection import groovy.sql.Sql import oracle.jdbc.pool.OracleDataSource

// def driver = Class.forName("oracle.jdbc.driver.OracleDriver") // println driver.name

OracleDataSource ds = new OracleDataSource(); ds.setURL("jdbc:oracle:thin:USERNAME/PASSWORD@//localhost:1521/xe") def sql = new Sql(ds.getConnection())

def mviews = node.map.root.createChild("materialized views") sql.eachRow("select * from user_tables where table_name like 'MVIEW%'", { mviews.createChild(it.table_name)} ); </groovy>

Author: User:boercher

Database access: MySQL

Test script that shows how to connect to a MySQL database. Thanks to Pascal and Michel.

Installation instructions:

1. Save the script to the scripts folder in your Freeplane user directory and edit database access properties appropriately.

2. Follow the instructions at the beginning of this chapter to install mysql-connector-java-5.1.17-bin.jar (or similar) to the lib directory in your Freeplane user directory

Required permissions: Execute script, read file, network access. Requires Freeplane 1.2.

<groovy name="databaseTestMysql"> // @ExecutionModes({ON_SINGLE_NODE}) import java.sql.Connection; import groovy.sql.Sql import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

MysqlDataSource ds = new MysqlDataSource();

ds.setServerName("localhost"); ds.setUser("root"); ds.setPassword(""); ds.setDatabaseName("cdcol");

def sql = new Sql(ds.getConnection())

def titles = node.map.root.createChild("Titles") sql.eachRow("select * from cds"){

   titles.createChild(it.title)

} </groovy>

Author: User:megatop, User:boercher

JFreeChart: Diagrams and Charts

Example how to use JFreeChart in Freeplane. Thanks to Michel for asking. See also Gantt chart example.

Installation instructions:

Follow the instructions at the beginning of this chapter to install jfreechart-<version>.jar and jcommon-<version>.jar (download) to the lib directory in your Freeplane user directory

Required permissions: Execute script, read file. Requires Freeplane 1.2.

<groovy name="jfreechartTest"> // @ExecutionModes({ON_SINGLE_NODE}) import org.jfree.chart.ChartFactory import org.jfree.chart.ChartPanel import org.jfree.data.general.DefaultPieDataset import groovy.swing.SwingBuilder import java.awt.* import javax.swing.WindowConstants as WC

def piedataset = new DefaultPieDataset(); piedataset.with {

    setValue "Apr", 10
    setValue "May", 30
    setValue "June", 40

}

def options = [true, true, true] def chart = ChartFactory.createPieChart("Pie Chart Sample",

   piedataset, *options)

chart.backgroundPaint = Color.white def swing = new SwingBuilder() def frame = swing.frame(title:'Groovy PieChart',

       defaultCloseOperation:WC.EXIT_ON_CLOSE) {
   panel(id:'canvas') { widget(new ChartPanel(chart)) }

} frame.pack() frame.show() </groovy>

Author: User:boercher