User-contributed example scripts

From FreeMind
Jump to navigationJump to search

What follows are example scripts have been contributed by FreeMind users; use them at your own risk and with caution.

Example scripts by Christian Foltin, one of the main FreeMind autors, are available in the help mind map, and also wikified in Scripting/Mind map documentation. See also Scripting.

A node with the current time

If you need a node that shows the last saving time, just add the following script and start it once each time, you are opening the map for editing.

<groovy> import freemind.modes.MindMapNode; import freemind.modes.mindmapmode.MindMapController; import java.util.Timer;

class DateTimer extends java.util.TimerTask { MindMapNode mNode; MindMapController mC; DateTimer(node, c) { mNode = node; mC = c; }

void run() { mNode.setText("Save Time: " + new java.util.Date()); mC.nodeRefresh(mNode); } }

t = new Timer(); t.schedule(new DateTimer(node, c),2000,2000); </groovy>

WARNING! I have now played with Groovy for 30 minutes and I am unsure of the side effects to FreeMind of using these examples, as they modify children of a node. - Dave Torok

Prepend the modified date at the beginning of the node text

This is an example of using some more Node methods.

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

  + " " + node.getText()</groovy>

Add up all subnodes and set them into the first row, second column.

Copy the script into subnodes if you want go more than one level. As far as I can tell you can't grab the values by attribute name, just by row / column, but I didn't spend forever looking over the source.. (Maybe someone could push some javadoc?)


<groovy> def i = node.childrenUnfolded(); def total=0; while (i.hasNext()) { d = i.next(); def val=d.getAttributes().getValueAt(0,1); if (val!=null) total=total+java.lang.Integer.parseInt(val); }; node.getAttributes().setValueAt(total, 0,1);</groovy>

The above code has a problem that it will let leaf node and its parent nodes with the script to yield 0 value. The following code would work recursively.

<groovy> if (!node.isLeaf()) {def i = node.childrenUnfolded(); def total=0; while (i.hasNext()) { d = i.next(); def val=d.getAttributes().getValueAt(0,1); if (val!=null) total=total+java.lang.Integer.parseInt(val); }; node.getAttributes().setValueAt(total, 0,1);};</groovy>

--yushen 15:35, 15 Jan 2007 (PST)



Set the color for all children

This is an example of iteration over child nodes. Also, a call to c.nodeStructureChanged(node) is necessary to force a refresh to paint the new color.

<groovy> def i = node.childrenUnfolded(); while (i.hasNext()) { d = i.next(); d.setColor(java.awt.Color.BLUE); }; c.nodeStructureChanged();</groovy>

Swap two CHILD nodes

This works. My goal is to get a foundation for a SORT script, to sort children by alphabet, by created date, etc. until the functionality is added to the software. WARNING WARNING WARNING this is just an example to swap the 2nd and 4th child nodes.

<groovy>def swap (a,b) {

   na = node.getChildAt(a);
   nb = node.getChildAt(b);
   node.remove(a); 
   node.remove(b - 1); 
   node.insert(nb,a);
   node.insert(na,b);
   c.nodeStructureChanged(node);

};

swap(1,3);</groovy>

Add/Replace today

<groovy> =nt=node.toString(); pos=nt.indexOf(" / "); return ((pos>=0)?nt.substring(0,pos):nt) + " / "

  + (new java.text.SimpleDateFormat("yyyy-M-d")).format(new Date())

</groovy>

Set up a logger

The following code, set up at the beginning of your script, will set up a logger that you can use to follow the progress of your script while you are working on it. You can find details on how to use the Java Logger at http://java.sun.com/javase/6/docs/api/

Be sure to read section 1.8 on how to set up your configuration file. If you don't get anything out of the logger, that's one of the likely sources of trouble.

<groovy>

 def logger = c.getFrame().getLogger(this.getClass().getName());
 logger.info(" ******* Logger set up for script");

</groovy>


Export to bibtex

