Index: TXM/trunk/bundles/org.txm.groovy.core/src/groovy/org/txm/macro/stats/TopVocabularyMacro.groovy
===================================================================
--- TXM/trunk/bundles/org.txm.groovy.core/src/groovy/org/txm/macro/stats/TopVocabularyMacro.groovy	(revision 3969)
+++ TXM/trunk/bundles/org.txm.groovy.core/src/groovy/org/txm/macro/stats/TopVocabularyMacro.groovy	(revision 3970)
@@ -1,90 +0,0 @@
-package org.txm.macro.stats
-// Copyright © 2010-2016 ENS de Lyon.
-// Copyright © 2007-2010 ENS de Lyon, CNRS, INRP, University of
-// Lyon 2, University of Franche-Comté, University of Nice
-// Sophia Antipolis, University of Paris 3.
-// @author mdecorde
-// @author sheiden
-//
-// The TXM platform is free software: you can redistribute it
-// and/or modify it under the terms of the GNU General Public
-// License as published by the Free Software Foundation,
-// either version 2 of the License, or (at your option) any
-// later version.
-//
-// The TXM platform is distributed in the hope that it will be
-// useful, but WITHOUT ANY WARRANTY; without even the implied
-// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-// PURPOSE. See the GNU General Public License for more
-// details.
-//
-// You should have received a copy of the GNU General
-// Public License along with the TXM platform. If not, see
-// http://www.gnu.org/licenses.
-
-// This macro computes the basic vocabulary of a partition
-// given a specificities table. The result is saved in a
-// TSV file.
-
-import org.txm.Toolbox
-import org.txm.searchengine.cqp.clientExceptions.*
-import org.txm.searchengine.cqp.corpus.*
-import org.txm.searchengine.cqp.corpus.query.*
-import org.txm.statsengine.r.core.RWorkspace
-import org.kohsuke.args4j.*
-import groovy.transform.Field
-import org.txm.rcp.swt.widget.parameters.*
-import org.txm.specificities.core.functions.Specificities
-
-if (!(corpusViewSelection instanceof Specificities)) {
-	println "** You need to select a Specificities result icon in the Corpus view before calling this macro. Aborting."
-	return
-}
-
-// PARAMETERS
-
-@Field @Option(name="outputFile", usage="TSV result file", widget="String", required=true, def="top.tsv")
-def outputFile
-
-@Field @Option(name="nTop", usage="Number of words per column", widget="Integer", required=false, def="10")
-def nTop
-
-// END OF PARAMETERS
-if (!ParametersDialog.open(this)) return
-
-
-Specificities specif = corpusViewSelection
-def indices = specif.getSpecificitesIndices()
-def freqs = specif.getFrequencies()
-def rownames = specif.getRowNames()
-def colnames = specif.getColumnsNames()
-
-def output = new File(outputFile)
-def writer = output.newWriter("UTF-8")
-for (int j = 0; j < colnames.size() ; j++) {
-	
-	writer.println "## "+colnames[j]
-	writer.println "unit\tF\tindice"
-	
-	def sorted = []
-	
-	for (int i = 0; i < rownames.length ; i++) {
-		sorted << [rownames[i], freqs[i][j], indices[i][j]]
-	}
-	
-	sorted.sort() { -it[2]}
-	
-	for (int i = 0 ; i < nTop ; i++) {
-		writer.println sorted[i].join("\t")
-	}
-	
-	for (int i = nTop ; i >= 1 ; i--) {
-		writer.println sorted[sorted.size() - i].join("\t")
-	}
-	
-	writer.println "\n\n"
-}
-
-writer.close()
-println "Result: "+output.getAbsolutePath()
-
Index: TXM/trunk/bundles/org.txm.groovy.core/src/groovy/org/txm/macro/stats/SpecificVocabularyMacro.groovy
===================================================================
--- TXM/trunk/bundles/org.txm.groovy.core/src/groovy/org/txm/macro/stats/SpecificVocabularyMacro.groovy	(revision 0)
+++ TXM/trunk/bundles/org.txm.groovy.core/src/groovy/org/txm/macro/stats/SpecificVocabularyMacro.groovy	(revision 3970)
@@ -0,0 +1,160 @@
+package org.txm.macro.stats
+// Copyright © 2010-2016 ENS de Lyon.
+// Copyright © 2007-2010 ENS de Lyon, CNRS, INRP, University of
+// Lyon 2, University of Franche-Comté, University of Nice
+// Sophia Antipolis, University of Paris 3.
+// @author mdecorde
+// @author sheiden
+//
+// The TXM platform is free software: you can redistribute it
+// and/or modify it under the terms of the GNU General Public
+// License as published by the Free Software Foundation,
+// either version 2 of the License, or (at your option) any
+// later version.
+//
+// The TXM platform is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied
+// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more
+// details.
+//
+// You should have received a copy of the GNU General
+// Public License along with the TXM platform. If not, see
+// http://www.gnu.org/licenses.
+
+// This macro computes the basic vocabulary of a partition
+// given a specificities table. The result is saved in a
+// TSV file.
+
+import org.txm.Toolbox
+import org.txm.searchengine.cqp.clientExceptions.*
+import org.txm.searchengine.cqp.corpus.*
+import org.txm.searchengine.cqp.corpus.query.*
+import org.txm.statsengine.r.core.RWorkspace
+import org.kohsuke.args4j.*
+import groovy.transform.Field
+import org.txm.rcp.swt.widget.parameters.*
+import org.txm.specificities.core.functions.Specificities
+import org.txm.libs.office.WriteODS
+
+if (!(corpusViewSelection instanceof Specificities)) {
+	println "** You need to select a Specificities result icon in the Corpus view before calling this macro. Aborting."
+	return
+}
+
+// PARAMETERS
+
+@Field @Option(name="outputFile", usage="TSV result file", widget="String", required=true, def="top.tsv")
+		def outputFile
+
+@Field @Option(name="nTop", usage="Number of words per column", widget="Integer", required=false, def="10")
+		def nTop
+
+@Field @Option(name="showPositiveIndices", usage="Show positive indices", widget="Boolean", required=false, def="true")
+		def showPositiveIndices
+
+@Field @Option(name="showNegativeIndices", usage="Show negative indices", widget="Boolean", required=false, def="false")
+		def showNegativeIndices
+
+// END OF PARAMETERS
+if (!ParametersDialog.open(this)) { return }
+
+if (!showPositiveIndices && !showNegativeIndices) {
+	println "Select positive or/and negative indices. Aborting."
+	return;
+}
+
+Specificities specif = corpusViewSelection
+def indices = specif.getSpecificitesIndices()
+def freqs = specif.getFrequencies()
+def rownames = specif.getRowNames()
+def colnames = specif.getColumnsNames()
+
+def allTopKeys = new LinkedHashSet()
+
+def output = new File(outputFile)
+def writer = output.newWriter("UTF-8")
+def allResults = new LinkedHashMap()
+for (int j = 0; j < colnames.size() ; j++) {
+	
+	writer.println "## "+colnames[j]
+	writer.println "unit\tF\tindice"
+	
+	def sorted = []
+	def tops = []
+	for (int i = 0; i < rownames.length ; i++) {
+		sorted << [rownames[i], freqs[i][j], indices[i][j]]
+	}
+	
+	sorted.sort() { -it[2]}
+	
+	if (showPositiveIndices) {
+		for (int i = 0 ; i < nTop ; i++) {
+			writer.println sorted[i].join("\t")
+			allTopKeys << rownames[i]
+			tops << sorted[i]
+		}
+	}
+	
+	if (showNegativeIndices) {
+		if (showPositiveIndices) {
+			writer.println "\n"
+			tops << ["","",""]
+		}
+		
+		for (int i = nTop ; i >= 1 ; i--) {
+			writer.println sorted[sorted.size() - i].join("\t")
+			allTopKeys << rownames[i]
+			tops << sorted[sorted.size() - i]
+		}
+	}
+	allResults[colnames[j]] = tops
+}
+
+writer.close()
+
+WriteODS odsWriter = new WriteODS(new File(output.getAbsolutePath()+".ods"))
+
+def firstLine = []
+for (def c : colnames) {
+	firstLine << c
+	firstLine << ""
+	firstLine << ""
+}
+odsWriter.writeLine(firstLine)
+
+int icol = 0;
+for (def c : colnames) {
+	def cellRange = odsWriter.table.getCellRangeByPosition(icol, 1, icol+2, 1);
+	cellRange.merge();
+	icol += 3;
+}
+
+firstLine = []
+for (def c : colnames) {
+	firstLine << "Unit"
+	firstLine << "F"
+	firstLine << "Indice"
+}
+odsWriter.writeLine(firstLine)
+
+println "write lines..."
+def lines = []
+for (def col : allResults.keySet()) {
+	iline = 0;
+	for (def s : allResults[col]) {
+		if (lines.size() == iline) lines << []
+		for (def ss : s) lines[iline] << ss
+		iline++
+	}
+}
+
+for (def line : lines) {
+	odsWriter.writeLine(line)
+}
+
+
+odsWriter.save()
+
+println "Result: "+output.getAbsolutePath()
+
Index: TXM/trunk/bundles/org.txm.groovy.core/src/groovy/org/txm/macro/prototypes/SelectSpecifMacro.groovy
===================================================================
--- TXM/trunk/bundles/org.txm.groovy.core/src/groovy/org/txm/macro/prototypes/SelectSpecifMacro.groovy	(revision 3969)
+++ TXM/trunk/bundles/org.txm.groovy.core/src/groovy/org/txm/macro/prototypes/SelectSpecifMacro.groovy	(revision 3970)
@@ -87,4 +87,4 @@
 for (def s : result.sort()) {
 	buffer.append("|"+s.replace(".", "\\.").replace("|", "\\|"))
 }
-println buffer.toString().substring(1)
\ No newline at end of file
+println buffer.toString().substring(1)
