Index: tmp/org.txm.groovy.core/src/groovy/org/txm/scripts/importer/xtz/XTZDefaultPagerStep.groovy
===================================================================
--- tmp/org.txm.groovy.core/src/groovy/org/txm/scripts/importer/xtz/XTZDefaultPagerStep.groovy	(revision 3210)
+++ tmp/org.txm.groovy.core/src/groovy/org/txm/scripts/importer/xtz/XTZDefaultPagerStep.groovy	(revision 3211)
@@ -239,8 +239,8 @@
 			pagedWriter.writeStartElement("div", ["class": pager.getImportModule().getProject().getName()]) //<div> of the corpus
 			pagedWriter.writeStartElement("div", ["class": "txmeditionpage"]) //<div>
 			
-			println "NEW HTML: "+outfile
-			println "TAGS: "+tags
+//			println "NEW HTML: "+outfile
+//			println "TAGS: "+tags
 			pagedWriter.writeStartElements(tags)
 			return true;
 		} catch (Exception e) {
Index: tmp/org.txm.groovy.core/src/groovy/org/txm/macro/InstallAndUpdateMacroMacro.groovy
===================================================================
--- tmp/org.txm.groovy.core/src/groovy/org/txm/macro/InstallAndUpdateMacroMacro.groovy	(revision 0)
+++ tmp/org.txm.groovy.core/src/groovy/org/txm/macro/InstallAndUpdateMacroMacro.groovy	(revision 3211)
@@ -0,0 +1,65 @@
+import org.txm.Toolbox
+import org.txm.utils.ConsoleProgressBar
+import org.txm.utils.io.IOUtils
+
+def groovyUserDirectory = new File(Toolbox.getTxmHomePath() + "/scripts/groovy/user/org/txm/macro/");
+
+if (!groovyUserDirectory.exists()) {
+	groovyUserDirectory.mkdirs();
+}
+
+if (!groovyUserDirectory.exists()) {
+	monitorShowError("Le répertoire $groovyUserDirectory n'a pas pu être créé. Abandon.")
+	return;
+}
+@Field @Option(name="targets", usage="Path to the macro to update", widget="String", required=true, def="")
+def targets
+
+@Field @Option(name="svn", usage="TXM SVN repository", widget="String", required=true, def="http://forge.cbp.ens-lyon.fr/svn/txm/tmp/org.txm.groovy.core/src/groovy/org/txm/macro/")
+def svn
+
+@Field @Option(name="debug", usage="Show devug messages", widget="Boolean", required=false, def="false")
+def debug
+
+if (!ParametersDialog.open(this)) return false;
+
+if (!svn.endsWith("/")) svn += "/"
+
+def macros = targets.split(",").collect(){it.trim()}
+	
+println "Copyping ${macros.size()} files: "
+def errors = []
+for (def m : macros) {
+	def f = new File(groovyUserDirectory, m+"Macro.groovy")
+	f.getParentFile().mkdirs()
+	String remoteUrl = svn+m+"Macro.groovy"
+	try {
+		
+		if (debug) print "\n"+remoteUrl
+		boolean replaced = f.exists()
+		
+		
+		def file = new FileOutputStream(f)
+		def out = new BufferedOutputStream(file)
+		out << new URL(remoteUrl).openStream()
+		out.close()
+		
+		if (replaced) print "*"
+		else print "."
+	} catch(Exception e ) {
+		print "x"
+		errors << "\nError while fetching $f from $remoteUrl: $e"
+	}
+}
+if (errors.size() > 0) {
+	println errors.join("\n")
+}
+println "\n$targets updated."
+
+def monitorShowError(String message) {
+	monitor.syncExec(new Runnable() {
+		public void run() {
+			org.eclipse.jface.dialogs.MessageDialog.openError(org.eclipse.swt.widgets.Display.getCurrent().getActiveShell(), "Error", message)
+		}
+	});
+}
Index: tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Table2XMLMacro.groovy
===================================================================
--- tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Table2XMLMacro.groovy	(revision 3210)
+++ tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Table2XMLMacro.groovy	(revision 3211)
@@ -67,22 +67,28 @@
 
 if (!ParametersDialog.open(this)) return false;
 
-textMetadataColumnList = textMetadataColumnList.split(",").collect { it.trim() }
-textContentColumnList = textContentColumnList.split(",").collect { it.trim() }
-structureMetadataColumnList = structureMetadataColumnList.split(",").collect { it.trim() }
-structureContentColumnList = structureContentColumnList.split(",").collect { it.trim() }
-dateColumnTypeList = dateColumnTypeList.split(",").collect { it.trim() }
-prefixesColumnTypeList = prefixesColumnTypeList.split(",").collect { it.trim() }
-listColumnTypeList = listColumnTypeList.split(",").collect { it.trim() }
+textMetadataColumnList = textMetadataColumnList.split(",") as List
+textMetadataColumnList.remove("")
+textContentColumnList = textContentColumnList.split(",") as List
+textContentColumnList.remove("")
+structureMetadataColumnList = structureMetadataColumnList.split(",") as List
+structureMetadataColumnList.remove("")
+structureContentColumnList = structureContentColumnList.split(",") as List
+structureContentColumnList.remove("")
+dateColumnTypeList = dateColumnTypeList.split(",") as List
+dateColumnTypeList.remove("")
+prefixesColumnTypeList = prefixesColumnTypeList.split(",") as List
+prefixesColumnTypeList.remove("")
+listColumnTypeList = listColumnTypeList.split(",") as List
+listColumnTypeList.remove("")
+
 if (textSelector != null && textSelector.contains("=")) {
-	textSelector = textSelector.trim()
-	textSelector = [textSelector.substring(0, textSelector.indexOf("=")).trim(), textSelector.substring(textSelector.indexOf("=")+1).trim()]
+	textSelector = [textSelector.substring(0, textSelector.indexOf("=")), textSelector.substring(textSelector.indexOf("=")+1)]
 } else {
 	textSelector = [null, null]
 }
 if (structureSelector != null && structureSelector.contains("=")) {
-	structureSelector = structureSelector.trim()
-	structureSelector = [structureSelector.substring(0, structureSelector.indexOf("=")).trim(), structureSelector.substring(structureSelector.indexOf("=")+1).trim()]
+	structureSelector = [structureSelector.substring(0, structureSelector.indexOf("=")), structureSelector.substring(structureSelector.indexOf("=")+1)]
 } else {
 	structureSelector = [null, null]
 }
@@ -119,12 +125,13 @@
 	"structureContentColumnList":structureContentColumnList]
 for (def key : hash.keySet()) {
 	for (def m : hash[key]) {
+		
 		if (!headers.contains(m)) {
 			println "** Error: missing $key column: $m"
 			ok = false
 		}
+	}
 }