This is an attempt to make a script that takes a mindmap formatted in a certain way and produces a bibtex file out of each node (each node has the correct fields to be parsed as a bibtex entry). I have a problem, the script exits from the main switch(attr['Type']), after calling any print*() function. The function is executed, the file first entry is written but after the call I have a "java.lang.NullPointerException: Cannot invoke method call() on null object" error. I'm a groovy programmer with 5hours experience, so I might be missing something easy. Can somebody help to make this script work? here you can find a suitable mm file.

<groovy> import freemind.modes.MindMapNode;

def attrMap = { node, f ->

   def attrs = [:];
   for(i in 0..<node.getAttributes().getRowCount()) {
       attrs[node.getAttributes().getName(i)] = node.getAttributes().getValue(i);
   };
   return attrs;

};

def printConference (f, attr, index){ f << "@InProceedings{ ${index.toString()},\n"; attr.each{ f << "switch"; if(it.key != null){ switch(it.key){ case 'Authors': f << "author = \"${it.value}\",\n"; break case 'Title': f << "title = \"${it.value}\",\n"; break case 'Conference': f << "booktitle = \"${it.value}\",\n"; break case 'Year': f << "year = \"${it.value}\",\n"; break case 'URL': f << "URL = \"${it.value}\",\n"; break default: break } } } f << '}\n'; };

def printJournal (f, attr, index) { f << "@Article{ ${index.toString()},\n"; attr.each{ switch(it.key){ case 'Authors': f << "author = \"${it.value}\",\n"; break case 'Title': f << "title = \"${it.value}\",\n"; break case 'Journal': f << "journal = \"${it.value}\",\n"; break case 'Year': f << "year = \"${it.value}\",\n"; break case 'URL': f << "URL = \"${it.value}\",\n"; break default: break } }

f << '}\n'; };

def printStandard (f, attr, index){ f << "@STANDARD{ ${index.toString()},\n"; attr.each{ switch(it.key){ case 'Authors': f << "author = \"${it.value}\",\n"; break case 'Title': f << "title = \"${it.value}\",\n"; break case 'Institution': f << "institution = \"${it.value}\",\n"; break case 'Year': f << "year = \"${it.value}\",\n"; break default: break } }

f << '}\n';

}; def printWeblink (f, attr, index) { f << "@online{ ${index.toString()},\n"; attr.each{ switch(it.key){ case 'Authors': f << "author = \"${it.value}\",\n"; break case 'URL': f << "URL = \"${it.value}\",\n"; break default: break } }

f << '}\n'; };


def allChildren (node, f) {

   def kids = [];
   def stack = new Stack();
   stack.push(node.childrenUnfolded());
   while(!stack.isEmpty()) {
       nodes = stack.pop();
       while(nodes.hasNext()) {

k = nodes.next();

           kids.add(k);

stack.push(k.childrenUnfolded());

       };
   };

kids.each{ };

   return kids;

};


f = new File("/tmp/File.txt"); f.delete(); index = 1;


allChildren(node, f).each { kid -> attr = attrMap(kid, f); switch(attr['Type']){ case 'Journalpaper': printJournal(f, attr, index); index = index + 1; break case 'Standard': printStandard(f, attr, index); index = index + 1; break case 'Weblink': printWeblink(f, attr, index); index = index + 1; break case 'Confpaper': printConference(f, attr, index); index = index + 1; break default: break } }


</groovy>

Sum up all subnodes recursively for a specific attribute(s) or all found attributes

A useful script which sums up values of a specified attribute(s) and performs this recursively propagating summed values over node structure.
All that you need - just assign this source to the attribute named 'script' for a high-level node containing subnodes with numeric attribute(s), press Alt-F8 and follow onscreen instructions on the popup dialog.
the script also supports calculation over several attributes or all found attributes containing numeric values (use wildcard *).
If there are attributes with the same name within a node - this is handled properly.

Important: this script adds (and removes) new attributes with prefix "Σ" (Greek letter Sigma) which contain summed values of corresponding nodes and all their subnodes.
So if you also use attributes with names those start from the letter Sigma, you should change the value of ATTRIBUTE_SUM_PREFIX variable within the script.
<groovy>

