User-contributed example scripts

From FreeMind
Revision as of 23:49, 23 May 2008 by Bpatters7 (talk | contribs) (adding demo video of debugging Groovy for FreeMind enhancement)
Jump to navigationJump to search

Code Examples from the author

Dear FreeMind users,

in Features 0.9.0 you'll find a mindmap with the upcoming features for 0.9.0. Included, there is a section about scripts and some very useful scripts, that are recommended to be read first.

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>

Demo video on debugging Groovy for FreeMind

This demo video shows how to do the following things:

  • reference a file containing Groovy script from within FreeMind
  • pull FreeMind into a debugger
  • set a break point and step through the Groovy you're debugging

This also gives all the advantages of creating FreeMind Groovy in an environment with:

Here is the video: Groovy for FreeMind inside IDEA