-}
 
 if (!ok) { return false; }
 
@@ -139,12 +146,12 @@
 while (reader.readRecord()) {
 	nRecord++
 	//	println "record="+reader.getRecord().get(textSelector[0])+" "+reader.getRecord().get(structureSelector[0])
-
-	String id = reader.get(textIDColumn).trim()
-	String join = reader.get(textJoinColumn).trim()
-	String textSelectorValue = reader.get(textSelector[0]).trim()
-	String structureSelectorValue = reader.get(structureSelector[0]).trim()
 	
+	String id = reader.get(textIDColumn)
+	String join = reader.get(textJoinColumn)
+	String textSelectorValue = reader.get(textSelector[0])
+	String structureSelectorValue = reader.get(structureSelector[0])
+	
 	if (textIDColumn != null && textJoinColumn != null && textIDColumn.length() > 0 && textJoinColumn.length() > 0) {
 		if (textSelectorValue != null && structureSelectorValue != null) {
 			
@@ -179,7 +186,7 @@
 }
 
 println "N records: "+nRecord
-println "N records to write: "+nRecordToWrite
+println "N records to join: "+nRecordToWrite
 println "N texts to build: "+texts.size()
 if (texts.size()  == 0) {
 	println "No text found. Aborting."
@@ -187,105 +194,48 @@
 }
 outputDirectory.mkdir()
 
+ConsoleProgressBar cpb = new ConsoleProgressBar(texts.size())
 for (def id : texts.keySet()) {
-def toWrite = texts[id]
-def text = toWrite[0]
-String textSelectorValue = text.get(textSelector[0]).trim()
-if (textSelectorValue == null || (textSelectorValue != null && textSelectorValue.matches(textSelector[1]))) {
-	if (debug) println "Processing text: $id"
-	
-	File outputfile = new File(outputDirectory, id+".xml")
-	
-	XMLOutputFactory factory = XMLOutputFactory.newInstance()
-	FileOutputStream output = new FileOutputStream(outputfile)
-	XMLStreamWriter writer = factory.createXMLStreamWriter(output, "UTF-8")
-	
-	writer.writeStartDocument("UTF-8","1.0")
-	writer.writeCharacters("\n") // simple XML formating
-	writer.writeStartElement("TEI")
-	writer.writeCharacters("\n") // simple XML formating
-	writer.writeStartElement("teiHeader")
-	writer.writeEndElement() // teiHeader
-	writer.writeCharacters("\n") // simple XML formating
-	writer.writeStartElement("text")
-	writer.writeAttribute("id", id)
-	
-	for (String att : textMetadataColumnList) {
-		if (att in dateColumnTypeList) {
-			writeMetadataDate(text, att, writer)
-		} else {
-			writer.writeAttribute(AsciiUtils.buildAttributeId(att), text.get(att)) // struct
-		}
-	}
-	
-	writer.writeCharacters("\n") // simple XML formating
-	
-	writer.writeStartElement("metadata")
-	writer.writeStartElement("list")
-	writer.writeAttribute("type", "unordered")
-	writer.writeCharacters("\n")
-	textMetadataColumnList.each { att ->
-		writer.writeStartElement("item")
-		writer.writeCharacters(att+" : "+text.get(att).replaceAll("\n", ";"))
-		writer.writeEndElement() // item
-		writer.writeCharacters("\n")
-	}
-	writer.writeEndElement() // list
-	writer.writeEndElement() // metadata
-	writer.writeCharacters("\n")
-	
-	for (String att : textContentColumnList) {
+	cpb.tick()
+	def toWrite = texts[id]
+	def text = toWrite[0]
+	String textSelectorValue = text.get(textSelector[0])
+	if (textSelectorValue == null || (textSelectorValue != null && textSelectorValue.matches(textSelector[1]))) {
+		if (debug) println "Processing text: $id"
 		
-		if (att in prefixesColumnTypeList) {
-			
-			writePrefixTextContent(text, att, writer)
-		} else if (att in listColumnTypeList) {
-			
-			writeListTextContent(text, att, writer)
-		} else {
-			
-			writer.writeStartElement("p");
-			writer.writeAttribute("type", att.trim())
-			writer.writeCharacters("\n") // simple XML formating
-			
-			writer.writeStartElement("head")
-			writer.writeStartElement("hi")
-			writer.writeCharacters(att+" : ")
-			writer.writeEndElement() // hi
-			writer.writeEndElement() // head
-			
-			writer.writeCharacters(text.get(att)) // get textColumnList content
-			writer.writeEndElement() // t
-			writer.writeCharacters("\n") // simple XML formating)
-		}
-	}
-	
-	int pb_n = 1;
-	for (int i = 1 ; i < toWrite.size() ; i++) {
-		def record = toWrite[i]
+		File outputfile = new File(outputDirectory, id+".xml")
 		
-		writer.writeEmptyElement("pb") // <pb/>
-		writer.writeAttribute("n", ""+pb_n++)
-		writer.writeAttribute("type", "record")
+		XMLOutputFactory factory = XMLOutputFactory.newInstance()
+		FileOutputStream output = new FileOutputStream(outputfile)
+		XMLStreamWriter writer = factory.createXMLStreamWriter(output, "UTF-8")
 		
-		writer.writeStartElement(structureTag)
+		writer.writeStartDocument("UTF-8","1.0")
+		writer.writeCharacters("\n") // simple XML formating
+		writer.writeStartElement("TEI")
+		writer.writeCharacters("\n") // simple XML formating
+		writer.writeStartElement("teiHeader")
+		writer.writeEndElement() // teiHeader
+		writer.writeCharacters("\n") // simple XML formating
+		writer.writeStartElement("text")
+		writer.writeAttribute("id", id)
 		
-		for (String att : structureMetadataColumnList) {
+		for (String att : textMetadataColumnList) {
 			if (att in dateColumnTypeList) {
-				writeMetadataDate(record, att, writer)
+				writeMetadataDate(text, att, writer)
 			} else {
-				writer.writeAttribute(AsciiUtils.buildAttributeId(att), record.get(att)) // struct
+				writer.writeAttribute(AsciiUtils.buildAttributeId(att), text.get(att)) // struct
 			}
 		}
-		writer.writeCharacters("\n")
 		
+		writer.writeCharacters("\n") // simple XML formating
+		
 		writer.writeStartElement("metadata")
 		writer.writeStartElement("list")
 		writer.writeAttribute("type", "unordered")
 		writer.writeCharacters("\n")
-		structureMetadataColumnList.each { att ->
+		textMetadataColumnList.each { att ->
 			writer.writeStartElement("item")
-			writer.writeCharacters(att+" : "+record.get(att).replaceAll("\n", ";"))
+			writer.writeCharacters(att+" : "+text.get(att).replaceAll("\n", ";"))
 			writer.writeEndElement() // item
 			writer.writeCharacters("\n")
 		}
@@ -293,106 +243,180 @@
 		writer.writeEndElement() // metadata
 		writer.writeCharacters("\n")
 		
-		for (String att : structureContentColumnList) {
+		for (String att : textContentColumnList) {
+			
 			if (att in prefixesColumnTypeList) {
+				
 				writePrefixTextContent(text, att, writer)
 			} else if (att in listColumnTypeList) {
+				
 				writeListTextContent(text, att, writer)
 			} else {
-				writer.writeStartElement("p");
-				writer.writeAttribute("type", att.trim())
-				writer.writeAttribute("id", record.get(textIDColumn))
-				writer.writeCharacters("\n") // simple XML formating
 				
-				writer.writeStartElement("head")
-				writer.writeStartElement("hi")
-				writer.writeCharacters(att+" : ")
-				writer.writeEndElement() // hi
-				writer.writeEndElement() // head
-				
-				writer.writeCharacters(record.get(att)) // get textColumnList content
-				writer.writeEndElement() // t
-				writer.writeCharacters("\n") // simple XML formating
+				writeTextContent(text, att, writer)
 			}
 		}
 		
-		writer.writeEndElement() // struct
+		int pb_n = 1;
+		for (int i = 1 ; i < toWrite.size() ; i++) {
+			def record = toWrite[i]
+			
+			writer.writeEmptyElement("pb") // <pb/>
+			writer.writeAttribute("n", ""+pb_n++)
+			writer.writeAttribute("type", "record")
+			
+			writer.writeStartElement(structureTag)
+			
+			for (String att : structureMetadataColumnList) {
+				if (att in dateColumnTypeList) {
+					writeMetadataDate(record, att, writer)
+				} else {
+					writer.writeAttribute(AsciiUtils.buildAttributeId(att), record.get(att)) // struct
+				}
+			}
+			writer.writeCharacters("\n")
+			
+			writer.writeStartElement("metadata")
+			writer.writeStartElement("list")
+			writer.writeAttribute("type", "unordered")
+			writer.writeCharacters("\n")
+			structureMetadataColumnList.each { att ->
+				writer.writeStartElement("item")
+				writer.writeCharacters(att+" : "+record.get(att).replaceAll("\n", ";"))
+				writer.writeEndElement() // item
+				writer.writeCharacters("\n")
+			}
+			writer.writeEndElement() // list
+			writer.writeEndElement() // metadata
+			writer.writeCharacters("\n")
+			
+			for (String att : structureContentColumnList) {
+				if (att in prefixesColumnTypeList) {
+					writePrefixTextContent(record, att, writer)
+				} else if (att in listColumnTypeList) {
+					writeListTextContent(record, att, writer)
+				} else {
+					writeTextContent(record, att, writer)
+				}
+			}
+			
+			writer.writeEndElement() // struct
+			writer.writeCharacters("\n") // simple XML formating
+		}
+		
+		writer.writeEndElement() // text
 		writer.writeCharacters("\n") // simple XML formating
+		writer.writeEndElement() // TEI
+		writer.close()
+		output.close()
+		reader.close()
+	} else {
+		// error
+		println "ERROR: '$id' text group with  no text line"
 	}
-	
-	writer.writeEndElement() // text
-	writer.writeCharacters("\n") // simple XML formating
-	writer.writeEndElement() // TEI
-	writer.close()
-	output.close()
-	reader.close()
-} else {
-	// error
-	println "ERROR: '$id' text group with  no text line"
 }
-}
+cpb.done()
 
+
 def writeListTextContent(def record, def att, def writer) {
-writer.writeCharacters("\n")
-found = false
-def value = record.get(att)
-value.findAll( /(?s)([^\n]+?)[\n]/ ).each { desc ->
-	found = true
-	writer.writeCharacters("\t")
-	writer.writeStartElement("p")
-	writer.writeAttribute("rend", "list")
-	matches = (desc =~ /(?s)([^\n]+?)[\n]/)
-	writer.writeCharacters(matches[0][1])
-	writer.writeEndElement() // p
+	
+	def value = record.get(att).replaceAll(" ++", " ")
+	if (value.length() <= 1) return
+	
 	writer.writeCharacters("\n")
+	found = false
+	for (String s : value.split("\n")) {
+		s = s.trim()
+		if (s.length() == 0) continue;
+		if (s.startsWith("-")) s = s.substring(1).trim();
+		if (s.length() == 0) continue;
+		
+		found = true
+		writer.writeCharacters("\t")
+		writer.writeStartElement("p")
+		writer.writeAttribute("rend", "list")
+		writer.writeCharacters(s)
+		writer.writeEndElement() // p
+		writer.writeCharacters("\n")
+	}
+	
+	if (!found) {
+		println "Warning: '$att' list not found in '$value'"
+		writer.writeCharacters("\t")
+		writer.writeStartElement("p")
+		writer.writeAttribute("rend", "no-list")
+		writer.writeCharacters(value)
+		writer.writeEndElement() // p
+		writer.writeCharacters("\n")
+	}
 }
-if (!found) {
-	writer.writeCharacters("\t")
-	writer.writeStartElement("p")
-	writer.writeAttribute("rend", "no-list")
-	writer.writeCharacters(value)
+
+def writeTextContent(def record, def att, def writer) {
+	
+	def value = record.get(att).replaceAll(" ++", " ")
+	if (value.length() == 0) return
+	
+	writer.writeStartElement("head")
+	writer.writeStartElement("hi")
+	writer.writeCharacters(att)
+	writer.writeEndElement() // hi
+	writer.writeEndElement() // head
+	
+	writer.writeStartElement(AsciiUtils.buildAttributeId(att));
+	writer.writeAttribute("type", att.trim())
+	if (record.get(textIDColumn) != null) {
+		writer.writeAttribute("id", record.get(textIDColumn))
+	}
+	writer.writeCharacters("\n") // simple XML formating
+	
+	writer.writeCharacters(value) // get textColumnList content
 	writer.writeEndElement() // p
-	writer.writeCharacters("\n")
+	writer.writeCharacters("\n") // simple XML formating)
 }
-}
 
 def writePrefixTextContent(def record, def att, def writer) {
-writer.writeCharacters("\n")
-writer.writeStartElement("list")
-writer.writeAttribute("rend", "prefixes")
-writer.writeAttribute("type", "unordered")
-writer.writeCharacters("\n")
-found = false
-def value = record.get(att)
-value.findAll( /(?s)[A-Z]{3}:? *([^;\n]+?) +[;\n]/ ).each { desc ->
-	found = true
-	writer.writeCharacters("\t")
-	writer.writeStartElement("item")
-	matches = (desc =~ /(?s)([A-Z]{3}):? *([^;\n]+?) +[;\n]/)
-	writer.writeAttribute("type", matches[0][1])
-	writer.writeStartElement("span")
-	writer.writeCharacters(matches[0][1]+" ")
-	writer.writeEndElement() // span
-	writer.writeCharacters(matches[0][2])
-	writer.writeEndElement() // item
+	def value = record.get(att).replaceAll(" ++", " ")
+	if (value.length() <= 1) return
+	
 	writer.writeCharacters("\n")
+	writer.writeStartElement("list")
+	writer.writeAttribute("rend", att.trim())
+	writer.writeAttribute("type", "unordered")
+	writer.writeCharacters("\n")
+	found = false
+	
+	value.findAll( /(?s)[A-Z]{3}:? *([^;\n]+?) +[;\n]/ ).each { desc ->
+		found = true
+		writer.writeCharacters("\t")
+		writer.writeStartElement("item")
+		matches = (desc =~ /(?s)([A-Z]{3}):? *([^;\n]+?) +[;\n]/)
+		writer.writeAttribute("type", matches[0][1])
+		writer.writeStartElement("span")
+		writer.writeCharacters(matches[0][1]+" ")
+		writer.writeEndElement() // span
+		writer.writeCharacters(matches[0][2])
+		writer.writeEndElement() // item
+		writer.writeCharacters("\n")
+	}
+	if (!found) {
+		println "Warning: '$att' prefixes not found in: '$value'"
+		writer.writeCharacters(value)
+	}
+	writer.writeEndElement() // list
+	writer.writeCharacters("\n")
 }
-if (!found) {
-	writer.writeCharacters(value)
-}
-writer.writeEndElement() // list
-writer.writeCharacters("\n")
-}
 
 def writeMetadataDate(def record, def att, def writer) {
-String value = record.get(att)
-String att_normalized = AsciiUtils.buildAttributeId(att)
-matches = (value =~ /([0-9]{2})\/([0-9]{2})\/([0-9]{4})/)
-writer.writeAttribute(att_normalized+"-jour", matches[0][1])
-writer.writeAttribute(att_normalized+"-joursemaine", new java.text.SimpleDateFormat('EEEE').format(Date.parse("dd/MM/yyyy", value)))
-writer.writeAttribute(att_normalized+"-mois", matches[0][2])
-writer.writeAttribute(att_normalized+"-annee", matches[0][3])
-writer.writeAttribute(att_normalized+"-tri", matches[0][3]+"-"+matches[0][2]+"-"+matches[0][1])
+	String value = record.get(att)
+	if (value.length() == 0) return
+	
+	String att_normalized = AsciiUtils.buildAttributeId(att)
+	matches = (value =~ /([0-9]{2})\/([0-9]{2})\/([0-9]{4})/)
+	writer.writeAttribute(att_normalized+"-jour", matches[0][1])
+	writer.writeAttribute(att_normalized+"-joursemaine", new java.text.SimpleDateFormat('EEEE').format(Date.parse("dd/MM/yyyy", value)))
+	writer.writeAttribute(att_normalized+"-mois", matches[0][2])
+	writer.writeAttribute(att_normalized+"-annee", matches[0][3])
+	writer.writeAttribute(att_normalized+"-tri", matches[0][3]+"-"+matches[0][2]+"-"+matches[0][1])
 }
 
 /*