/* sum Attributes */ /**

* a useful script which sums up values of a specified attribute(s) and performs this recursively propagating
* summed values over node structure.
* all that you need - just assign this source to the attribute named 'script' for a high-level node containing
* subnodes with numeric attribute(s), press Alt-F8 and follow onscreen instructions on the popup dialog.
* the script also supports calculation over several attributes or 
* all found attributes containing numeric values (use wildcard *).
* if there are attributes with the same name within a node - this is handled properly.
*
* Important: this script adds (and removes) new attributes with prefix "\u03a3 " (Greek letter Sigma) which contains
* summed values to corresponding nodes so if you also use attributes with names those start from
* the sigma letter you should change the value of ATTRIBUTE_SUM_PREFIX below.
* 
* @author Dmitry Romanov
* coded with help of Groovy Eclipse plugin
  • /

import freemind.modes.*; import freemind.modes.attributes.*; import freemind.view.mindmapview.attributeview.*; import freemind.view.mindmapview.*; import javax.swing.JOptionPane; /* def node = (MindMapNode) node; */

ATTRIBUTE_SUM_PREFIX = "\u03a3 ";

/* prepare decimal format object: */ decimalFormat = java.text.NumberFormat.getNumberInstance(); decimalFormat.setMinimumFractionDigits(0); def dfs = decimalFormat.getDecimalFormatSymbols(); dfs.setDecimalSeparator((char)'.'); decimalFormat.setDecimalFormatSymbols(dfs);

def ATTRIBUTE_TO_SUM = getAttributeValues(node, "ATTRIBUTE_TO_SUM");

if (ATTRIBUTE_TO_SUM.size()==0) { def uniqueAttrNames = collectUniqueAttributeNames(node, null, false); if (uniqueAttrNames.size()>0) { uniqueAttrNames << "*"; String selectedAttribute = (String)JOptionPane.showInputDialog(

               	null,
               	"Choose the attribute to sum,\nyour selection will be saved \nin the current node in the attribute \"ATTRIBUTE_TO_SUM\" \nwhich you can change later or even specify more attributes to sum up\n by adding more attributes with the name \"ATTRIBUTE_TO_SUM\":\n\n(* is wildcard for all attributes)\n\n",
               	"Need More Info",
               	JOptionPane.PLAIN_MESSAGE,
               	null,
               	uniqueAttrNames.toArray(),
               	null);

if (selectedAttribute?.length()>0) { setAttributeValue(node, "ATTRIBUTE_TO_SUM", selectedAttribute, false); ATTRIBUTE_TO_SUM = [selectedAttribute]; } } else { JOptionPane.showMessageDialog(null, "Found no attributes inside, starting from the current node.", "Error", JOptionPane.ERROR_MESSAGE); }; };

if (ATTRIBUTE_TO_SUM.size()>0) { collectUniqueAttributeNames(node, null, true).each() { anAttrName -> if (anAttrName.startsWith(ATTRIBUTE_SUM_PREFIX) && !ATTRIBUTE_TO_SUM.contains(anAttrName)) { removeAttribute(node, anAttrName); }; }; ATTRIBUTE_TO_SUM.each() { anAttrToSum -> if (anAttrToSum?.trim().length()>0) { ATTRIBUTE_TOTAL_NAME = ATTRIBUTE_SUM_PREFIX + anAttrToSum; def grandTotal = decimalFormat.format(countTotal(node, anAttrToSum, false)); setAttributeValue(node, ATTRIBUTE_TOTAL_NAME, grandTotal, true); }; }; };

/**

* visits child nodes recursively and sums up values of found attributes
* with name of "attributeToSum".
  • /

def countTotal (MindMapNode targetNode, String attributeToSum, boolean andSetAttribute = false) {

  def total = 0;
  targetNode.childrenUnfolded().each() { aChildNode ->
  		total += countTotal(aChildNode, attributeToSum, true); /* recursive invocation */
  };
  /* currently total holds sum from all child nodes */
  if (andSetAttribute && total>0 && targetNode.hasChildren()) {

total += countTotalForNode(targetNode, attributeToSum); setAttributeValue(targetNode, ATTRIBUTE_TOTAL_NAME, decimalFormat.format(total), true);

  } else {

total += countTotalForNode(targetNode, attributeToSum);

  };
  total;

};

def countTotalForNode(MindMapNode targetNode, String attributeToSum) { def total = 0; def values = getAttributeValues(targetNode, attributeToSum);

   values.each() { val ->
   	if (val?.length()>0) {
   		if (val.matches("[-+]?([0-9]*\\.[0-9]+|[0-9]+)")) {
      			total += val.toDouble();
   		} else if (attributeToSum != "*") {
   			JOptionPane.showMessageDialog(null, "Invalid numeric value '"+val+"' for the node: \n\n\""+targetNode.getText()+"\"\n\nIgnored it.");	
   		};
   	};

}; total; };

def String getAttributeValue(MindMapNode targetNode, String attributeName) { def attribute = findAttribute(targetNode, attributeName); return attribute?.value; };

def Attribute findAttribute(MindMapNode targetNode, String attributeName) { return targetNode.getAttributes()?.getAttributes()?.find() { anAttribute -> anAttribute?.name == attributeName; } };

def getAttributeValues(MindMapNode targetNode, String attributeName) { return targetNode.getAttributes()?.getAttributes()?.findAll() { anAttribute -> (attributeName == "*" && !anAttribute?.name.startsWith(ATTRIBUTE_SUM_PREFIX)) || anAttribute?.name == attributeName; }.collect() { anAttribute -> anAttribute?.value } };

def int findAttributeIndex(MindMapNode targetNode, String attributeName) { def attrIndex = -1; targetNode.getAttributes().getAttributes().eachWithIndex() { anAttribute, index -> if (anAttribute.name == attributeName) { attrIndex = index; return; }; }; return attrIndex; };


def setAttributeValue(MindMapNode targetNode, String attrName, String attrValue, boolean firstOrLast) { def existingAttribute = findAttribute(targetNode, attrName); if (existingAttribute != null) { existingAttribute.setValue(attrValue); } else { if (targetNode.getAttributes()==null || targetNode.getAttributes().getRowCount()==0) { targetNode.createAttributeTableModel(); };

 		targetNode.getAttributes().insertRow(firstOrLast ? 0 : targetNode.getAttributes().getRowCount(), attrName, attrValue);

}; setOptimalColumnWidthsByFirstColumn(targetNode); };

def void setOptimalColumnWidthsByFirstColumn(MindMapNode targetNode) { def NodeAttributeTableModel attributesModel = targetNode.getAttributes();

   if (attributesModel.getRowCount()==0) {

return;

   };
   java.awt.Component comp = null;
   int cellWidth = 0;
   int maxCellWidth = 0;
   
   def AttributeView attrView = 
   	targetNode.getViewers().find() {it instanceof NodeView}?.getAttributeView();
   for (row in 0..attributesModel.getRowCount()-1) {
   	    def AttributeTable attrTable = attrView?.attributeTable;
           comp = attrTable?.dtcr?.getTableCellRendererComponent(
                   attrView?.attributeTable, attributesModel.getValueAt(row, 0),
                   false, false, row, 0);
           if (comp!=null) {
           	cellWidth = comp.getPreferredSize().width;
           	maxCellWidth = Math.max(cellWidth, maxCellWidth);
           }; 
   };
   attributesModel.setColumnWidth(0, maxCellWidth>0 ? maxCellWidth + 1 : 100);

};

def void removeAttribute(MindMapNode startNode, String attrName) { def attrIndex = findAttributeIndex(startNode, attrName); if (attrIndex>-1) { startNode.getAttributes().removeRow(attrIndex); }; startNode.childrenUnfolded().each() { aChildNode -> removeAttribute(aChildNode, attrName); /* recursive invocation */ }; };

def Set collectUniqueAttributeNames(MindMapNode startNode, Set result, boolean includeAttrsWithSum) { result = result!=null ? result : new LinkedHashSet(); startNode.getAttributeKeyList().each() { if (it.length()>0 && it!="script" && (includeAttrsWithSum || !it.startsWith(ATTRIBUTE_SUM_PREFIX))) { result.add(it); }; }; startNode.childrenUnfolded().each() { aChildNode -> collectUniqueAttributeNames(aChildNode, result, includeAttrsWithSum); /* recursive invocation */ }; return result; };

</groovy> --Dmitry2008 12:58, 5 Jan 2008 (PST)

Output A Map to CSV

I use Freemind for estimating sometimes and when I do I want to export to a spreadsheet for inclusion in a larger project plan. The following script creates CSV output from a FreeMind map. Nodes without children are output at node depth 7 (magic number) so that the estimates column lines up (of course I could have put the estimates in attributes but I didn't). <groovy> def process(thisNode, childPosition) {

	def nodeName = thisNode.toString()
	if (!thisNode.children) {
		print("," * (7 - thisNode.nodeLevel))
	}
	if (childPosition != 1) {
		print "," * thisNode.nodeLevel + nodeName
	} else {
		print "," + nodeName
	}
	def i = 0
	if (thisNode.children) {
		thisNode.children.each {
			i++
			process(it, i)
		}
	} else {
		println()
	}
}

process(node, 0) </groovy>

Output A Map to CSV Part 2

The second iteration of the estimate to CSV script allows different item types to be denoted by icons 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. The icon names for the 'Type' attribute are taken from the fam fam fam silk icon set (http://www.famfamfam.com/lab/icons/silk/) which I have added to FreeMind. Here is the code: <groovy> import freemind.main.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()
   thisNode.icons.each {
     def iconName = it.name
     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.fontSize) < 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>

Daves massivly cheezy Api Generator

If you are lazy (like me) and dont want 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


<groovy> import freemind.modes.MindMapNode; import freemind.modes.mindmapmode.MindMapController;


def lastSection(Str){

  String[] p;
  p = Str.split("\\.");
  return p[p.size()-1];
  }

def newChildNode(meth){

  MindMapNode ApiNode;
  MindMapNode Rtype;
  MindMapNode IProto;
  MindMapNode Comment;
  ApiNode = c.addNewNode(node,0,false);   
  Rtype   = c.addNewNode(ApiNode,0,false);
  IProto  = c.addNewNode(ApiNode,1,false);
  Comment = c.addNewNode(ApiNode,2,false);
  def text = meth.toString();
  String[] parts;
  parts = text.split(" ");
  StringBuffer Sb = new StringBuffer();
  Sb.append(parts[0]);
  Sb.append(" ");
  Sb.append(lastSection(parts[1]));
  Rtype.setText(parts[1]);
  Sb.append(" ");
  Sb.append(meth.getName());
  Sb.append("(");
  def Class[] parms;
  parms = meth.getParameterTypes();
  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.setText(Sb.toString());
  ApiNode.setFolded(false);
  IProto.setText(protoTxt.toString());
  Comment.setText("Comments");
  c.nodeRefresh(ApiNode);
  c.nodeRefresh(Rtype);
  c.nodeRefresh(IProto);
  c.nodeRefresh(Comment);
  }

/* M = c.getClass().getMethods(); */

M = node.getClass().getMethods(); for(i in 0..<M.size()){

  newChildNode(M[i]);
  }

</groovy>

Enable/disable script execution

Alt-F8 automatically runs ALL scripts in the map; to run a single script, it appears you have to go to the script editor. This one is a workaround... on the node that the script is on, set the green check icon to enable it; delete the icon to disable it.

<groovy> def hasIcon(anode, iconname) { iconlist = anode.getIcons(); found = false; iconlist.each { if (it.getName() == iconname) { found = true; } } return(found); }

def check_script_run(): return(hasIcon(node, "button_ok")); }

</groovy>