Index: /applications/editors/josm/plugins/trustosm/README
===================================================================
--- /applications/editors/josm/plugins/trustosm/README	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/README	(revision 24389)
@@ -0,0 +1,5 @@
+README 
+======
+
+This plugin uses PGP to digital sign OSM data.
+It is used to systematically check the osm data and helps users to trust in it.
Index: /applications/editors/josm/plugins/trustosm/build.xml
===================================================================
--- /applications/editors/josm/plugins/trustosm/build.xml	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/build.xml	(revision 24389)
@@ -0,0 +1,295 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+** This is a template build file for a JOSM  plugin.
+**
+** Maintaining versions
+** ====================
+** see README.template
+**
+** Usage
+** =====
+** To build it run
+**
+**    > ant  dist
+**
+** To install the generated plugin locally (in you default plugin directory) run
+**
+**    > ant  install
+**
+** The generated plugin jar is not automatically available in JOSMs plugin configuration
+** dialog. You have to check it in first.
+**
+** Use the ant target 'publish' to check in the plugin and make it available to other
+** JOSM users:
+**    set the properties commit.message and plugin.main.version
+** and run
+**    > ant  publish
+**
+**
+-->
+<project name="trustosm" default="dist" basedir=".">
+
+	<!-- enter the SVN commit message -->
+	<property name="commit.message" value="New plugin for digital signing osm data" />
+	<!-- enter the *lowest* JOSM version this plugin is currently compatible with -->
+	<property name="plugin.main.version" value="3329" />
+
+
+    <!--
+      ************************************************
+      ** should not be necessary to change the following properties
+     -->
+	<property name="josm"                   location="../../core/dist/josm-custom.jar"/>
+	<property name="plugin.build.dir"       value="build"/>
+	<property name="plugin.src.dir"         value="src"/>
+	<property name="plugin.lib.dir"         value="lib"/>
+	<!-- this is the directory where the plugin jar is copied to -->
+	<property name="plugin.dist.dir"        value="../../dist"/>
+	<property name="ant.build.javac.target" value="1.6"/>
+	<property name="plugin.dist.dir"        value="../../dist"/>
+	<property name="plugin.jar"             value="${plugin.dist.dir}/${ant.project.name}.jar"/>
+
+    <!--
+    **********************************************************
+    ** init - initializes the build
+    **********************************************************
+    -->
+    <target name="init">
+        <mkdir dir="${plugin.build.dir}"/>
+    </target>
+
+    <!--
+    **********************************************************
+    ** compile - complies the source tree
+    **********************************************************
+    -->
+	<target name="compile" depends="init">
+		<echo message="compiling sources for  ${plugin.jar} ... "/>
+		<javac srcdir="src" classpath="${josm}" debug="true" destdir="${plugin.build.dir}">
+			<compilerarg value="-Xlint:deprecation"/>
+			<compilerarg value="-Xlint:unchecked"/>
+			<classpath>
+				<pathelement location="${josm}"/>
+				<fileset dir="${plugin.lib.dir}">
+					<include name="**/*.jar"/>
+				</fileset>
+			</classpath>
+		</javac>
+	</target>
+
+	
+	<!-- create a property containing all .jar files, prefix lib/, and seperated with a space -->
+	  <pathconvert property="libs.project" pathsep=" ">
+	    <mapper>
+	      <chainedmapper>
+
+	        <!-- remove absolute path -->
+	        <flattenmapper />
+
+	        <!-- add lib/ prefix -->
+	        <globmapper from="*" to="${ant.project.name}/lib/*" />
+	      </chainedmapper>
+	    </mapper>
+
+	    <path>
+
+	      <!-- plugin.lib.dir contains all jar files -->
+	      <fileset dir="${plugin.lib.dir}">
+	        <include name="**/*.jar" />
+	      </fileset>
+	    </path>
+	  </pathconvert>
+
+	
+	<!--
+    **********************************************************
+    ** dist - creates the plugin jar
+    **********************************************************
+    -->
+	<target name="dist" depends="compile,revision">
+		<echo message="creating ${ant.project.name}.jar ... "/>
+		<copy todir="${plugin.build.dir}/jce">
+			<fileset dir="jce"/>
+		</copy>
+		<copy todir="${plugin.build.dir}/lib">
+			<fileset dir="${plugin.lib.dir}"/>
+		</copy>
+		<copy todir="${plugin.build.dir}/resources">
+			<fileset dir="resources"/>
+		</copy>
+		<copy todir="${plugin.build.dir}/images">
+			<fileset dir="images"/>
+		</copy>
+		<copy todir="${plugin.build.dir}">
+			<fileset dir=".">
+				<include name="README" />
+				<include name="LICENSE" />
+			</fileset>
+		</copy>
+		<jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
+			<!--
+        ************************************************
+        ** configure these properties. Most of them will be copied to the plugins
+        ** manifest file. Property values will also show up in the list available
+        ** plugins: http://josm.openstreetmap.de/wiki/Plugins.
+        **
+        ************************************************
+    -->
+			<manifest>
+				<attribute name="Author" value="Christoph Wagner"/>
+				<attribute name="Class-Path" value="${libs.project}" />
+				<attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.trustosm.TrustOSMplugin"/>
+				<attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
+				<attribute name="Plugin-Description" value="Plugin to digital sign OSM-Data"/>
+				<attribute name="Plugin-Icon" value="trustosm"/>
+				<attribute name="Plugin-Link" value="..."/>
+				<attribute name="Plugin-Mainversion" value="${plugin.main.version}"/>
+				<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
+			</manifest>
+		</jar>
+	</target>
+
+    <!--
+    **********************************************************
+    ** revision - extracts the current revision number for the
+    **    file build.number and stores it in the XML property
+    **    version.*
+    **********************************************************
+    -->
+    <target name="revision">
+
+        <exec append="false" output="REVISION" executable="svn" failifexecutionfails="false">
+            <env key="LANG" value="C"/>
+            <arg value="info"/>
+            <arg value="--xml"/>
+            <arg value="."/>
+        </exec>
+        <xmlproperty file="REVISION" prefix="version" keepRoot="false" collapseAttributes="true"/>
+        <delete file="REVISION"/>
+    </target>
+
+    <!--
+    **********************************************************
+    ** clean - clean up the build environment
+    **********************************************************
+    -->
+    <target name="clean">
+        <delete dir="${plugin.build.dir}"/>
+        <delete file="${plugin.jar}"/>
+    </target>
+
+    <!--
+    **********************************************************
+    ** install - install the plugin in your local JOSM installation
+    **********************************************************
+    -->
+    <target name="install" depends="dist">
+        <property environment="env"/>
+        <condition property="josm.plugins.dir" value="${env.APPDATA}/JOSM/plugins" else="${user.home}/.josm/plugins">
+            <and>
+                <os family="windows"/>
+            </and>
+        </condition>
+        <copy file="${plugin.jar}" todir="${josm.plugins.dir}"/>
+    </target>
+
+
+    <!--
+    ************************** Publishing the plugin *********************************** 
+    -->
+    <!--
+        ** extracts the JOSM release for the JOSM version in ../core and saves it in the 
+        ** property ${coreversion.info.entry.revision}
+        **
+        -->
+    <target name="core-info">
+        <exec append="false" output="core.info.xml" executable="svn" failifexecutionfails="false">
+            <env key="LANG" value="C"/>
+            <arg value="info"/>
+            <arg value="--xml"/>
+            <arg value="../../core"/>
+        </exec>
+        <xmlproperty file="core.info.xml" prefix="coreversion" keepRoot="true" collapseAttributes="true"/>
+        <echo>Building against core revision ${coreversion.info.entry.revision}.</echo>
+        <echo>Plugin-Mainversion is set to ${plugin.main.version}.</echo>
+        <delete file="core.info.xml" />
+    </target>
+
+    <!--
+        ** commits the source tree for this plugin
+        -->
+    <target name="commit-current">
+        <echo>Commiting the plugin source with message '${commit.message}' ...</echo>
+        <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+            <env key="LANG" value="C"/>
+            <arg value="commit"/>
+            <arg value="-m '${commit.message}'"/>
+            <arg value="."/>
+        </exec>
+    </target>
+
+    <!--
+        ** updates (svn up) the source tree for this plugin
+        -->
+    <target name="update-current">
+        <echo>Updating plugin source ...</echo>
+        <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+            <env key="LANG" value="C"/>
+            <arg value="up"/>
+            <arg value="."/>
+        </exec>
+        <echo>Updating ${plugin.jar} ...</echo>
+        <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+            <env key="LANG" value="C"/>
+            <arg value="up"/>
+            <arg value="../dist/${plugin.jar}"/>
+        </exec>
+    </target>
+
+    <!--
+        ** commits the plugin.jar 
+        -->
+    <target name="commit-dist">
+        <echo>
+    ***** Properties of published ${plugin.jar} *****
+    Commit message    : '${commit.message}'                    
+    Plugin-Mainversion: ${plugin.main.version}
+    JOSM build version: ${coreversion.info.entry.revision}
+    Plugin-Version    : ${version.entry.commit.revision}
+    ***** / Properties of published ${plugin.jar} *****                    
+                        
+    Now commiting ${plugin.jar} ...
+    </echo>
+        <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+            <env key="LANG" value="C"/>
+            <arg value="-m '${commit.message}'"/>
+            <arg value="commit"/>
+            <arg value="${plugin.jar}"/>
+        </exec>
+    </target>
+
+    <!-- ** make sure svn is present as a command line tool ** -->
+    <target name="ensure-svn-present">
+        <exec append="true" output="svn.log" executable="svn" failifexecutionfails="false" failonerror="false" resultproperty="svn.exit.code">
+            <env key="LANG" value="C" />
+            <arg value="--version" />
+        </exec>
+        <fail message="Fatal: command 'svn --version' failed. Please make sure svn is installed on your system.">
+            <!-- return code not set at all? Most likely svn isn't installed -->
+            <condition>
+                <not>
+                    <isset property="svn.exit.code" />
+                </not>
+            </condition>
+        </fail>
+        <fail message="Fatal: command 'svn --version' failed. Please make sure a working copy of svn is installed on your system.">
+            <!-- error code from SVN? Most likely svn is not what we are looking on this system -->
+            <condition>
+                <isfailure code="${svn.exit.code}" />
+            </condition>
+        </fail>
+    </target>
+
+    <target name="publish" depends="ensure-svn-present,core-info,commit-current,update-current,clean,dist,commit-dist">
+    </target>
+</project>
Index: /applications/editors/josm/plugins/trustosm/resources/syllables.txt
===================================================================
--- /applications/editors/josm/plugins/trustosm/resources/syllables.txt	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/resources/syllables.txt	(revision 24389)
@@ -0,0 +1,35 @@
+-a
+-al
+-au +c
+-an
+-ba
+-be
+-bi
+-br +v
+-da
+-di
+-do
+-du
+-e
+-eu +c
+-fa
+bi
+be
+bo
+bu
+nul +v
+gu
+da
+au +c -c
+fri
+gus
++tus
++lus
++lius
++nus
++es
++ius -c
++cus
++tor
++cio
++tin 
Index: /applications/editors/josm/plugins/trustosm/resources/trustXML.dtd
===================================================================
--- /applications/editors/josm/plugins/trustosm/resources/trustXML.dtd	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/resources/trustXML.dtd	(revision 24389)
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--DTD for XML Format to store PGPSignatures and OSM-Object references-->
+
+<!-- ROOT-ELEMENT -->
+<!ELEMENT trustcollection (trustitem)*>
+<!ATTLIST trustcollection version CDATA #IMPLIED creator CDATA #IMPLIED >
+
+<!-- A trustitem stores all signatures that are referenced to a specific OSM-Object -->
+<!ELEMENT trustitem (signatures)*>
+<!-- a trustitem needs the referenced OSM-ID and the API-Type of that OSM-Object to find it faster in the OSM-DB -->
+<!ATTLIST trustitem 
+osmid	CDATA	#REQUIRED
+type	CDATA	#REQUIRED
+>
+
+<!-- signatures are available for simple tags or geometry objects -->
+<!ELEMENT signatures (tags|geometry)*>
+
+<!-- tags contain all signed keys with their signature -->
+<!ELEMENT tags (key)*>
+
+<!-- every different value for a specified key gets its own openpgp tag -->
+<!ELEMENT key (openpgp)*>
+<!ATTLIST key k CDATA #REQUIRED >
+
+<!-- geometry contains all signed nodes with their signature -->
+<!ELEMENT geometry (node)*>
+
+<!-- every node gets its own openpgp tag -->
+<!ELEMENT node (openpgp)*>
+<!ATTLIST node id CDATA #REQUIRED >
+
+
+<!-- the openpgp tag is a clearsigned OpenPGP message -->
+<!ELEMENT openpgp (#PCDATA)*>
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/TrustOSMplugin.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/TrustOSMplugin.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/TrustOSMplugin.java	(revision 24389)
@@ -0,0 +1,279 @@
+package org.openstreetmap.josm.plugins.trustosm;
+
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
+import static org.openstreetmap.josm.tools.I18n.marktr;
+
+import java.awt.event.KeyEvent;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.gui.MainMenu;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.plugins.Plugin;
+import org.openstreetmap.josm.plugins.PluginInformation;
+import org.openstreetmap.josm.plugins.trustosm.actions.ExportSigsAction;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustOSMItem;
+import org.openstreetmap.josm.plugins.trustosm.gui.dialogs.TrustDialog;
+import org.openstreetmap.josm.plugins.trustosm.gui.preferences.TrustPreferenceEditor;
+import org.openstreetmap.josm.plugins.trustosm.io.SigExporter;
+import org.openstreetmap.josm.plugins.trustosm.io.SigImporter;
+import org.openstreetmap.josm.plugins.trustosm.util.TrustGPG;
+
+public class TrustOSMplugin extends Plugin {
+
+	static JMenu gpgJMenu;
+
+	private TrustDialog trustDialog;
+
+	/** Use a TrustGPGPreparer to sign or validate signatures */
+	public static TrustGPG gpg;
+
+	/** A global list with all OSM-Ids and corresponding TrustOSMItems */
+	public static final Map<String, TrustOSMItem> signedItems = new HashMap<String, TrustOSMItem>();
+
+	/**
+	 * Will be invoked by JOSM to bootstrap the plugin
+	 *
+	 * @param info  information about the plugin and its local installation
+	 */
+	public TrustOSMplugin(PluginInformation info) {
+		// init the plugin
+		super(info);
+		refreshMenu();
+		checkForUnrestrictedPolicyFiles();
+		extractFiles("trustosm","lib");
+		// register new SigImporter and SigExporter
+		ExtensionFileFilter.importers.add(new SigImporter());
+		ExtensionFileFilter.exporters.add(new SigExporter());
+
+		gpg = new TrustGPG();
+		setSettings();
+		File gpgDir = new File(getGpgPath());
+		if (!gpgDir.exists())
+			gpgDir.mkdirs();
+
+	}
+
+	public static void checkForUnrestrictedPolicyFiles() {
+		byte[] data = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
+
+		// create a cipher and attempt to encrypt the data block with our key
+		try{
+			Cipher c = Cipher.getInstance("AES");
+			// create a 192 bit secret key from raw bytes
+
+			SecretKey key192 = new SecretKeySpec(new byte[] { 0x00, 0x01, 0x02,
+					0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+					0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+					0x17 }, "AES");
+
+			// now try encrypting with the larger key
+
+			c.init(Cipher.ENCRYPT_MODE, key192);
+			c.doFinal(data);
+		} catch (Exception e) {
+			e.printStackTrace();
+			System.err.println("It seems that the Unrestricted Policy Files are not available in this JVM. So high level crypto is not allowed. Problems may occure.");
+			//extractFiles("trustosm","jce");
+			installUnrestrictedPolicyFiles();
+		}
+	}
+
+	public static boolean installUnrestrictedPolicyFiles() {
+		/*
+		String[] cmd = new String[3];
+		cmd[0] = "sudo";
+		cmd[1] = "-S";
+		cmd[2] = "/tmp/skript.sh";
+
+		try
+		{
+			Process p = Runtime.getRuntime().exec(cmd);
+			OutputStream os = p.getOutputStream();
+			Writer writer = new OutputStreamWriter(os);
+
+			JPasswordField passwordField = new JPasswordField(10);
+			JOptionPane.showMessageDialog(null, passwordField, "Enter password", JOptionPane.OK_OPTION);
+			String password = passwordField.getPassword().toString();
+
+			writer.write(password + "\n");
+			writer.close();
+			InputStream in = p.getInputStream();
+			BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+			String satz = reader.readLine();
+			while (satz != null)
+			{
+				System.out.println(satz);
+				satz = reader.readLine();
+			}
+			int rc = p.waitFor();
+		}
+		catch (Exception e)
+		{
+			System.out.println(e.toString());
+		}
+
+		/*
+
+		Process	p;
+		int exitCode;
+		String stdout,stderr;
+		String sysSecPath = System.getProperty("java.home")+"/lib/security";
+		File localPolicy = new File(sysSecPath+"/local_policy.jar");
+		if (!localPolicy.exists()) {
+			System.err.println("No local_policy.jar file found in "+sysSecPath+"\n Is this the right java directory?");
+			return false;
+		}
+
+
+		String cmd = "sh -c sudo -S mv "+sysSecPath+"/local_policy.jar "+sysSecPath+"/local_policy.jar.restricted";
+		/*		String cmd2 = "sudo -S mv "+sysSecPath+"/US_export_policy.jar "+sysSecPath+"/US_export_policy.jar.restricted";
+		String cmd3 = "sudo -S cp "+Main.pref.getPluginsDirectory().getPath()+"/trustosm/jce/US_export_policy.jar "+sysSecPath;
+		String cmd4 = "sudo -S cp "+Main.pref.getPluginsDirectory().getPath()+"/trustosm/jce/local_policy.jar "+sysSecPath;
+
+
+		//System.out.println (cmd);
+
+		try
+		{
+			p = Runtime.getRuntime().exec(cmd);
+		}
+		catch(IOException io)
+		{
+			System.err.println ("io Error" + io.getMessage ());
+			return false;
+		}
+
+		JPasswordField passwordField = new JPasswordField(10);
+		JOptionPane.showMessageDialog(null, passwordField, "Enter password", JOptionPane.OK_OPTION);
+		String password = passwordField.getPassword().toString();
+
+		if (password != null)
+		{
+			BufferedWriter out = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
+			try
+			{
+				out.write(password);
+				out.close();
+			}
+			catch(IOException io)
+			{
+				System.err.println("Exception at write! " + io.getMessage ());
+				return false;
+			}
+		}
+
+		try
+		{
+			exitCode = p.exitValue ();
+			if (exitCode==0) {
+				System.err.println("Everything seems to be ok.");
+			} else {
+				System.err.println("Exit code was not 0.");
+				StringBuffer buf = new StringBuffer();
+				InputStream errIn = p.getErrorStream();
+				int read;
+				while ((read = errIn.read()) != -1) {
+					buf.append(read);
+				}
+				System.err.println(buf.toString());
+			}
+		}
+		catch (IllegalThreadStateException itse)
+		{
+			return false;
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+
+		 */
+		return false;
+	}
+
+	public static void extractFiles(String pluginname, String extractDir) {
+		try {
+			if (extractDir == null) extractDir = "lib";
+			JarFile jar = new JarFile(Main.pref.getPluginsDirectory().getPath()+"/"+pluginname+".jar");
+			Enumeration<JarEntry> entries = jar.entries();
+			InputStream is;
+			FileOutputStream fos;
+			File file;
+			while (entries.hasMoreElements()) {
+				JarEntry entry = entries.nextElement();
+				String name = entry.getName();
+				if (name.startsWith(extractDir+"/") && !entry.isDirectory()) {
+					System.out.println(Main.pref.getPluginsDirectory().getPath()+"/"+pluginname+"/"+name);
+					file = new File(Main.pref.getPluginsDirectory().getPath()+"/"+pluginname+"/"+name);
+					file.getParentFile().mkdirs();
+					is = jar.getInputStream(entry);
+					fos = new FileOutputStream(file);
+					while (is.available() > 0) {  // write contents of 'is' to 'fos'
+						fos.write(is.read());
+					}
+					fos.close();
+					is.close();
+				}
+			}
+
+		} catch (IOException e) {
+			e.printStackTrace();
+
+		}
+
+	}
+
+	public static void refreshMenu() {
+		MainMenu menu = Main.main.menu;
+
+		if (gpgJMenu == null) {
+			gpgJMenu = menu.addMenu(marktr("GPG"), KeyEvent.VK_G, menu.defaultMenuPos, ht("/Plugin/trustosm"));
+			gpgJMenu.add(new JMenuItem(new ExportSigsAction()));
+		}
+
+	}
+
+	public static void setSettings() {
+		Map<String,String> prefs = Main.pref.getAllPrefix("trustosm.");
+
+		// if setting isn't present, we set a default
+		// This makes sense for example when we start the plugin for the first time
+		if (!prefs.containsKey("trustosm.gpg")) Main.pref.put("trustosm.gpg", "gpg");
+		if (!prefs.containsKey("trustosm.gpg.separateHomedir")) Main.pref.put("trustosm.gpg.separateHomedir", true);
+	}
+
+
+	@Override
+	public PreferenceSetting getPreferenceSetting() {
+		return new TrustPreferenceEditor();
+	}
+
+	@Override
+	public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
+		if (oldFrame==null && newFrame!=null) {
+			trustDialog = new TrustDialog();
+			newFrame.addToggleDialog(trustDialog);
+		}
+	}
+
+	public static String getGpgPath() {
+		return Main.pref.getPluginsDirectory().getPath() + "/trustosm/gnupg/";
+	}
+
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/actions/ExportSigsAction.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/actions/ExportSigsAction.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/actions/ExportSigsAction.java	(revision 24389)
@@ -0,0 +1,47 @@
+package org.openstreetmap.josm.plugins.trustosm.actions;
+
+import static org.openstreetmap.josm.actions.SaveActionBase.createAndOpenSaveFileChooser;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.io.File;
+import java.io.IOException;
+
+import org.openstreetmap.josm.actions.DiskAccessAction;
+import org.openstreetmap.josm.plugins.trustosm.io.SigExporter;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public class ExportSigsAction extends DiskAccessAction {
+
+	public ExportSigsAction() {
+		super(tr("Export sigs..."), "exportsigs", tr("Export all signatures to XML file."),
+				Shortcut.registerShortcut("file:exportsigs", tr("Export sigs to XML..."), KeyEvent.VK_E, Shortcut.GROUP_MENU));
+	}
+
+	public ExportSigsAction(String name, String iconName, String tooltip, Shortcut shortcut) {
+		super(name, iconName, tooltip, shortcut);
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		if (!isEnabled())
+			return;
+		doSave();
+	}
+
+	public boolean doSave() {
+		File f = createAndOpenSaveFileChooser(tr("Save Signatures file"), "tosm");
+		if (f == null)
+			return false;
+		SigExporter exporter = new SigExporter();
+		try {
+			exporter.exportData(f, null);
+		} catch (IOException e) {
+			e.printStackTrace();
+			return false;
+		}
+		return true;
+	}
+
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/actions/GetMissingDataAction.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/actions/GetMissingDataAction.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/actions/GetMissingDataAction.java	(revision 24389)
@@ -0,0 +1,66 @@
+package org.openstreetmap.josm.plugins.trustosm.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.plugins.trustosm.TrustOSMplugin;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustOSMItem;
+import org.openstreetmap.josm.plugins.trustosm.gui.DownloadSignedOsmDataTask;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public class GetMissingDataAction extends JosmAction {
+
+	public GetMissingDataAction() {
+		super(tr("Download OSM"),"getmissing",tr("Get all referenced but not actually present OSM objects from OSM server."),
+				Shortcut.registerShortcut("gpg:download", tr("Download referenced osm objects..."), KeyEvent.VK_D, Shortcut.GROUP_MENU),true);
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent arg0) {
+		if (!isEnabled())
+			return;
+		downloadMissing();
+	}
+
+	public boolean downloadMissing() {
+		Collection<OsmPrimitive> missingData = new HashSet<OsmPrimitive>();
+		Map<String,TrustOSMItem> trustitems = TrustOSMplugin.signedItems;
+		getMissing(trustitems, missingData);
+
+		int missingCount = missingData.size();
+		int itemCount = trustitems.size();
+		if (missingCount == 0) {
+			JOptionPane.showMessageDialog(Main.parent, tr("{0} Signatures loaded. All referenced OSM objects found.",itemCount));
+		} else {
+			int n = JOptionPane.showOptionDialog(Main.parent, tr("{0} of {1} OSM objects are referenced but not there.\nDo you want to load them from OSM-Server?",missingCount,itemCount), tr("Load objects from server"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null);
+
+			if (n == JOptionPane.YES_OPTION) {
+				Main.worker.submit(new DownloadSignedOsmDataTask(missingData, Main.main.getEditLayer()));
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	public void getMissing(Map<String,TrustOSMItem> trustitems, Collection<OsmPrimitive> missingData) {
+		Collection<OsmPrimitive> presentData = getCurrentDataSet().allPrimitives();
+		for (TrustOSMItem t : trustitems.values()) {
+			OsmPrimitive osm = t.getOsmItem();
+			if (!presentData.contains(osm))
+				missingData.add(osm);
+		}
+	}
+
+
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/data/TrustOSMItem.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/data/TrustOSMItem.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/data/TrustOSMItem.java	(revision 24389)
@@ -0,0 +1,89 @@
+package org.openstreetmap.josm.plugins.trustosm.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.openpgp.PGPSignature;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.plugins.trustosm.util.TrustGPG;
+
+public class TrustOSMItem {
+
+	private OsmPrimitive osm;
+	private final Map<String, TrustSignatures> keySig = new HashMap<String, TrustSignatures>();
+	private final Map<Node, TrustSignatures> geomSig = new HashMap<Node, TrustSignatures>();
+
+	public TrustOSMItem(OsmPrimitive osmItem) {
+		this.osm = osmItem;
+
+		/*for (String key: osm.keySet()) {
+			keySig.put(key, new TrustSignatures());
+		}*/
+	}
+
+	public OsmPrimitive getOsmItem() {
+		return osm;
+	}
+
+	public void setOsmItem(OsmPrimitive osmItem) {
+		this.osm = osmItem;
+	}
+
+	public void storeAllTagSigs(Map<String, TrustSignatures> sigs){
+		keySig.putAll(sigs);
+	}
+
+	public void storeTagSig(String key, PGPSignature sig) {
+		if (keySig.containsKey(key)) {
+			keySig.get(key).addSignature(sig, TrustGPG.generateTagSigtext(osm, key));
+			return;
+		} else if (osm.keySet().contains(key)) {
+			keySig.put(key, new TrustSignatures(sig, TrustGPG.generateTagSigtext(osm, key), TrustSignatures.SIG_VALID));
+		}
+	}
+
+	public void storeNodeSig(Node node, PGPSignature sig) {
+		if (geomSig.containsKey(node)) {
+			geomSig.get(node).addSignature(sig, TrustGPG.generateNodeSigtext(osm, node));
+		} else {
+			geomSig.put(node, new TrustSignatures(sig, TrustGPG.generateNodeSigtext(osm, node), TrustSignatures.SIG_VALID));
+		}
+	}
+
+	public Map<Node, TrustSignatures> getGeomSigs() {
+		return geomSig;
+	}
+
+	public TrustSignatures getSigsOnNode(Node node) {
+		return geomSig.get(node);
+	}
+
+	public Map<String, TrustSignatures> getTagSigs() {
+		return keySig;
+	}
+
+	public TrustSignatures getSigsOnKey(String key) {
+		return keySig.get(key);
+	}
+
+	public void updateTagSigStatus(String key, byte status) {
+		if (keySig.containsKey(key)) {
+			keySig.get(key).setStatus(status);
+		} else if (osm.keySet().contains(key)) {
+			TrustSignatures tsigs = new TrustSignatures();
+			tsigs.setStatus(status);
+			keySig.put(key, tsigs);
+		}
+	}
+
+	public void updateNodeSigStatus(Node node, byte status) {
+		if (geomSig.containsKey(node)) {
+			geomSig.get(node).setStatus(status);
+		} else {
+			TrustSignatures tsigs = new TrustSignatures();
+			tsigs.setStatus(status);
+			geomSig.put(node, tsigs);
+		}
+	}
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/data/TrustSignatures.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/data/TrustSignatures.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/data/TrustSignatures.java	(revision 24389)
@@ -0,0 +1,161 @@
+package org.openstreetmap.josm.plugins.trustosm.data;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
+import org.bouncycastle.bcpg.ArmoredOutputStream;
+import org.bouncycastle.bcpg.BCPGOutputStream;
+import org.bouncycastle.openpgp.PGPSignature;
+
+public class TrustSignatures {
+
+	public final static byte SIG_UNKNOWN = 0;
+	public final static byte SIG_VALID = 1;
+	public final static byte SIG_BROKEN = -1;
+	public final static byte ITEM_REMOVED = -2;
+
+	//private final Vector<PGPSignature> signatures = new Vector<PGPSignature>();
+	//	private final HashMap<PGPSignature, String> signatureTextMap = new HashMap<PGPSignature, String>();
+	private final Map<String, List<PGPSignature>> textsigs = new HashMap<String, List<PGPSignature>>();
+	private byte status;
+
+	public TrustSignatures() {
+		this.status = SIG_UNKNOWN;
+	}
+
+	public int countSigs() {
+		//		return signatures.size();
+		//return signatureTextMap.size();
+		int count = 0;
+		for (List<PGPSignature> siglist : textsigs.values()) {
+			count += siglist.size();
+		}
+		return count;
+	}
+
+	public TrustSignatures(PGPSignature signature, String sigtext, byte status) {
+		this.status = status;
+		addSignature(signature, sigtext);
+	}
+
+	public void setStatus(byte status) {
+		this.status = status;
+	}
+
+	public byte getStatus() {
+		return status;
+	}
+
+	/*	public void setSignatures(Vector<PGPSignature> signatures) {
+		this.signatures.addAll(signatures);
+	}
+	 */
+	public Vector<PGPSignature> getSignatures() {
+		//		return signatures;
+		Vector<PGPSignature> sigs = new Vector<PGPSignature>();
+		for (List<PGPSignature> siglist : textsigs.values()) {
+			sigs.addAll(siglist);
+		}
+		return sigs;
+	}
+
+	public Map<String, List<PGPSignature>> getSignaturesWithText() {
+		//		return signatures;
+		return textsigs;
+	}
+
+	public List<PGPSignature> getSignaturesByPlaintext(String plain) {
+		return textsigs.get(plain);
+	}
+
+	public void addSignature(PGPSignature signature, String sigtext) {
+		//		signatures.add(signature);
+		//signatureTextMap.put(signature, sigtext);
+		if (textsigs.containsKey(sigtext)) {
+			textsigs.get(sigtext).add(signature);
+		} else {
+			List<PGPSignature> l = new ArrayList<PGPSignature>();
+			l.add(signature);
+			textsigs.put(sigtext, l);
+		}
+	}
+
+	/*	public void addSignatures(List<PGPSignature> signatures, String sigtext) {
+		textsigs.get(sigtext).addAll(signatures);
+	}
+	 */
+
+	/*
+	public PGPSignature getLatestSignature() {
+		return signatures.lastElement();
+	}
+	 */
+	public Set<String> getAllPlainTexts() {
+		return textsigs.keySet();
+	}
+
+	public String getSigtext(PGPSignature signature) {
+		for (String sigtext : textsigs.keySet()) {
+			if (textsigs.get(sigtext).contains(signature)) return sigtext;
+		}
+		return "";
+	}
+
+	public String getArmoredFulltextSignatureAll(String plain) {
+		if (textsigs.containsKey(plain)){
+			List<PGPSignature> l = textsigs.get(plain);
+			try {
+				ByteArrayOutputStream baos = new ByteArrayOutputStream();
+				ArmoredOutputStream aOut = new ArmoredOutputStream(baos);
+				aOut.beginClearText(l.get(0).getHashAlgorithm());
+				aOut.write(plain.getBytes(Charset.forName("UTF-8")));
+				aOut.write('\n');
+				aOut.endClearText();
+
+				BCPGOutputStream bOut = new BCPGOutputStream(aOut);
+				for (PGPSignature sig : l) {
+					sig.encode(bOut);
+				}
+
+				bOut.close();
+				aOut.close();
+
+				return baos.toString("UTF-8");
+
+			} catch (Exception e) {
+				e.printStackTrace();
+				return "Error - read console Output";
+			}
+		}
+		return "No sigs available";
+	}
+
+	public String getArmoredFulltextSignature(PGPSignature sig) {
+		try {
+			ByteArrayOutputStream baos = new ByteArrayOutputStream();
+			ArmoredOutputStream aOut = new ArmoredOutputStream(baos);
+			aOut.beginClearText(sig.getHashAlgorithm());
+			aOut.write(getSigtext(sig).getBytes(Charset.forName("UTF-8")));
+			aOut.write('\n');
+			aOut.endClearText();
+
+			BCPGOutputStream bOut = new BCPGOutputStream(aOut);
+			sig.encode(bOut);
+			bOut.close();
+			aOut.close();
+
+
+			return baos.toString("UTF-8");
+		} catch (Exception e) {
+			e.printStackTrace();
+			return "Error - read console Output";
+		}
+	}
+
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/DownloadSignedOsmDataTask.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/DownloadSignedOsmDataTask.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/DownloadSignedOsmDataTask.java	(revision 24389)
@@ -0,0 +1,127 @@
+package org.openstreetmap.josm.plugins.trustosm.gui;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.I18n.trn;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import javax.swing.SwingUtilities;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.AutoScaleAction;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.ExceptionDialogUtil;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.plugins.trustosm.TrustOSMplugin;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustOSMItem;
+import org.xml.sax.SAXException;
+
+public class DownloadSignedOsmDataTask  extends PleaseWaitRunnable {
+
+	private boolean cancelled;
+	private Exception lastException;
+	private final Collection<OsmPrimitive> missing;
+	private final OsmDataLayer curLayer;
+	private MultiFetchServerObjectReader objectReader;
+
+	/**
+	 * Download the given OSMPrimitives to the given layer
+	 * 
+	 */
+	public DownloadSignedOsmDataTask(Collection<OsmPrimitive> missing, OsmDataLayer curLayer) {
+		super(tr("Download signed data"));
+		this.missing = missing;
+		this.curLayer = curLayer;
+	}
+
+
+	@Override
+	protected void cancel() {
+		cancelled = true;
+		synchronized(this) {
+			if (objectReader != null) {
+				objectReader.cancel();
+			}
+		}
+	}
+
+	@Override
+	protected void finish() {
+		Main.map.repaint();
+		if (cancelled)
+			return;
+		if (lastException != null) {
+			ExceptionDialogUtil.explainException(lastException);
+		}
+	}
+
+	protected String buildDownloadFeedbackMessage() {
+		return trn("Downloading {0} incomplete child of relation ''{1}''",
+				"Downloading {0} incomplete children of relation ''{1}''",
+				missing.size(),
+				missing.size(),
+				"Wurst"
+		);
+	}
+
+	@Override
+	protected void realRun() throws SAXException, IOException, OsmTransferException {
+		try {
+			synchronized (this) {
+				if (cancelled) return;
+				objectReader = new MultiFetchServerObjectReader();
+			}
+			objectReader.append(missing);
+			progressMonitor.indeterminateSubTask(
+					buildDownloadFeedbackMessage()
+			);
+			final DataSet dataSet = objectReader.parseOsm(progressMonitor
+					.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
+			if (dataSet == null)
+				return;
+			synchronized (this) {
+				if (cancelled) return;
+				objectReader = null;
+			}
+
+			SwingUtilities.invokeLater(
+					new Runnable() {
+						public void run() {
+							curLayer.mergeFrom(dataSet);
+							curLayer.onPostDownloadFromServer();
+							AutoScaleAction.zoomTo(dataSet.allPrimitives());
+							updateReferences(dataSet);
+						}
+					}
+			);
+
+		} catch (Exception e) {
+			if (cancelled) {
+				System.out.println(tr("Warning: ignoring exception because task is cancelled. Exception: {0}", e.toString()));
+				return;
+			}
+			lastException = e;
+		}
+	}
+
+	public boolean updateReferences(DataSet ds) {
+		for (TrustOSMItem t : TrustOSMplugin.signedItems.values()) {
+			OsmPrimitive osm = ds.getPrimitiveById(t.getOsmItem().getPrimitiveId());
+			if (osm != null) {
+				t.setOsmItem(osm);
+				return true;
+			} else {
+				System.out.println("No item found");
+			}
+		}
+		return false;
+	}
+
+}
+
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/KeyGenerationTask.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/KeyGenerationTask.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/KeyGenerationTask.java	(revision 24389)
@@ -0,0 +1,83 @@
+package org.openstreetmap.josm.plugins.trustosm.gui;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+
+import org.openstreetmap.josm.gui.ExceptionDialogUtil;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.xml.sax.SAXException;
+
+public class KeyGenerationTask extends PleaseWaitRunnable {
+
+	private boolean cancelled;
+	private Exception lastException;
+
+	public KeyGenerationTask() {
+		super(tr("Generating new Keypair.\nCollecting randomness..."));
+	}
+
+	@Override
+	protected void cancel() {
+		cancelled = true;
+		synchronized(this) {
+			/*		if (objectReader != null) {
+				objectReader.cancel();
+			}*/
+		}
+	}
+
+	@Override
+	protected void finish() {
+		if (cancelled)
+			return;
+		if (lastException != null) {
+			ExceptionDialogUtil.explainException(lastException);
+		}
+
+	}
+
+	@Override
+	protected void realRun() throws SAXException, IOException,
+	OsmTransferException {
+		try {
+			/*			synchronized (this) {
+				if (cancelled) return;
+				objectReader = new MultiFetchServerObjectReader();
+			}
+			objectReader.append(missing);
+			progressMonitor.indeterminateSubTask(
+					buildDownloadFeedbackMessage()
+			);
+			final DataSet dataSet = objectReader.parseOsm(progressMonitor
+					.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
+			if (dataSet == null)
+				return;
+			synchronized (this) {
+				if (cancelled) return;
+				objectReader = null;
+			}
+
+			SwingUtilities.invokeLater(
+					new Runnable() {
+						public void run() {
+							curLayer.mergeFrom(dataSet);
+							curLayer.onPostDownloadFromServer();
+							AutoScaleAction.zoomTo(dataSet.allPrimitives());
+						}
+					}
+			);
+			 */
+		} catch (Exception e) {
+			if (cancelled) {
+				System.out.println(tr("Warning: ignoring exception because task is cancelled. Exception: {0}", e
+						.toString()));
+				return;
+			}
+			lastException = e;
+		}
+
+	}
+
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/KeyTreeTableModel.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/KeyTreeTableModel.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/KeyTreeTableModel.java	(revision 24389)
@@ -0,0 +1,151 @@
+package org.openstreetmap.josm.plugins.trustosm.gui;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.bouncycastle.bcpg.SignatureSubpacketTags;
+import org.bouncycastle.bcpg.sig.NotationData;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPSignature;
+import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
+import org.jdesktop.swingx.treetable.AbstractTreeTableModel;
+import org.openstreetmap.josm.plugins.trustosm.TrustOSMplugin;
+
+public class KeyTreeTableModel extends AbstractTreeTableModel {
+
+	public static String convPGPSignatureToString(PGPSignature s) {
+		PGPSignatureSubpacketVector sv = s.getHashedSubPackets();
+		if (sv.hasSubpacket(SignatureSubpacketTags.SIGNER_USER_ID))
+			return sv.getSignerUserID();
+
+		PGPPublicKey pub = TrustOSMplugin.gpg.getPublicKeyFromRing(s.getKeyID());
+		if (pub != null){
+			Iterator i = pub.getUserIDs();
+			if (i.hasNext())
+				return (String)i.next();
+		}
+		return tr("unknown");
+	}
+
+	private final SignatureTreeNode root;
+	private final String[] allTitle = {tr("UID"),tr("KeyID"),tr("OSM-Cert"),tr("Signed")};
+	private final List<String> columns = new ArrayList<String>(Arrays.asList(allTitle));
+
+	public KeyTreeTableModel(Collection<PGPSignature> sigs) {
+		root = new SignatureTreeNode();
+		for (PGPSignature s : sigs){
+			SignatureTreeNode sn = new SignatureTreeNode(s);
+			PGPPublicKey pub = TrustOSMplugin.gpg.getPublicKeyFromRing(s.getKeyID());
+			Iterator iter = pub.getSignatures();
+			while (iter.hasNext()){
+				PGPSignature ks = (PGPSignature)iter.next();
+				sn.getChildren().add(new SignatureTreeNode(ks));
+			}
+			root.getChildren().add(sn);
+		}
+	}
+
+	@Override
+	public int getColumnCount() {
+		return columns.size();
+	}
+
+	@Override
+	public String getColumnName( int column ) {
+		String title = columns.get(column);
+		if (title != null)
+			return title;
+		return tr("Unknown");
+	}
+
+
+	@Override
+	public Object getValueAt(Object node, int column) {
+		SignatureTreeNode signode = ( SignatureTreeNode )node;
+		String title = columns.get(column);
+		if (title != null){
+			if (title.equals(allTitle[0]))
+				return signode.getUID();
+			if (title.equals(allTitle[1]))
+				return signode.getKeyID();
+			if (title.equals(allTitle[2]))
+				return signode.getOsmCertificate();
+			if (title.equals(allTitle[3]))
+				return signode.getSignatureDate();
+		}
+		return tr("Unknown");
+
+	}
+
+	@Override
+	public Object getChild(Object node, int index) {
+		SignatureTreeNode signode = ( SignatureTreeNode )node;
+		return signode.getChildren().get( index );
+	}
+
+	@Override
+	public int getChildCount(Object node) {
+		SignatureTreeNode signode = ( SignatureTreeNode )node;
+		return signode.getChildren().size();
+	}
+
+	@Override
+	public int getIndexOfChild( Object parent, Object child ) {
+		SignatureTreeNode signode = ( SignatureTreeNode )parent;
+		for( int i=0; i>signode.getChildren().size(); i++ ) {
+			if( signode.getChildren().get( i ) == child )
+				return i;
+		}
+		return 0;
+	}
+
+	@Override
+	public Object getRoot() {
+		return root;
+	}
+
+
+	public class SignatureTreeNode {
+		private PGPSignature s;
+		private final List<SignatureTreeNode> children = new ArrayList<SignatureTreeNode>();
+		private final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd:hh.mm.ss");
+		public SignatureTreeNode() {
+		}
+
+		public SignatureTreeNode(PGPSignature s) {
+			this.s = s;
+		}
+
+		public PGPSignature getSignature(){
+			return s;
+		}
+		public String getUID() {
+			return convPGPSignatureToString(s);
+		}
+		public String getKeyID() {
+			return "0x"+Long.toHexString(s.getKeyID()).substring(8).toUpperCase();
+		}
+		public String getOsmCertificate() {
+			String cert = "";
+			for (NotationData nd : s.getHashedSubPackets().getNotationDataOccurences()){
+				if (nd.getNotationName().equals("trustosm@openstreetmap.org")) {
+					cert += nd.getNotationValue();
+				}
+			}
+			return cert;
+		}
+		public String getSignatureDate() {
+			return formatter.format(s.getCreationTime());
+		}
+		public List<SignatureTreeNode> getChildren() {
+			return children;
+		}
+
+	}
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/dialogs/JCollapsiblePanel.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/dialogs/JCollapsiblePanel.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/dialogs/JCollapsiblePanel.java	(revision 24389)
@@ -0,0 +1,65 @@
+package org.openstreetmap.josm.plugins.trustosm.gui.dialogs;
+
+import java.awt.GridBagLayout;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingConstants;
+
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+public class JCollapsiblePanel extends JPanel {
+
+	private boolean expanded;
+	JPanel contentPanel_;
+	HeaderPanel headerPanel_;
+
+	private class HeaderPanel extends JPanel {
+		JLabel title;
+
+		public HeaderPanel(String text) {
+
+			setLayout(new GridBagLayout());
+			title = new JLabel(text,ImageProvider.get("misc", "minimized"),SwingConstants.LEADING);
+			add(title,GBC.eol());
+
+
+			addMouseListener(
+					new MouseAdapter() {
+						@Override
+						public void mouseClicked(MouseEvent e) {
+							expanded = !expanded;
+
+							if (contentPanel_.isShowing()) {
+								contentPanel_.setVisible(false);
+								title.setIcon(ImageProvider.get("misc", "minimized"));
+							}
+							else {
+								contentPanel_.setVisible(true);
+								title.setIcon(ImageProvider.get("misc", "normal"));
+							}
+							validate();
+
+							headerPanel_.repaint();
+						}
+					}
+			);
+
+		}
+
+	}
+
+	public JCollapsiblePanel(String text, JPanel panel) {
+		super(new GridBagLayout());
+		expanded = false;
+		headerPanel_ = new HeaderPanel(text);
+		contentPanel_ = panel;
+		add(headerPanel_, GBC.eol());
+		add(contentPanel_, GBC.eol());
+		contentPanel_.setVisible(false);
+	}
+
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/dialogs/KeySignaturesDialog.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/dialogs/KeySignaturesDialog.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/dialogs/KeySignaturesDialog.java	(revision 24389)
@@ -0,0 +1,147 @@
+package org.openstreetmap.josm.plugins.trustosm.gui.dialogs;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GridBagLayout;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.Iterator;
+
+import javax.swing.BoxLayout;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPSignature;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+public class KeySignaturesDialog extends JPanel {
+
+	protected boolean isCollapsed;
+
+	protected TitleBar titleBar;
+
+	/** the label in the title bar which shows whether the toggle dialog is expanded or collapsed */
+	private JLabel lblMinimized;
+
+
+	public KeySignaturesDialog(PGPPublicKey key) {
+		super(new BorderLayout());
+
+		String userid = "Unknown";
+		Iterator iter = key.getUserIDs();
+		if (iter.hasNext()) {
+			userid = (String)iter.next();
+		}
+
+		isCollapsed = false;
+
+		titleBar = new TitleBar(userid);
+		add(titleBar, BorderLayout.NORTH);
+		add(createKeySigPanel(key));
+
+	}
+
+	public static JPanel createKeySigPanel(PGPPublicKey key) {
+		JPanel p = new JPanel();
+		p.setLayout(new BoxLayout(p, BoxLayout.PAGE_AXIS));
+		Iterator iter = key.getSignatures();
+		while (iter.hasNext()) {
+			PGPSignature sig = (PGPSignature)iter.next();
+			String uid = "0x"+Long.toHexString(sig.getKeyID()).substring(8).toUpperCase();
+			p.add(new JLabel(uid));
+		}
+		return p;
+	}
+
+	/**
+	 * Collapses the toggle dialog to the title bar only
+	 *
+	 */
+	public void collapse() {
+		if (!isCollapsed) {
+			//setContentVisible(false);
+			isCollapsed = true;
+			setPreferredSize(new Dimension(0,20));
+			setMaximumSize(new Dimension(Integer.MAX_VALUE,20));
+			setMinimumSize(new Dimension(Integer.MAX_VALUE,20));
+			lblMinimized.setIcon(ImageProvider.get("misc", "minimized"));
+		}
+		else throw new IllegalStateException();
+	}
+
+	/**
+	 * Expands the toggle dialog
+	 */
+	protected void expand() {
+		if (isCollapsed) {
+			//		setContentVisible(true);
+			isCollapsed = false;
+			setPreferredSize(new Dimension(0,200));
+			setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
+			lblMinimized.setIcon(ImageProvider.get("misc", "normal"));
+		}
+		else throw new IllegalStateException();
+	}
+
+	/**
+	 * Sets the visibility of all components in this toggle dialog, except the title bar
+	 *
+	 * @param visible true, if the components should be visible; false otherwise
+	 */
+	protected void setContentVisible(boolean visible) {
+		Component comps[] = getComponents();
+		for(int i=0; i<comps.length; i++) {
+			if(comps[i] != titleBar) {
+				comps[i].setVisible(visible);
+			}
+		}
+	}
+
+	/**
+	 * The title bar displayed in docked mode
+	 *
+	 */
+	protected class TitleBar extends JPanel {
+		final private JLabel lblTitle;
+
+		public TitleBar(String toggleDialogName) {
+			setLayout(new GridBagLayout());
+			lblMinimized = new JLabel(ImageProvider.get("misc", "minimized"));
+			add(lblMinimized);
+
+			lblTitle = new JLabel(toggleDialogName);
+			add(lblTitle, GBC.std().fill(GBC.HORIZONTAL));
+
+			addMouseListener(
+					new MouseAdapter() {
+						@Override
+						public void mouseClicked(MouseEvent e) {
+							// toggleExpandedState
+							if (isCollapsed) {
+								expand();
+							} else {
+								collapse();
+							}
+						}
+					}
+			);
+
+			setToolTipText(tr("Click to minimize/maximize the panel content"));
+			setTitle(toggleDialogName);
+		}
+
+		public void setTitle(String title) {
+			lblTitle.setText(title);
+		}
+
+		public String getTitle() {
+			return lblTitle.getText();
+		}
+	}
+
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/dialogs/TrustDialog.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/dialogs/TrustDialog.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/dialogs/TrustDialog.java	(revision 24389)
@@ -0,0 +1,466 @@
+package org.openstreetmap.josm.plugins.trustosm.gui.dialogs;
+
+import static org.openstreetmap.josm.tools.I18n.marktr;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+
+import javax.swing.BoxLayout;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTree;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreePath;
+
+import org.openstreetmap.josm.data.SelectionChangedListener;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.DefaultNameFormatter;
+import org.openstreetmap.josm.gui.SideButton;
+import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
+import org.openstreetmap.josm.plugins.trustosm.TrustOSMplugin;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustOSMItem;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustSignatures;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public class TrustDialog extends ToggleDialog implements ActionListener, SelectionChangedListener {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -3324984194315776740L;
+
+
+	public final static Color BGCOLOR_NO_SIG = new Color(234, 234, 234);
+	//	public final static Color BGCOLOR_VALID_SIG = new Color(235,255,177);
+	public final static Color BGCOLOR_VALID_SIG = new Color(74,245,106);
+	public final static Color BGCOLOR_BROKEN_SIG = new Color(255, 197, 197);
+	public final static Color BGCOLOR_REMOVED_ITEM = new Color(255, 100, 100);
+	public final static Color BGCOLOR_UPDATED_ITEM = new Color(249,221,95);
+
+
+	/** Use a TrustGPGPreparer to sign or validate signatures */
+	//private final TrustGPGPreparer gpg;
+
+	/** The check signatures button */
+	private final SideButton checkButton;
+
+	/** The sign button */
+	private final SideButton signButton;
+
+	/** The show sigs button */
+	private final SideButton showButton;
+
+	private final Map<String, Byte> rowStatus = new HashMap<String, Byte>();
+
+	/** The selected osmData */
+	private Collection<? extends OsmPrimitive> osmData;
+
+	private final JTree geomTree;
+
+	/**
+	 * The property data.
+	 */
+	private final DefaultTableModel propertyData = new DefaultTableModel() {
+		/**
+		 * 
+		 */
+		private static final long serialVersionUID = -1252801283184909691L;
+		@Override public boolean isCellEditable(int row, int column) {
+			return false;
+		}
+		@Override public Class<?> getColumnClass(int columnIndex) {
+			return String.class;
+		}
+	};
+	private final JTable propertyTable = new JTable(propertyData) {
+		/**
+		 * 
+		 */
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
+			Component c = super.prepareRenderer(renderer, row, column);
+			Byte stat = rowStatus.get(getModel().getValueAt(row, 0));
+			if (!isRowSelected(row))
+				switch (stat.byteValue()) {
+				case -2: c.setBackground( BGCOLOR_REMOVED_ITEM ); break;
+				case -1: c.setBackground( BGCOLOR_BROKEN_SIG ); break;
+				case 1: c.setBackground( BGCOLOR_VALID_SIG ); break;
+				default: c.setBackground( BGCOLOR_NO_SIG ); break;
+				}
+			return c;
+		}
+	};
+
+	/**
+	 * Constructor
+	 */
+	public TrustDialog() {
+		super(tr("Object signatures"), "trustosm", tr("Open object signing window."),
+				Shortcut.registerShortcut("subwindow:trustosm", tr("Toggle: {0}", tr("Object signatures")),
+						KeyEvent.VK_T, Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150);
+
+
+		// setting up the properties table
+		propertyData.setColumnIdentifiers(new String[]{tr("Key"),tr("Value")});
+
+		// copy and paste from org.openstreetmap.josm.gui.dialogs.properties.PropertiesDialog
+
+		propertyTable.getColumnModel().getColumn(1).setCellRenderer(new DefaultTableCellRenderer(){
+			/**
+			 * 
+			 */
+			private static final long serialVersionUID = 8003207668070727861L;
+
+			@Override
+			public Component getTableCellRendererComponent(JTable table, Object value,
+					boolean isSelected, boolean hasFocus, int row, int column) {
+				Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
+				if (c instanceof JLabel) {
+					String str = null;
+					if (value instanceof String) {
+						str = (String) value;
+					} else if (value instanceof Map<?, ?>) {
+						Map<?, ?> v = (Map<?, ?>) value;
+						if (v.size() != 1) {
+							str=tr("<different>");
+							c.setFont(c.getFont().deriveFont(Font.ITALIC));
+						} else {
+							final Map.Entry<?, ?> entry = v.entrySet().iterator().next();
+							str = (String) entry.getKey();
+						}
+					}
+					((JLabel)c).setText(str);
+				}
+				return c;
+			}
+		});
+
+		geomTree = new JTree( createTree() );
+
+		geomTree.setBackground( BGCOLOR_NO_SIG );
+		geomTree.setRootVisible(false);
+		geomTree.setCellRenderer(new DefaultTreeCellRenderer(){
+
+			/**
+			 * 
+			 */
+			private static final long serialVersionUID = -3070210847060314196L;
+
+			@Override
+			public Component getTreeCellRendererComponent(JTree tree, Object value,
+					boolean selected, boolean expanded, boolean leaf, int row,
+					boolean hasFocus)
+			{
+				super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
+
+				DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
+				if (node.isRoot()) return this;
+				OsmPrimitive osm = (OsmPrimitive) node.getUserObject();
+				setIcon(ImageProvider.get(OsmPrimitiveType.from(osm)));
+				setText(osm.getDisplayName(DefaultNameFormatter.getInstance()));
+
+				if (osm instanceof Node) {
+					Node osmNode = (Node) osm;
+					if (((DefaultMutableTreeNode) node.getParent()).getUserObject() instanceof Way) {
+						osm = (OsmPrimitive) ((DefaultMutableTreeNode) node.getParent()).getUserObject();
+					}
+					TrustSignatures sigs;
+					String id = String.valueOf(osm.getUniqueId());
+					if (TrustOSMplugin.signedItems.containsKey(id) && (sigs = TrustOSMplugin.signedItems.get(id).getSigsOnNode(osmNode)) != null) {
+						byte stat = sigs.getStatus();
+						switch (stat) {
+						case -2: setBackgroundNonSelectionColor( BGCOLOR_REMOVED_ITEM ); break;
+						case -1: setBackgroundNonSelectionColor( BGCOLOR_BROKEN_SIG ); break;
+						case 1: setBackgroundNonSelectionColor( BGCOLOR_VALID_SIG ); break;
+						default: setBackgroundNonSelectionColor( BGCOLOR_NO_SIG ); break;
+						}
+					} else {
+						setBackgroundNonSelectionColor( BGCOLOR_NO_SIG );
+					}
+				} else if (osm instanceof Way) {
+					setBackgroundNonSelectionColor( BGCOLOR_NO_SIG );
+				}
+				return this;
+			}
+
+
+		});
+
+
+
+
+		JPanel dataPanel = new JPanel();
+		dataPanel.setLayout(new BoxLayout(dataPanel, BoxLayout.PAGE_AXIS));
+		propertyTable.setAlignmentX(LEFT_ALIGNMENT);
+		dataPanel.add(propertyTable);
+		geomTree.setAlignmentX(LEFT_ALIGNMENT);
+		dataPanel.add(geomTree);
+
+		add(new JScrollPane(dataPanel), BorderLayout.CENTER);
+
+		JPanel buttonPanel = new JPanel(new GridLayout(1, 3));
+
+		checkButton = new SideButton(marktr("Check"), "checksignatures", "TrustOSM",
+				tr("Check all available signatures for selected object."), this);
+		buttonPanel.add(checkButton);
+
+		signButton = new SideButton(marktr("Sign"), "sign", "TrustOSM",
+				tr("Digital sign selected Tags, if you believe they are correct."), this);
+		buttonPanel.add(signButton);
+
+		showButton = new SideButton(marktr("Show"), "showsig", "TrustOSM",
+				tr("Show all available signatures for selected attribute."), this);
+		buttonPanel.add(showButton);
+
+
+		add(buttonPanel, BorderLayout.SOUTH);
+		DataSet.addSelectionListener(this);
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		String actionCommand = e.getActionCommand();
+		if (actionCommand.equals("Check")) {
+			for (OsmPrimitive osm : osmData) {
+				String id = String.valueOf(osm.getUniqueId());
+				if (TrustOSMplugin.signedItems.containsKey(id))
+					TrustOSMplugin.gpg.checkAll(TrustOSMplugin.signedItems.get(id));
+				//checkedItems.put(osm, TrustOSMplugin.gpg.check(checkedItems.containsKey(osm)? checkedItems.get(osm) : new TrustOSMItem(osm)));
+			}
+			updateTable();
+			geomTree.repaint();
+		} else if (actionCommand.equals("Sign")) {
+			for (int i : propertyTable.getSelectedRows()) {
+				String key = (String)propertyTable.getValueAt(i, 0);
+				for (OsmPrimitive osm : osmData) {
+					if (osm.keySet().contains(key)) {
+						String id = String.valueOf(osm.getUniqueId());
+						TrustOSMplugin.signedItems.put(id, TrustOSMplugin.gpg.signTag(TrustOSMplugin.signedItems.containsKey(id)? TrustOSMplugin.signedItems.get(id) : new TrustOSMItem(osm), key));
+					}
+				}
+			}
+			if (geomTree.getSelectionPaths()!=null)
+				for (TreePath tp : geomTree.getSelectionPaths()) {
+					OsmPrimitive osm = (OsmPrimitive) ((DefaultMutableTreeNode) tp.getLastPathComponent()).getUserObject();
+					String id = String.valueOf(osm.getUniqueId());
+					if (osm instanceof Node) {
+						TreePath parentPath = tp.getParentPath();
+						if (geomTree.isPathSelected(parentPath)) return;
+
+						Node osmNode = ((Node) osm);
+						if (((DefaultMutableTreeNode) parentPath.getLastPathComponent()).getUserObject() instanceof Way) {
+							osm = (OsmPrimitive) ((DefaultMutableTreeNode) parentPath.getLastPathComponent()).getUserObject();
+						}
+						TrustOSMItem trust = TrustOSMplugin.signedItems.containsKey(id)? TrustOSMplugin.signedItems.get(id) : new TrustOSMItem(osm);
+						trust.storeNodeSig(osmNode, TrustOSMplugin.gpg.signNode(osm,osmNode));
+						TrustOSMplugin.signedItems.put(id, trust);
+					} else if (osm instanceof Way) {
+						TrustOSMplugin.signedItems.put(id, TrustOSMplugin.gpg.signGeometry(TrustOSMplugin.signedItems.containsKey(id)? TrustOSMplugin.signedItems.get(id) : new TrustOSMItem(osm)));
+					}
+				}
+			updateTable();
+			geomTree.repaint();
+		} else if (actionCommand.equals("Show")) {
+			for (int i : propertyTable.getSelectedRows()) {
+				String key = (String)propertyTable.getValueAt(i, 0);
+				for (OsmPrimitive osm : osmData) {
+					String id = String.valueOf(osm.getUniqueId());
+					if (osm.keySet().contains(key) && TrustOSMplugin.signedItems.containsKey(id)) {
+						TrustSignaturesDialog.showSignaturesDialog(TrustOSMplugin.signedItems.get(id), key);
+					}
+				}
+			}
+		}
+	}
+	/*
+	public void showSignaturesDialog(TrustOSMItem trust, String key) {
+		TrustSignatures sigs;
+		if ((sigs = trust.getSigsOnKey(key)) == null) {
+			JOptionPane.showMessageDialog(null,tr("Sorry, there are no Signatures for the selected Attribute."), tr("No Signature found"), JOptionPane.WARNING_MESSAGE);
+		} else {
+			JPanel p = new JPanel();
+			p.setLayout(new BoxLayout(p, BoxLayout.PAGE_AXIS));
+			Dimension d = new Dimension(0,20);
+			JLabel head = new JLabel(tr("Selected key value pair was:\n{0}={1}",key,trust.getOsmItem().get(key)));
+			head.setAlignmentX(LEFT_ALIGNMENT);
+			p.add(head);
+			p.add(Box.createRigidArea(d));
+			SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd:hh.mm.ss");
+			for (PGPSignature s : sigs.getSignatures()) {
+				JTextArea sigtext = new JTextArea(sigs.getArmoredFulltextSignature(s));
+				sigtext.setEditable(false);
+				sigtext.setAlignmentX(LEFT_ALIGNMENT);
+				p.add(sigtext);
+				JLabel siginfo = new JLabel(tr("Signature created at {0} by User {1}",formatter.format(s.getCreationTime()),s.getHashedSubPackets().getSignerUserID()));
+				siginfo.setAlignmentX(LEFT_ALIGNMENT);
+				p.add(siginfo);
+				p.add(Box.createRigidArea(d));
+			}
+
+			JScrollPane scroller = new JScrollPane(p);
+			JPanel content = new JPanel();
+			content.setMaximumSize(new Dimension(600,500));
+			content.add(scroller);
+			JOptionPane.showMessageDialog(Main.parent,content, tr("Clearsigned Signature"), JOptionPane.PLAIN_MESSAGE);
+		}
+	}
+	 */
+	private DefaultTreeModel createTree(){
+		DefaultMutableTreeNode root = new DefaultMutableTreeNode();
+		DefaultMutableTreeNode wayNode;
+		if (osmData!=null)
+			for (OsmPrimitive osm : osmData) {
+				String id = String.valueOf(osm.getUniqueId());
+				if(osm instanceof Node) {
+					root.add(new DefaultMutableTreeNode(osm));
+				} else if(osm instanceof Way) {
+					wayNode = new DefaultMutableTreeNode(osm);
+					List<Node> presentNodes = ((Way)osm).getNodes();
+					Iterator<Node> iter = presentNodes.iterator();
+					while (iter.hasNext()) {
+						wayNode.add(new DefaultMutableTreeNode(iter.next()));
+					}
+
+					if (TrustOSMplugin.signedItems.containsKey(id)) {
+						TrustOSMItem trust = TrustOSMplugin.signedItems.get(id);
+						HashSet<Node> signedNodes = new HashSet<Node>(trust.getGeomSigs().keySet());
+						signedNodes.removeAll(presentNodes);
+						iter = signedNodes.iterator();
+						Node removedNode;
+						while (iter.hasNext()) {
+							removedNode = iter.next();
+							trust.updateNodeSigStatus(removedNode, TrustSignatures.ITEM_REMOVED);
+							wayNode.add(new DefaultMutableTreeNode(removedNode));
+						}
+					}
+					root.add(wayNode);
+				} else if(osm instanceof Relation) {
+
+				}
+
+			}
+
+		return new DefaultTreeModel(root);
+
+	}
+
+	public void updateTable() {
+		// re-load property data
+		propertyData.setRowCount(0);
+
+		Map<String, Map<String, Integer>> valueCount = new TreeMap<String, Map<String, Integer>>();
+
+		TrustOSMItem trust;
+
+		valueCount.clear();
+		rowStatus.clear();
+		boolean sigsAvailable = false;
+
+		for (OsmPrimitive osm : osmData) {
+			String id = String.valueOf(osm.getUniqueId());
+			if (TrustOSMplugin.signedItems.containsKey(id)) {
+				trust = TrustOSMplugin.signedItems.get(id);
+				sigsAvailable = true;
+				Map<String,String> tags = osm.getKeys();
+				Map<String, TrustSignatures>  signedTags = trust.getTagSigs();
+				HashSet<String> removedKeys = new HashSet<String>(signedTags.keySet());
+				removedKeys.removeAll(tags.keySet());
+				for (String removedKey: removedKeys) {
+					TrustSignatures sigs = signedTags.get(removedKey);
+					sigs.setStatus( TrustSignatures.ITEM_REMOVED );
+					//tags.putAll(TrustOSMplugin.gpg.getKeyValueFromSignature(sigs.getLatestSignature()));
+				}
+
+			} else {
+				trust = new TrustOSMItem(osm);
+				sigsAvailable = false;
+			}
+
+			//		trust = TrustOSMplugin.signedItems.containsKey(osm) ? TrustOSMplugin.signedItems.get(osm) : new TrustOSMItem(osm);
+
+			for (String key: osm.keySet()) {
+				String value = osm.get(key);
+				//keyCount.put(key, keyCount.containsKey(key) ? keyCount.get(key) + 1 : 1);
+
+				byte status = sigsAvailable && trust.getTagSigs().containsKey(key) ? trust.getTagSigs().get(key).getStatus() : TrustSignatures.SIG_UNKNOWN ;
+				Byte oldstatus = rowStatus.containsKey(key)? rowStatus.get(key) : new Byte(TrustSignatures.SIG_VALID);
+				Byte sigstatus = new Byte(status);
+				Byte newstatus;
+				if (sigstatus.equals(new Byte(TrustSignatures.SIG_BROKEN)) || oldstatus.equals(new Byte(TrustSignatures.SIG_BROKEN))) {
+					newstatus = new Byte(TrustSignatures.SIG_BROKEN);
+				} else if (sigstatus.equals(new Byte(TrustSignatures.SIG_UNKNOWN)) || oldstatus.equals(new Byte(TrustSignatures.SIG_UNKNOWN))) {
+					newstatus = new Byte(TrustSignatures.SIG_UNKNOWN);
+				} else newstatus = new Byte(TrustSignatures.SIG_VALID);
+
+				rowStatus.put(key, newstatus );
+				if (valueCount.containsKey(key)) {
+					Map<String, Integer> v = valueCount.get(key);
+					v.put(value, v.containsKey(value)? v.get(value) + 1 : 1 );
+				} else {
+					TreeMap<String,Integer> v = new TreeMap<String, Integer>();
+					v.put(value, 1);
+					valueCount.put(key, v);
+				}
+			}
+		}
+		for (Entry<String, Map<String, Integer>> e : valueCount.entrySet()) {
+			int count=0;
+			for (Entry<String, Integer> e1: e.getValue().entrySet()) {
+				count+=e1.getValue();
+			}
+			if (count < osmData.size()) {
+				e.getValue().put("", osmData.size()-count);
+			}
+			propertyData.addRow(new Object[]{e.getKey(), e.getValue()});
+		}
+
+
+		boolean hasSelection = !osmData.isEmpty();
+		boolean hasTags = hasSelection && propertyData.getRowCount() > 0;
+
+		propertyTable.setVisible(hasTags);
+		propertyTable.getTableHeader().setVisible(hasTags);
+	}
+
+	@Override
+	public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
+		this.osmData = newSelection;
+
+		if (!isVisible())
+			return;
+		geomTree.setModel(createTree());
+		updateTable();
+		//		signButton.setEnabled(newSelection.size() == 1);
+	}
+
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/dialogs/TrustSignaturesDialog.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/dialogs/TrustSignaturesDialog.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/dialogs/TrustSignaturesDialog.java	(revision 24389)
@@ -0,0 +1,236 @@
+package org.openstreetmap.josm.plugins.trustosm.gui.dialogs;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.Box;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreePath;
+
+import org.bouncycastle.bcpg.SignatureSubpacketTags;
+import org.bouncycastle.bcpg.sig.NotationData;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPSignature;
+import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
+import org.jdesktop.swingx.JXTreeTable;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.plugins.trustosm.TrustOSMplugin;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustOSMItem;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustSignatures;
+import org.openstreetmap.josm.plugins.trustosm.gui.KeyTreeTableModel;
+import org.openstreetmap.josm.plugins.trustosm.gui.KeyTreeTableModel.SignatureTreeNode;
+import org.openstreetmap.josm.plugins.trustosm.util.TrustGPG;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+
+
+public class TrustSignaturesDialog {
+
+	public static void showSignaturesDialog(TrustOSMItem trust, String key) {
+		TrustSignatures sigs;
+		if ((sigs = trust.getSigsOnKey(key)) == null) {
+			JOptionPane.showMessageDialog(null,tr("Sorry, there are no Signatures for the selected Attribute."), tr("No Signature found"), JOptionPane.WARNING_MESSAGE);
+		} else {
+			JPanel p = new JPanel(new GridBagLayout());
+			p.add(new JLabel(tr("Selected key value pair was:\n{0}={1}",key,trust.getOsmItem().get(key))),GBC.eol());
+
+			SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd:hh.mm.ss");
+
+			for (String plain : sigs.getAllPlainTexts()) {
+				JTextArea sigtext = new JTextArea(sigs.getArmoredFulltextSignatureAll(plain));
+				sigtext.setEditable(false);
+				JPanel textcontent = new JPanel();
+				textcontent.add(sigtext);
+				String[] kv = TrustGPG.generateTagsFromSigtext(plain);
+				p.add(new JCollapsiblePanel(tr("Signed key value pair was: {0}={1}", kv[0],kv[1]),textcontent),GBC.eol());
+
+				List<PGPSignature> siglist = sigs.getSignaturesByPlaintext(plain);
+				JPanel signerPanel = new JPanel(new GridBagLayout());
+				//signerPanel.add(createSignerTree(siglist));
+				KeyTreeTableModel km = new KeyTreeTableModel(siglist);
+				final JXTreeTable t = new JXTreeTable( km );
+				//t.setHorizontalScrollEnabled(true);
+				//t.setRootVisible(false);
+				t.addMouseListener(new MouseAdapter() {
+					@Override
+					public void mouseClicked(MouseEvent e) {
+						if (e.getClickCount() == 2) {
+							TreePath selPath = t.getPathForLocation(e.getX(), e.getY());
+							if (selPath == null)
+								return;
+							SignatureTreeNode sn = (SignatureTreeNode)selPath.getLastPathComponent();
+							PGPPublicKey pub = TrustOSMplugin.gpg.getPublicKeyFromRing(sn.getSignature().getKeyID());
+							TrustGPG.showKeyDetails(pub);
+						}
+					}
+				});
+				t.setLeafIcon(ImageProvider.get("dialogs/sign"));
+				t.setOpenIcon(ImageProvider.get("dialogs/sign_color"));
+				t.setClosedIcon(ImageProvider.get("dialogs/sign_color"));
+				t.expandAll();
+				t.packAll();
+				t.collapseAll();
+				signerPanel.add(new JScrollPane(t));
+
+
+				//			JTreeTable tt = new JTreeTable();
+
+				/*				for (PGPSignature s : siglist) {
+					signerPanel.add(createKeyButton(tr("Signature created at {0} by User {1}",formatter.format(s.getCreationTime()),s.getHashedSubPackets().getSignerUserID()),s.getKeyID()),GBC.eol());
+					//signerPanel.add(new JLabel(tr("Signature created at {0} by User {1}",formatter.format(s.getCreationTime()),s.getHashedSubPackets().getSignerUserID())),GBC.eol());
+				}*/
+
+				p.add(new JCollapsiblePanel(tr("{0} Signatures found.", siglist.size()),signerPanel),GBC.eol().insets(20,0,0,0));
+			}
+
+			/*
+
+			for (PGPSignature s : sigs.getSignatures()) {
+				JTextArea sigtext = new JTextArea(sigs.getArmoredFulltextSignature(s));
+				sigtext.setEditable(false);
+				sigtext.setAlignmentX(Component.LEFT_ALIGNMENT);
+				p.add(sigtext);
+				JLabel siginfo = new JLabel(tr("Signature created at {0} by User {1}",formatter.format(s.getCreationTime()),s.getHashedSubPackets().getSignerUserID()));
+				siginfo.setAlignmentX(Component.LEFT_ALIGNMENT);
+				p.add(siginfo);
+				p.add(Box.createRigidArea(d));
+			}
+			 */
+
+
+			p.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH));
+			JScrollPane scroller = new JScrollPane(p);
+			//JPanel content = new JPanel();
+			scroller.setPreferredSize(new Dimension(700,500));
+			//content.add(scroller);
+			//JOptionPane.showMessageDialog(Main.parent,scroller, tr("Clearsigned Signature"), JOptionPane.PLAIN_MESSAGE);
+			String[] buttons = {tr("Ok")};
+			ExtendedDialog info = new ExtendedDialog(Main.parent, tr("Signature Info"),buttons,false);
+			info.setContent(scroller,false);
+			info.showDialog();
+			//info.setBounds(200, 200, 300, 200);
+			//info.setVisible(true);
+		}
+	}
+
+	public static JButton createKeyButton(String label,final long keyID) {
+		JButton detailsButton = new JButton(label);
+		detailsButton.addActionListener(new ActionListener() {
+
+			public void actionPerformed(ActionEvent e)
+			{
+				showKeySignaturesDialog(keyID);
+			}
+		});
+		return detailsButton;
+	}
+
+	public static void showKeySignaturesDialog(long keyID) {
+		PGPPublicKey pub = TrustOSMplugin.gpg.getPublicKeyFromRing(keyID);
+		JPanel p = new JPanel(new GridBagLayout());
+
+		Iterator sigIt = pub.getSignatures();
+		while (sigIt.hasNext()) {
+			PGPSignature s = (PGPSignature)sigIt.next();
+			p.add(createKeyButton(s.getHashedSubPackets().getSignerUserID(),pub.getKeyID()));
+		}
+
+		String uid = String.valueOf(pub.getKeyID());
+		Iterator i = pub.getUserIDs();
+		if (i.hasNext())
+			uid = (String)i.next();
+
+		p.add(createKeyButton(uid,pub.getKeyID()));
+		JOptionPane.showMessageDialog(Main.parent,p, tr("Key Signatures"), JOptionPane.PLAIN_MESSAGE);
+	}
+
+
+	public static String convPGPSignatureToString(PGPSignature s) {
+		PGPSignatureSubpacketVector sv = s.getHashedSubPackets();
+		if (sv.hasSubpacket(SignatureSubpacketTags.SIGNER_USER_ID))
+			return sv.getSignerUserID();
+
+		PGPPublicKey pub = TrustOSMplugin.gpg.getPublicKeyFromRing(s.getKeyID());
+		if (pub != null){
+			Iterator i = pub.getUserIDs();
+			if (i.hasNext())
+				return (String)i.next();
+		}
+		return tr("unknown");
+	}
+
+	public static JTree createSignerTree(Collection<PGPSignature> sigs) {
+		DefaultMutableTreeNode root = new DefaultMutableTreeNode();
+		for (PGPSignature s : sigs){
+			DefaultMutableTreeNode sn = new DefaultMutableTreeNode(s);
+			PGPPublicKey pub = TrustOSMplugin.gpg.getPublicKeyFromRing(s.getKeyID());
+			Iterator iter = pub.getSignatures();
+			while (iter.hasNext()){
+				PGPSignature ks = (PGPSignature)iter.next();
+				sn.add(new DefaultMutableTreeNode(ks));
+			}
+			root.add(sn);
+		}
+
+
+		JTree t = new JTree(new DefaultTreeModel(root));
+		t.setRootVisible(false);
+		t.setBackground(TrustDialog.BGCOLOR_NO_SIG);
+		t.setCellRenderer(new DefaultTreeCellRenderer(){
+
+			@Override
+			public Component getTreeCellRendererComponent(JTree tree, Object value,
+					boolean selected, boolean expanded, boolean leaf, int row,
+					boolean hasFocus)
+			{
+				super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
+
+				DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
+				if (node.isRoot()) return this;
+				PGPSignature s = (PGPSignature) node.getUserObject();
+				setIcon(ImageProvider.get("dialogs/sign"));
+				setText(convPGPSignatureToString(s));
+				PGPSignatureSubpacketVector sv = s.getHashedSubPackets();
+				setBackgroundNonSelectionColor(TrustDialog.BGCOLOR_NO_SIG);
+				if (sv.hasSubpacket(SignatureSubpacketTags.NOTATION_DATA)) {
+					for (NotationData nd : sv.getNotationDataOccurences()){
+						System.out.println(nd.getNotationName()+"="+nd.getNotationValue());
+						if (nd.getNotationName().equals("trustosm@openstreetmap.org")) {
+							setBackgroundNonSelectionColor(TrustDialog.BGCOLOR_VALID_SIG);
+							setToolTipText(tr("Key certified for OSM-Usage with comment:{0}",nd.getNotationValue()));
+						}
+					}
+				}
+				return this;
+			}
+
+
+		});
+		return t;
+	}
+
+
+
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/preferences/TrustPreferenceEditor.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/preferences/TrustPreferenceEditor.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/gui/preferences/TrustPreferenceEditor.java	(revision 24389)
@@ -0,0 +1,79 @@
+package org.openstreetmap.josm.plugins.trustosm.gui.preferences;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.ButtonGroup;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextField;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
+import org.openstreetmap.josm.plugins.trustosm.TrustOSMplugin;
+import org.openstreetmap.josm.tools.GBC;
+
+public class TrustPreferenceEditor implements PreferenceSetting {
+
+	private final JTextField gpgcommand = new JTextField();
+	private final JRadioButton defaultHomedir = new JRadioButton(tr("Use default (and maybe existing) GnuPG directory ({0}) to store new keys and configs.", "~/.gnupg"));
+	private final JRadioButton separateHomedir = new JRadioButton(tr("Use separate GnuPG directory ({0}) to store new keys and configs.", TrustOSMplugin.getGpgPath()));
+
+	@Override
+	public void addGui(final PreferenceTabbedPane gui) {
+		// TODO Auto-generated method stub
+		JPanel p = gui.createPreferenceTab("trustosm", tr("Trust OSM Settings"), tr("Change GPG and privacy settings of the trustosm plugin."));
+		JTabbedPane tabs = new JTabbedPane();
+		p.add(tabs, GBC.eol().fill(GBC.BOTH));
+
+
+		JPanel gpgsettings = new JPanel(new GridBagLayout());
+
+		gpgsettings.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+		gpgsettings.add(new JLabel(tr("GnuPG command to execute:")), GBC.eol().insets(20,0,0,0));
+
+		gpgcommand.setText(Main.pref.get("trustosm.gpg"));
+		gpgsettings.add(gpgcommand, GBC.eol().fill(GBC.HORIZONTAL).insets(20,0,0,5));
+
+		gpgsettings.add(new JLabel(tr("Use separate GnuPG configuration directory?")), GBC.eol().insets(20,0,0,0));
+		ButtonGroup gpgdirGroup = new ButtonGroup();
+		gpgdirGroup.add(separateHomedir);
+		gpgdirGroup.add(defaultHomedir);
+
+		if (Main.pref.getBoolean("trustosm.gpg.separateHomedir")) {
+			separateHomedir.setSelected(true);
+		} else
+			defaultHomedir.setSelected(true);
+
+		gpgsettings.add(separateHomedir, GBC.eol().insets(40,0,0,0));
+		gpgsettings.add(defaultHomedir, GBC.eol().insets(40,0,0,0));
+
+
+		gpgsettings.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH));
+		JScrollPane scrollpane = new JScrollPane(gpgsettings);
+		scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
+		tabs.add(tr("GnuPG"), scrollpane);
+
+
+		JPanel privacysettings = new JPanel(new GridBagLayout());
+		tabs.add(tr("Privacy"), privacysettings);
+
+	}
+
+	@Override
+	public boolean ok() {
+		Main.pref.put("trustosm.gpg", gpgcommand.getText());
+		Main.pref.put("trustosm.gpg.separateHomedir", separateHomedir.isSelected());
+		//		TrustOSMplugin.gpg.refreshGpg();
+		return false;
+	}
+
+
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/io/SigExporter.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/io/SigExporter.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/io/SigExporter.java	(revision 24389)
@@ -0,0 +1,50 @@
+package org.openstreetmap.josm.plugins.trustosm.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.io.FileExporter;
+import org.openstreetmap.josm.plugins.trustosm.TrustOSMplugin;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+
+public class SigExporter extends FileExporter {
+
+	public SigExporter(ExtensionFileFilter filter) {
+		super(filter);
+		// TODO Auto-generated constructor stub
+	}
+
+	public SigExporter() {
+		super(new ExtensionFileFilter("toxm,xml", "tosm", tr("Signature Files") + " (*.tosm *.xml)"));
+	}
+
+	@Override
+	public void exportData(File file, Layer layer) throws IOException {
+		CheckParameterUtil.ensureParameterNotNull(file, "file");
+
+		String fn = file.getPath();
+		if (fn.indexOf('.') == -1) {
+			fn += ".tosm";
+			file = new File(fn);
+		}
+		try {
+			FileOutputStream fo = new FileOutputStream(file);
+			new SigWriter(fo).write(TrustOSMplugin.signedItems.values());
+			fo.flush();
+			fo.close();
+		} catch (IOException x) {
+			x.printStackTrace();
+			JOptionPane.showMessageDialog(Main.parent, tr("Error while exporting {0}:\n{1}", fn, x.getMessage()),
+					tr("Error"), JOptionPane.ERROR_MESSAGE);
+		}
+	}
+
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/io/SigImporter.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/io/SigImporter.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/io/SigImporter.java	(revision 24389)
@@ -0,0 +1,71 @@
+package org.openstreetmap.josm.plugins.trustosm.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.ExtensionFileFilter;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.FileImporter;
+import org.openstreetmap.josm.io.IllegalDataException;
+import org.openstreetmap.josm.plugins.trustosm.TrustOSMplugin;
+import org.openstreetmap.josm.plugins.trustosm.actions.GetMissingDataAction;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustOSMItem;
+
+public class SigImporter extends FileImporter {
+
+	public SigImporter() {
+		super(new ExtensionFileFilter("tosm,xml", "tosm", tr("OSM Signature Files") + " (*.tosm *.xml)"));
+	}
+
+	public SigImporter(ExtensionFileFilter filter) {
+		super(filter);
+	}
+
+	@Override public void importData(File file, ProgressMonitor progressMonitor) throws IOException, IllegalDataException {
+		try {
+			FileInputStream in = new FileInputStream(file);
+			importData(in, file);
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+			throw new IOException(tr("File ''{0}'' does not exist.", file.getName()));
+		}
+	}
+
+	protected void importData(InputStream in, File associatedFile) throws IllegalDataException {
+		if (!Main.main.hasEditLayer()) {
+			DataSet dataSet = new DataSet();
+			final OsmDataLayer layer = new OsmDataLayer(dataSet, associatedFile.getName(), associatedFile);
+			Main.main.addLayer(layer);
+		}
+		//		Set<OsmPrimitive> missingData = new HashSet<OsmPrimitive>();
+		Map<String,TrustOSMItem> trustitems = SigReader.parseSignatureXML(in, NullProgressMonitor.INSTANCE);
+
+		/*
+		int missingCount = missingData.size();
+		int itemCount = trustitems.size();
+		if (missingCount == 0) {
+			JOptionPane.showMessageDialog(Main.parent, tr("{0} Signatures loaded. All referenced OSM objects found.",itemCount));
+		} else {
+			int n = JOptionPane.showOptionDialog(Main.parent, tr("{0} of {1} OSM objects are referenced but not there.\nDo you want to load them from OSM-Server?",missingCount,itemCount), tr("Load objects from server"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null);
+
+			if (n == JOptionPane.YES_OPTION) {
+				Main.worker.submit(new DownloadSignedOsmDataTask(missingData, Main.main.getEditLayer()));
+			}
+		}
+		 */
+		TrustOSMplugin.signedItems.putAll(trustitems);
+		new GetMissingDataAction().downloadMissing();
+		//TrustOSMplugin.signedItems.putAll(TrustStoreHandler.loadSigsFromFile(in));
+
+	}
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/io/SigReader.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/io/SigReader.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/io/SigReader.java	(revision 24389)
@@ -0,0 +1,244 @@
+package org.openstreetmap.josm.plugins.trustosm.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.bouncycastle.bcpg.ArmoredInputStream;
+import org.bouncycastle.openpgp.PGPObjectFactory;
+import org.bouncycastle.openpgp.PGPSignatureList;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.IllegalDataException;
+import org.openstreetmap.josm.io.OsmDataParsingException;
+import org.openstreetmap.josm.io.UTFInputStreamReader;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustOSMItem;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustSignatures;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class SigReader {
+
+	private final Map<String,TrustOSMItem> trustitems = new HashMap<String,TrustOSMItem>();
+	private final Set<OsmPrimitive> missingData = new HashSet<OsmPrimitive>();
+
+	public Map<String,TrustOSMItem> getTrustItems() {
+		return trustitems;
+	}
+
+	public Set<OsmPrimitive> getMissingData() {
+		return missingData;
+	}
+
+	private class Parser extends DefaultHandler {
+		private Locator locator;
+
+		@Override
+		public void setDocumentLocator(Locator locator) {
+			this.locator = locator;
+		}
+
+		protected void throwException(String msg) throws OsmDataParsingException{
+			throw new OsmDataParsingException(msg).rememberLocation(locator);
+		}
+
+		/**
+		 * The current TrustOSMItem to be read.
+		 */
+		private TrustOSMItem trust;
+
+		/**
+		 * Signatures of geometry and tags.
+		 */
+		private final Map<String, TrustSignatures> keySig = new HashMap<String, TrustSignatures>();
+		private final Map<String, TrustSignatures> nodeSig = new HashMap<String, TrustSignatures>();
+		/**
+		 * The current Signatures.
+		 */
+		private TrustSignatures tsigs;
+
+
+		private StringBuffer tmpbuf = new StringBuffer();
+
+
+		@Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
+
+			try {
+				if (qName.equals("trustitem")) {
+					if (atts == null) {
+						throwException(tr("Missing mandatory attribute ''{0}'' of XML element {1}.", "osmid", "trustitem"));
+					}
+
+					String osmid = atts.getValue("osmid");
+					if (osmid == null){
+						throwException(tr("Missing mandatory attribute ''{0}''.", "osmid"));
+					} else if (!osmid.matches("\\d+")) {
+						throwException(tr("Only digits allowed in osmid: ''{0}''.", osmid));
+					}
+					long uid = Long.parseLong(osmid);
+
+					String osmtype = atts.getValue("type");
+					if (osmtype == null){
+						throwException(tr("Missing mandatory attribute ''{0}''.", "type"));
+					}
+					OsmPrimitiveType t = OsmPrimitiveType.fromApiTypeName(osmtype);
+
+					// search corresponding OsmPrimitive
+					OsmPrimitive osm = Main.main.getCurrentDataSet().getPrimitiveById(uid, t);
+					if (osm == null) {
+						switch (t) {
+						case NODE: osm = new Node(uid); break;
+						case WAY: osm = new Way(uid); break;
+						case RELATION: osm = new Relation(uid); break;
+						}
+						missingData.add(osm);
+					}
+					trust = new TrustOSMItem(osm);
+				} else if (qName.equals("key")) {
+					if (atts == null) {
+						throwException(tr("Missing mandatory attribute ''{0}'' of XML element {1}.", "k", "key"));
+					}
+					String key = atts.getValue("k");
+					if (key == null || key.equals("")){
+						throwException(tr("Missing mandatory attribute ''{0}''.", "k"));
+					}
+					tsigs = new TrustSignatures();
+					keySig.put(key, tsigs);
+				} else if (qName.equals("node")) {
+					if (atts == null) {
+						throwException(tr("Missing mandatory attribute ''{0}'' of XML element {1}.", "id", "node"));
+					}
+					String key = atts.getValue("id");
+					if (key == null || key.equals("")){
+						throwException(tr("Missing mandatory attribute ''{0}''.", "id"));
+					}
+					tsigs = new TrustSignatures();
+					nodeSig.put(key, tsigs);
+				} else if (qName.equals("openpgp")) {
+					tmpbuf = new StringBuffer();
+				}
+			} catch (Exception e) {
+				throw new SAXParseException(e.getMessage(), locator, e);
+			}
+		}
+
+		@Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
+			if (qName.equals("trustitem")) {
+				trust.storeAllTagSigs(keySig);
+				//trust.storeAllNodeSigs;
+				trustitems.put(String.valueOf(trust.getOsmItem().getUniqueId()), trust);
+			} else if (qName.equals("openpgp")) {
+				// System.out.println(tmpbuf.toString());
+				try {
+					parseOpenPGP(tmpbuf.toString());
+				} catch (IOException e) {
+					throw new OsmDataParsingException(tr("Could not parse OpenPGP message."),e).rememberLocation(locator);
+				}
+			}
+		}
+
+		@Override public void characters(char[] ch, int start, int length) {
+			tmpbuf.append(ch, start, length);
+		}
+
+		public void parseOpenPGP(String clearsigned) throws IOException {
+			System.out.println("Clearsignedtext vorher:\n"+clearsigned);
+
+			// handle different newline characters and match them all to \n
+			//clearsigned = clearsigned.replace('\r', '\n').replaceAll("\n\n", "\n");
+
+			String plain = "";
+
+			ArmoredInputStream aIn = new ArmoredInputStream(new ByteArrayInputStream(clearsigned.getBytes(Charset.forName("UTF-8"))));
+			PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
+			// read plain text
+			ByteArrayOutputStream bout = new ByteArrayOutputStream();
+			if (aIn.isClearText()) {
+				int ch = aIn.read();
+				do {
+					bout.write(ch);
+					ch = aIn.read();
+				} while (aIn.isClearText());
+			}
+			plain = bout.toString();
+
+			// remove the last \n because it is not part of the plaintext
+			plain = plain.substring(0, plain.length()-1);
+
+			System.out.println("Plaintext:\n"+plain+"--------- hier is zu ende");
+			PGPSignatureList siglist = (PGPSignatureList)pgpFact.nextObject();
+			for (int i=0; i<siglist.size();i++) {
+				tsigs.addSignature(siglist.get(i), plain);
+			}
+
+
+		}
+
+
+	}
+
+	/**
+	 * Parse the given input source and return the TrustosmItems.
+	 *
+	 * @param source the source input stream. Must not be null.
+	 * @param progressMonitor  the progress monitor. If null, {@see NullProgressMonitor#INSTANCE} is assumed
+	 * @param missingData	every OsmPrimitive that is not present in the current Datalayer while parsing is stored in that set
+	 *
+	 * @return a map of the parsed OSM Signatures (TrustOSMItem) with their related OSM-ID as key
+	 * @throws IllegalDataException thrown if the an error was found while parsing the data from the source
+	 * @throws IllegalArgumentException thrown if source is null
+	 */
+	public static Map<String,TrustOSMItem> parseSignatureXML(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException {
+		if (progressMonitor == null) {
+			progressMonitor = NullProgressMonitor.INSTANCE;
+		}
+		CheckParameterUtil.ensureParameterNotNull(source, "source");
+		SigReader reader = new SigReader();
+		try {
+			progressMonitor.beginTask(tr("Prepare stuff...", 2));
+			progressMonitor.indeterminateSubTask(tr("Parsing Signature data..."));
+
+			InputSource inputSource = new InputSource(UTFInputStreamReader.create(source, "UTF-8"));
+			SAXParserFactory.newInstance().newSAXParser().parse(inputSource, reader.new Parser());
+			//			if (missingData != null)
+			//				missingData.addAll(reader.getMissingData());
+			progressMonitor.worked(1);
+
+			return reader.getTrustItems();
+		} catch(ParserConfigurationException e) {
+			throw new IllegalDataException(e.getMessage(), e);
+		} catch (SAXParseException e) {
+			throw new IllegalDataException(tr("Line {0} column {1}: ", e.getLineNumber(), e.getColumnNumber()) + e.getMessage(), e);
+		} catch(SAXException e) {
+			throw new IllegalDataException(e.getMessage(), e);
+		} catch(Exception e) {
+			throw new IllegalDataException(e);
+		} finally {
+			progressMonitor.finishTask();
+		}
+	}
+
+
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/io/SigWriter.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/io/SigWriter.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/io/SigWriter.java	(revision 24389)
@@ -0,0 +1,150 @@
+package org.openstreetmap.josm.plugins.trustosm.io;
+
+import java.io.BufferedWriter;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Collection;
+import java.util.Map;
+
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.io.XmlWriter;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustOSMItem;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustSignatures;
+
+public class SigWriter extends XmlWriter {
+
+	private String indent = "";
+
+	public SigWriter(PrintWriter out) {
+		super(out);
+	}
+
+	public SigWriter(OutputStream out) throws UnsupportedEncodingException {
+		super(new PrintWriter(new BufferedWriter(new OutputStreamWriter(out, "UTF-8"))));
+	}
+
+	public void write(Collection<TrustOSMItem> items) {
+		writeHeader();
+		indent = "  ";
+		writeItems(items);
+		writeFooter();
+		out.flush();
+	}
+
+	private void writeDTD() {
+		out.println("<!DOCTYPE trustXML [");
+		out.println("  <!ELEMENT trustcollection (trustitem)*>");
+		out.println("  <!ATTLIST trustcollection version CDATA #IMPLIED creator CDATA #IMPLIED >");
+		out.println("  <!ELEMENT trustitem (signatures)*>");
+		out.println("  <!ATTLIST trustitem osmid CDATA #REQUIRED type CDATA #REQUIRED >");
+		out.println("  <!ELEMENT signatures (tags|geometry)*>");
+		out.println("  <!ELEMENT tags (key)*>");
+		out.println("  <!ELEMENT key (openpgp)*>");
+		out.println("  <!ATTLIST key k CDATA #REQUIRED >");
+		out.println("  <!ELEMENT geometry (node)*>");
+		out.println("  <!ELEMENT node (openpgp)*>");
+		out.println("  <!ATTLIST node id CDATA #REQUIRED >");
+		out.println("  <!ELEMENT openpgp (#PCDATA)*>");
+		out.println("]>");
+	}
+
+	private void writeHeader() {
+		out.println("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
+		writeDTD();
+		out.println("<trustcollection version=\"0.1\" creator=\"JOSM Signature export\">");
+	}
+
+	private void writeFooter() {
+		out.println("</trustcollection>");
+	}
+
+	private void writeSigs(TrustSignatures tsigs) {
+		for (String plain : tsigs.getAllPlainTexts()) {
+			simpleTag("openpgp",tsigs.getArmoredFulltextSignatureAll(plain));
+		}
+
+	}
+
+	private void writeItems(Collection<TrustOSMItem> items) {
+		Map<String, TrustSignatures> tagsigs;
+		Map<Node, TrustSignatures> nodesigs;
+
+		for (TrustOSMItem item : items){
+			OsmPrimitive osm = item.getOsmItem();
+
+			openAtt("trustitem", "osmid=\""+String.valueOf(osm.getUniqueId())+"\" type=\""+osm.getType().getAPIName()+"\"");
+			openln("signatures");
+
+			tagsigs = item.getTagSigs();
+			openln("tags");
+			for (String key : tagsigs.keySet()) {
+				openAtt("key","k=\""+key+"\"");
+
+				writeSigs(tagsigs.get(key));
+
+				closeln("key");
+			}
+			closeln("tags");
+
+			nodesigs = item.getGeomSigs();
+			openln("geometry");
+			for (Node node : nodesigs.keySet()) {
+				openAtt("node","id=\""+String.valueOf(node.getUniqueId())+"\"");
+
+				writeSigs(nodesigs.get(node));
+
+				closeln("node");
+			}
+			closeln("geometry");
+			closeln("signatures");
+			closeln("trustitem");
+		}
+	}
+
+	private void openln(String tag) {
+		open(tag);
+		out.println();
+	}
+
+	private void open(String tag) {
+		out.print(indent + "<" + tag + ">");
+		indent += "  ";
+	}
+
+	private void openAtt(String tag, String attributes) {
+		out.println(indent + "<" + tag + " " + attributes + ">");
+		indent += "  ";
+	}
+
+	private void inline(String tag, String attributes) {
+		out.println(indent + "<" + tag + " " + attributes + " />");
+	}
+
+	private void close(String tag) {
+		indent = indent.substring(2);
+		out.print(indent + "</" + tag + ">");
+	}
+
+	private void closeln(String tag) {
+		close(tag);
+		out.println();
+	}
+
+	/**
+	 * if content not null, open tag, write encoded content, and close tag
+	 * else do nothing.
+	 */
+	private void simpleTag(String tag, String content) {
+		if (content != null && content.length() > 0) {
+			open(tag);
+			out.print(encode(content));
+			//out.print(content);
+			out.println("</" + tag + ">");
+			indent = indent.substring(2);
+		}
+	}
+
+}
Index: /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/util/TrustGPG.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/util/TrustGPG.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/org/openstreetmap/josm/plugins/trustosm/util/TrustGPG.java	(revision 24389)
@@ -0,0 +1,745 @@
+package org.openstreetmap.josm.plugins.trustosm.util;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Random;
+import java.util.Vector;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JTextField;
+import javax.swing.SpringLayout;
+
+import org.bouncycastle.bcpg.ArmoredOutputStream;
+import org.bouncycastle.bcpg.BCPGOutputStream;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.PGPEncryptedData;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPKeyPair;
+import org.bouncycastle.openpgp.PGPKeyRingGenerator;
+import org.bouncycastle.openpgp.PGPPrivateKey;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyRing;
+import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
+import org.bouncycastle.openpgp.PGPSecretKey;
+import org.bouncycastle.openpgp.PGPSecretKeyRing;
+import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
+import org.bouncycastle.openpgp.PGPSignature;
+import org.bouncycastle.openpgp.PGPSignatureGenerator;
+import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
+import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
+import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.util.encoders.Hex;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.CoordinateFormat;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustOSMItem;
+import org.openstreetmap.josm.plugins.trustosm.data.TrustSignatures;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+import tools.NameGenerator;
+import tools.SpringUtilities;
+
+import com.toedter.calendar.JDateChooser;
+import com.toedter.calendar.JSpinnerDateEditor;
+
+public class TrustGPG {
+
+	//	private GnuPG gpg;
+	private char[] password;
+	private PGPSecretKeyRingCollection pgpSec;
+	private PGPPublicKeyRingCollection pgpPub;
+	private static int digest = PGPUtil.SHA1;
+	private PGPSecretKey pgpSecKey;
+	public boolean keepkey = true;
+
+	public TrustGPG() {
+		Security.addProvider(new BouncyCastleProvider());
+		try {
+			readGpgFiles();
+		} catch (Exception e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+	public PGPPublicKey getPublicKeyFromRing(long keyID) {
+		try {
+			if (pgpPub.contains(keyID))
+				return pgpPub.getPublicKey(keyID);
+			else if (pgpSec.contains(keyID))
+				return pgpSec.getSecretKey(keyID).getPublicKey();
+		} catch (PGPException e) {
+			System.err.println("Could not read a PGPPublic key from your KeyRingCollectionFile. Stacktrace:");
+			e.printStackTrace();
+		}
+		return null;
+	}
+
+	public static String secKeytoString(PGPSecretKey k) {
+		String keyText = "0x"+Long.toHexString(k.getKeyID()).substring(8).toUpperCase() + " ";
+		//			keyText = new String(Hex.encode(sigKey.getPublicKey().getFingerprint()),"UTF-8") + " ";
+		Iterator iter = k.getUserIDs();
+		if (iter.hasNext()) {
+			keyText += (String)iter.next();
+		}
+		/*			iter = sigKey.getUserIDs();
+		while (iter.hasNext()) {
+			keyText += (String)iter.next() + "; ";
+		}
+		 */
+		return keyText.trim();
+	}
+
+	private void readSecretKey() {
+
+		//
+		// we just loop through the collection till we find a key suitable for encryption, in the real
+		// world you would probably want to be a bit smarter about this.
+		//
+		if (keepkey && pgpSecKey != null) return;
+
+		final ArrayList<PGPSecretKey> sigKeys = new ArrayList<PGPSecretKey>();
+
+		//
+		// iterate through the key rings.
+		//
+		Iterator rIt = pgpSec.getKeyRings();
+
+		while (rIt.hasNext()) {
+
+			PGPSecretKeyRing    kRing = (PGPSecretKeyRing)rIt.next();
+			Iterator            kIt = kRing.getSecretKeys();
+
+			while (kIt.hasNext()) {
+				PGPSecretKey    k = (PGPSecretKey)kIt.next();
+
+				if (k.isSigningKey()) {
+					sigKeys.add(k);
+				}
+			}
+		}
+
+
+		Iterator<PGPSecretKey> skIt = sigKeys.iterator();
+
+		final Vector<String> keys = new Vector<String>();
+
+		while (skIt.hasNext()) {
+			PGPSecretKey sigKey = skIt.next();
+			keys.add(secKeytoString(sigKey));
+		}
+
+		JPanel p = new JPanel();
+		p.setLayout(new BoxLayout(p, BoxLayout.PAGE_AXIS));
+		Dimension d = new Dimension(0,20);
+
+		JLabel head = new JLabel(tr("Select a signing key from your keyring-file:"));
+		head.setAlignmentX(Component.LEFT_ALIGNMENT);
+		p.add(head);
+
+		final JComboBox keyBox = new JComboBox(keys);
+		keyBox.setAlignmentX(Component.LEFT_ALIGNMENT);
+		p.add(keyBox);
+
+		JCheckBox keepkeyBox = new JCheckBox(tr("Don't ask again for the key"));
+		keepkeyBox.setAlignmentX(Component.LEFT_ALIGNMENT);
+		p.add(keepkeyBox);
+
+		JButton detailsButton = new JButton(tr("Details"), ImageProvider.get("keydetails"));
+		detailsButton.setAlignmentX(Component.LEFT_ALIGNMENT);
+		detailsButton.addActionListener(new ActionListener(){
+
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				PGPSecretKey sk = sigKeys.get(keyBox.getSelectedIndex());
+				showKeyDetails(getPublicKeyFromRing(sk.getKeyID()));
+			}});
+		p.add(detailsButton);
+
+		JCheckBox random = new JCheckBox(tr("Use a random key from this list"));
+		random.setAlignmentX(Component.LEFT_ALIGNMENT);
+		p.add(random);
+
+		p.add(Box.createRigidArea(d));
+
+		JButton createButton = new JButton(tr("Create new Key"), ImageProvider.get("key"));
+		createButton.setAlignmentX(Component.LEFT_ALIGNMENT);
+		createButton.addActionListener(new ActionListener(){
+
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				try {
+					PGPSecretKey secKey = generateKey();
+					if (secKey != null) {
+						keyBox.addItem(secKeytoString(secKey));
+						sigKeys.add(secKey);
+					}
+				} catch (NoSuchAlgorithmException e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				} catch (NoSuchProviderException e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				} catch (FileNotFoundException e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				} catch (PGPException e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				} catch (IOException e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				}
+
+			}});
+		p.add(createButton);
+		p.add(Box.createRigidArea(d));
+
+		int n = JOptionPane.showOptionDialog(Main.parent, p, tr("Select a Key to sign"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, ImageProvider.get("keyring"), null, null);
+
+		if (n == JOptionPane.OK_OPTION) {
+			keepkey = keepkeyBox.isSelected();
+			if (random.isSelected()) {
+				Random r = new Random();
+				pgpSecKey = sigKeys.get(r.nextInt(sigKeys.size()-1));
+			} else {
+				pgpSecKey = sigKeys.get(keyBox.getSelectedIndex());
+			}
+		}
+		//String selection = (String) JOptionPane.showInputDialog(null, tr("Select a Key to sign"),tr("Secret Key Choice"), JOptionPane.OK_CANCEL_OPTION, null, keys, keys[0]);
+
+		//System.out.println(selection);
+
+		//		return pgpSecKey;
+	}
+
+	public void readGpgFiles() throws PGPException, IOException, NoSuchAlgorithmException, NoSuchProviderException {
+		FileInputStream pubIn;
+		FileInputStream secIn;
+		try {
+			pubIn = new FileInputStream(Main.pref.getPluginsDirectory().getPath() + "/trustosm/gnupg/secring.gpg");
+			secIn = new FileInputStream(Main.pref.getPluginsDirectory().getPath() + "/trustosm/gnupg/pubring.gpg");
+			//pubIn = new FileInputStream("/tmp/secring.gpg");
+			//secIn = new FileInputStream("/tmp/pubring.gpg");
+			pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(pubIn));
+			pgpPub = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(secIn));
+		} catch (FileNotFoundException e) {
+			System.err.println("No gpg files found in "+Main.pref.getPluginsDirectory().getPath() + "/trustosm/gnupg/secring.gpg");
+			System.err.println("Creating new...");
+			pgpSec = null;
+			pgpPub = null;
+			generateKey();
+		}
+
+	}
+
+	public void writeGpgFiles() throws FileNotFoundException, IOException {
+		FileOutputStream    pubOut = new FileOutputStream(Main.pref.getPluginsDirectory().getPath() + "/trustosm/gnupg/pubring.gpg");
+		FileOutputStream    secOut = new FileOutputStream(Main.pref.getPluginsDirectory().getPath() + "/trustosm/gnupg/secring.gpg");
+		pgpSec.encode(secOut);
+		pgpPub.encode(pubOut);
+		pubOut.flush();
+		secOut.flush();
+		pubOut.close();
+		secOut.close();
+	}
+
+	public static String[] generateTagsFromSigtext(String sigtext) {
+		String[] keyValue = sigtext.substring(sigtext.indexOf('\n')).split("=");
+		return keyValue;
+	}
+
+	public static String generateTagSigtext(OsmPrimitive osm, String key) {
+		String sigtext = "ID=" + osm.getUniqueId() + "\n";
+		sigtext += key + "=" + osm.get(key);
+		return sigtext;
+	}
+
+	public static String generateNodeSigtext(OsmPrimitive osm, Node node) {
+		LatLon point = node.getCoor();
+		String sigtext = "ID=" + osm.getUniqueId() + "\n";
+		sigtext += "Lat:" + point.latToString(CoordinateFormat.DECIMAL_DEGREES) + "\n";
+		sigtext += "Lon:" + point.lonToString(CoordinateFormat.DECIMAL_DEGREES);
+		return sigtext;
+	}
+
+	public void getPasswordfromUser() {
+
+		final JPasswordField passwordField = new JPasswordField();
+		JOptionPane jop = new JOptionPane(passwordField, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION, ImageProvider.get("lock"));
+		JDialog dialog = jop.createDialog("Password:");
+		dialog.addComponentListener(new ComponentAdapter(){
+			@Override
+			public void componentShown(ComponentEvent e){
+				passwordField.requestFocusInWindow();
+			}
+		});
+		dialog.setVisible(true);
+		int result = (Integer)jop.getValue();
+		dialog.dispose();
+		if(result == JOptionPane.OK_OPTION){
+			password = passwordField.getPassword();
+		}
+
+
+		/*final JPasswordField passwordField = new JPasswordField(10);
+		JOptionPane.showMessageDialog(Main.parent, passwordField, "Enter password", JOptionPane.OK_OPTION, ImageProvider.get("lock"));
+		password = passwordField.getPassword();
+		 */
+	}
+
+	public void checkTag(TrustOSMItem trust, String key) {
+		String sigtext = generateTagSigtext(trust.getOsmItem(),key);
+		TrustSignatures sigs;
+		if ((sigs = trust.getSigsOnKey(key))!=null)
+			for (PGPSignature sig : sigs.getSignatures()) {
+				trust.updateTagSigStatus(key, verify(sigtext,sig)? TrustSignatures.SIG_VALID : TrustSignatures.SIG_BROKEN);
+			}
+	}
+
+	public void checkNode(TrustOSMItem trust, Node node) {
+		String sigtext = generateNodeSigtext(trust.getOsmItem(),node);
+		TrustSignatures sigs;
+		if ((sigs = trust.getSigsOnNode(node))!=null)
+			for (PGPSignature sig : sigs.getSignatures()) {
+				trust.updateNodeSigStatus(node, verify(sigtext,sig)? TrustSignatures.SIG_VALID : TrustSignatures.SIG_BROKEN);
+			}
+	}
+
+	public void checkAll(TrustOSMItem trust) {
+		OsmPrimitive osm = trust.getOsmItem();
+		for (String key : osm.keySet()) {
+			checkTag(trust, key);
+		}
+
+		if(osm instanceof Node) {
+			checkNode(trust, (Node)osm);
+		} else if(osm instanceof Way) {
+			Iterator<Node> iter = ((Way)osm).getNodes().iterator();
+			while (iter.hasNext()) {
+				checkNode(trust, iter.next());
+			}
+		} else if(osm instanceof Relation) {
+
+		}
+	}
+
+	public void invalidIDWarning(OsmPrimitive osm) {
+		JOptionPane.showMessageDialog(Main.parent, tr("The object with the ID \"{0}\" ({1}) is newly created.\nYou can not sign it, because the signature would lose the ID-Reference after uploading it to the OSM-server.",osm.getUniqueId(),osm.toString()), tr("Signing canceled!"), JOptionPane.ERROR_MESSAGE);
+	}
+
+	public TrustOSMItem signGeometry(TrustOSMItem trust) {
+		PGPSignature s;
+		Node node;
+		OsmPrimitive osm = trust.getOsmItem();
+		if (osm.isNew()) {
+			invalidIDWarning(osm);
+			return trust;
+		}
+		if(osm instanceof Node) {
+			s = signNode(osm,(Node)osm);
+			if (s != null) trust.storeNodeSig((Node)osm, s);
+		} else if(osm instanceof Way) {
+			Iterator<Node> iter = ((Way)osm).getNodes().iterator();
+			while (iter.hasNext()) {
+				node = iter.next();
+				s = signNode(osm,node);
+				if (s != null) trust.storeNodeSig(node, s);
+			}
+		} else if(osm instanceof Relation) {
+
+		}
+		return trust;
+	}
+
+	public PGPSignature signNode(OsmPrimitive osm, Node node) {
+		if (osm.isNew()) {
+			invalidIDWarning(osm);
+			return null;
+		}
+		String tosign = generateNodeSigtext(osm,node);
+		return sign(tosign);
+	}
+
+	public TrustOSMItem signTag(TrustOSMItem trust, String key) {
+		OsmPrimitive osm = trust.getOsmItem();
+		if (osm.isNew()) {
+			invalidIDWarning(osm);
+			return trust;
+		}
+		PGPSignature s;
+		String tosign = generateTagSigtext(osm,key);
+		s = sign(tosign);
+		if (s != null)
+			trust.storeTagSig(key, s);
+		return trust;
+	}
+
+	public PGPSignature sign(String tosign) {
+
+		PGPSignature sig;
+		try{
+
+			readSecretKey();
+			if (pgpSec == null) return null;
+
+			if (password == null) {
+				getPasswordfromUser();
+			}
+
+			PGPPrivateKey pgpPrivKey = pgpSecKey.extractPrivateKey(password, "BC");
+			PGPSignatureGenerator sGen = new PGPSignatureGenerator(pgpSecKey.getPublicKey().getAlgorithm(), digest, "BC");
+			sGen.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, pgpPrivKey);
+			Iterator it = pgpSecKey.getPublicKey().getUserIDs();
+			if (it.hasNext())
+			{
+				PGPSignatureSubpacketGenerator  spGen = new PGPSignatureSubpacketGenerator();
+
+				spGen.setSignerUserID(false, (String)it.next());
+				sGen.setHashedSubpackets(spGen.generate());
+			}
+			sGen.update(tosign.getBytes(Charset.forName("UTF-8")));
+			sig = sGen.generate();
+			//writeSignatureToFile(sig, tosign, new FileOutputStream("/tmp/sigtest.asc"));
+			//sig.encode(new BCPGOutputStream(new ArmoredOutputStream(new FileOutputStream("/tmp/sigtest.asc"))));
+			return sig;
+		}catch (Exception e){//Catch exception if any
+			System.err.println("Error: " + e.getMessage());
+		}
+
+
+		/*		String seckeys = gpg.listSecretKeys()? gpg.getResult() : "GPG-ERROR: " + gpg.getErrorString();
+		System.out.println("Die gelisteten keys sehen so aus:\n"+seckeys);
+		String[] keys = seckeys.split("\n");
+		System.out.println("Das Array hat so viele einträge:"+keys.length);
+		if (keys.length <= 1) {
+			System.out.println("Auf auf zum lustigen generieren!");
+			generateKey();
+		}
+		System.out.println("Achtung die Errorausgabe sieht so aus:\n"+gpg.getErrorString());
+		String sig = gpg.sign(tosign, password)? gpg.getResult() : "GPG-ERROR: " + gpg.getErrorString();
+		 */
+
+
+		return null;
+	}
+
+	public boolean verify(String sigtext, PGPSignature sig) {
+		/*		if (gpg.verifySignature(sig)) {
+		success = trust.updateSigStatus(key, gpg.getResult().equals(sigtext)? TrustSignatures.SIG_VALID : TrustSignatures.SIG_BROKEN);
+	}*/
+		try {
+			sig.initVerify(pgpPub.getPublicKey(sig.getKeyID()), "BC");
+			sig.update(sigtext.getBytes(Charset.forName("UTF-8")));
+			return sig.verify();
+		}catch (Exception e){//Catch exception if any
+			System.err.println("Error: " + e.getMessage());
+		}
+		return false;
+	}
+
+
+	public static void writeSignatureToFile(PGPSignature sig, String clearText, FileOutputStream fout) throws Exception {
+		ArmoredOutputStream aOut = new ArmoredOutputStream(fout);
+		aOut.beginClearText(digest);
+		aOut.write(clearText.getBytes(Charset.forName("UTF-8")));
+		aOut.write('\n');
+		aOut.endClearText();
+
+		BCPGOutputStream bOut = new BCPGOutputStream(aOut);
+		sig.encode(bOut);
+		aOut.close();
+		bOut.close();
+	}
+
+	public Map<String, String> getKeyValueFromSignature(PGPSignature sig) {
+		Map<String, String> tags = new HashMap<String, String>();
+		try {
+			String sigtext = new String(sig.getEncoded(), Charset.forName("UTF-8"));
+			String[] kv = generateTagsFromSigtext(sigtext);
+			tags.put(kv[0],kv[1]);
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		return tags;
+	}
+
+	public static void showKeyDetails(PGPPublicKey key) {
+		String userid = "Unknown";
+		Iterator iter = key.getUserIDs();
+		if (iter.hasNext()) {
+			userid = (String)iter.next();
+		}
+
+		String fingerprint = new String(Hex.encode(key.getFingerprint())).toUpperCase();
+		String keyid = "0x"+Long.toHexString(key.getKeyID()).substring(8).toUpperCase();
+
+		String algorithm = "";
+		int algo = key.getAlgorithm();
+		switch(algo) {
+		case PGPPublicKey.DIFFIE_HELLMAN:
+			algorithm = "Diffie Hellman (DH)"; break;
+		case PGPPublicKey.DSA:
+			algorithm = "Digital Signature Algorithm (DSA)"; break;
+		case PGPPublicKey.EC:
+			algorithm = "Elliptic Curve (EC)"; break;
+		case PGPPublicKey.ECDSA:
+			algorithm = "Elliptic Curve Digital Signature Algorithm (ECDSA)"; break;
+		case PGPPublicKey.ELGAMAL_ENCRYPT:
+			algorithm = "Elgamal encrypt-only"; break;
+		case PGPPublicKey.ELGAMAL_GENERAL:
+			algorithm = "Elgamal"; break;
+		case PGPPublicKey.RSA_ENCRYPT:
+			algorithm = "Rivest Shamir Adleman (RSA) encrypt-only"; break;
+		case PGPPublicKey.RSA_GENERAL:
+			algorithm = "Rivest Shamir Adleman (RSA)"; break;
+		case PGPPublicKey.RSA_SIGN:
+			algorithm = "Rivest Shamir Adleman (RSA) sign-only"; break;
+		default:
+			algorithm = "Unknown algorithm ID: "+algo; break;
+		}
+
+		String strength = String.valueOf(key.getBitStrength());
+
+		//SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd:hh.mm.ss");
+		SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
+		String creationTime = formatter.format(key.getCreationTime());
+
+		long validSeconds = key.getValidSeconds();
+		String expirationTime;
+		if (validSeconds == 0) {
+			expirationTime = tr("never");
+		} else {
+			expirationTime = formatter.format(new Date(key.getCreationTime().getTime()+validSeconds*1000));
+		}
+
+
+		String[] labels = {tr("Primary user-ID: "), tr("Key-ID: "), tr("Fingerprint: "), tr("Algorithm: "), tr("Strength in bit: "), tr("Creation date: "), tr("Expiration date: ")};
+		String[] values = {userid, keyid, fingerprint, algorithm, strength, creationTime, expirationTime};
+		int numPairs = labels.length;
+
+		//Create and populate the panel.
+		JPanel p = new JPanel(new SpringLayout());
+		for (int i = 0; i < numPairs; i++) {
+			JLabel l = new JLabel(labels[i], JLabel.TRAILING);
+			p.add(l);
+			JTextField textField = new JTextField(values[i]);
+			textField.setEditable(false);
+			l.setLabelFor(textField);
+			p.add(textField);
+		}
+
+		//Lay out the panel.
+		SpringUtilities.makeCompactGrid(p,
+				numPairs, 2, //rows, cols
+				6, 6,        //initX, initY
+				6, 6);       //xPad, yPad
+
+
+		//		JPanel metaPanel = new JPanel();
+		//		metaPanel.setLayout(new BoxLayout(metaPanel, BoxLayout.PAGE_AXIS));
+		//		metaPanel.add(p);
+		//		JScrollPane sp = new JScrollPane(new KeySignaturesDialog(key));
+		//		sp.setPreferredSize(new Dimension(0,200));
+		//		metaPanel.add(sp);
+
+		JOptionPane.showMessageDialog(Main.parent, p, tr("PGP-Key details"), JOptionPane.INFORMATION_MESSAGE);
+	}
+
+
+	public PGPSecretKey generateKey() throws NoSuchAlgorithmException, NoSuchProviderException, PGPException, FileNotFoundException, IOException {
+
+		JTextField userId = new JTextField();
+		NameGenerator nameGen = new NameGenerator(Main.pref.getPluginsDirectory().getPath()+"/trustosm/resources/syllables.txt");
+		userId.setText(nameGen.compose(3));
+
+		final String[] sizes = {"1024", "2048", "3072", "4096"};
+
+		final JComboBox strengthBox = new JComboBox(sizes);
+		strengthBox.setEnabled(false);
+
+		/*		final String[] curves = {"prime192v1", "prime192v2", "prime192v3", "prime239v1", "prime239v2", "prime239v3", "prime256v1", "secp224r1", "secp256r1", "secp384r1", "secp521r1", "P-224", "P-256", "P-384", "P-521", "c2pnb163v1", "c2pnb163v2", "c2pnb163v3", "c2pnb176w1", "c2tnb191v2", "c2tnb191v1", "c2tnb191v3", "c2pnb208w1", "c2tnb239v1", "c2tnb239v2", "c2tnb239v3", "c2pnb272w1", "c2pnb304w1", "c2tnb359v1", "c2pnb368w1", "c2tnb431r1", "sect163r2", "sect233r1", "sect283r1", "sect409r1", "sect571r1", "B-163", "B-233", "B-283", "B-409", "B-571", "brainpoolp160r1", "brainpoolp160t1", "brainpoolp192r1", "brainpoolp192t1", "brainpoolp224r1", "brainpoolp224t1", "brainpoolp256r1", "brainpoolp256t1", "brainpoolp320r1", "brainpoolp320t1", "brainpoolp384r1", "brainpoolp384t1", "brainpoolp512r1", "brainpoolp512t1"};
+		final String[] curvesizes = {"192", "192", "192", "239", "239", "239", "256", "224", "256", "384", "521", "224", "256", "384", "521", "163", "163", "163", "176", "191", "191", "191", "208", "239", "239", "239", "272", "304", "359", "368", "431", "163", "233", "283", "409", "571", "163", "233", "283", "409", "571", "160", "160", "192", "192", "224", "224", "256", "256", "320", "320", "384", "384", "512", "512"};
+		final JComboBox curveBox = new JComboBox(curves);
+		curveBox.addActionListener(new ActionListener(){
+
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				strengthBox.setSelectedIndex(((JComboBox)e.getSource()).getSelectedIndex());
+			}});
+		curveBox.setEnabled(false);
+		 */
+
+		//		final String[] algos = {"DSA","RSA","ECDSA"};
+		final String[] algos = {"DSA","RSA"};
+		final JComboBox algoBox = new JComboBox(algos);
+		algoBox.addActionListener(new ActionListener(){
+
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				JComboBox cb = (JComboBox)e.getSource();
+				String alg = (String)cb.getSelectedItem();
+				if (alg.equals("DSA")) {
+					strengthBox.setSelectedItem("1024");
+					strengthBox.setEnabled(false);
+				} else
+					strengthBox.setEnabled(true);
+				/*if (alg.equals("ECDSA")) {
+					curveBox.setEnabled(true);
+					strengthBox.setModel(new DefaultComboBoxModel(curvesizes));
+					strengthBox.setSelectedItem(curvesizes[curveBox.getSelectedIndex()]);
+					strengthBox.setEnabled(false);
+				} else {
+					curveBox.setEnabled(false);
+					strengthBox.setModel(new DefaultComboBoxModel(sizes));
+					strengthBox.setEnabled(true);
+				}*/
+			}
+		});
+
+
+
+
+
+		final String[] protectAlgos = {"AES_256", "AES_192", "AES_128", "BLOWFISH", "CAST5", "DES", "IDEA", "SAFER", "TRIPLE_DES", "TWOFISH", "NULL"};
+		int[] protAl = {PGPEncryptedData.AES_256, PGPEncryptedData.AES_192, PGPEncryptedData.AES_128, PGPEncryptedData.BLOWFISH, PGPEncryptedData.CAST5, PGPEncryptedData.DES, PGPEncryptedData.IDEA, PGPEncryptedData.SAFER, PGPEncryptedData.TRIPLE_DES, PGPEncryptedData.TWOFISH, PGPEncryptedData.NULL};
+		final JComboBox protectBox = new JComboBox(protectAlgos);
+
+		final JDateChooser cal = new JDateChooser(null, null, null, new JSpinnerDateEditor());
+		cal.setPreferredSize(new Dimension(130,cal.getPreferredSize().height));
+
+		final String[] labels = {tr("User-ID:"), tr("Select algorithm:"), tr("Choose Bitlength (Strength):"), tr("Encryption algorithm to protect private key:"), tr("Choose an expiry date for the key:")};
+		final JComponent[] values = {userId, algoBox, strengthBox, protectBox, cal};
+
+		int numPairs = labels.length;
+
+		//Create and populate the panel.
+		JPanel p = new JPanel(new SpringLayout());
+		for (int i = 0; i < numPairs; i++) {
+			JLabel l = new JLabel(labels[i], JLabel.TRAILING);
+			p.add(l);
+			l.setLabelFor(values[i]);
+			p.add(values[i]);
+		}
+
+		//Lay out the panel.
+		SpringUtilities.makeCompactGrid(p,
+				numPairs, 2, //rows, cols
+				6, 6,        //initX, initY
+				16, 6);       //xPad, yPad
+
+		int n = JOptionPane.showOptionDialog(Main.parent, p, tr("Create a new signing key"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null);
+
+		if (n != JOptionPane.OK_OPTION)
+			return null;
+
+
+		String algo = (String)algoBox.getSelectedItem();
+
+		KeyPairGenerator Kpg = KeyPairGenerator.getInstance(algo, "BC");
+
+		int al;
+		/*		if (algo.equals("ECDSA")) {
+			al = PGPPublicKey.ECDSA;
+			ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec((String)curveBox.getSelectedItem());
+			try {
+				Kpg.initialize(ecSpec);
+			} catch (InvalidAlgorithmParameterException e1) {
+				// TODO Auto-generated catch block
+				System.err.println("EC-Parameter not accepted");
+				e1.printStackTrace();
+			}
+		}
+		else {*/
+		Kpg.initialize(Integer.parseInt((String)strengthBox.getSelectedItem()));
+		//
+		// this takes a while as the key generator has to generate some DSA params
+		// before it generates the key.
+		//
+
+		if (algo.equals("RSA")) al = PGPPublicKey.RSA_GENERAL;
+		else al = PGPPublicKey.DSA;
+		//		}
+
+
+		KeyPair kp = Kpg.generateKeyPair();
+
+		Date now = new Date();
+		PGPKeyPair pgpKp = new PGPKeyPair(al, kp, now);
+
+		getPasswordfromUser();
+
+		PGPSignatureSubpacketVector subPck = null;
+		PGPSignatureSubpacketGenerator spGen = null;
+		Date expire = cal.getDate();
+		if (expire != null && expire.after(now)) {
+			spGen = new PGPSignatureSubpacketGenerator();
+			spGen.setKeyExpirationTime(true, (expire.getTime()-now.getTime())/1000);
+			subPck = spGen.generate();
+		}
+
+		PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, pgpKp,
+				userId.getText(), protAl[protectBox.getSelectedIndex()], password, true, subPck, null, new SecureRandom(), "BC");
+
+		if (pgpPub == null) {
+			Vector<PGPPublicKeyRing> pubKeyRing = new Vector<PGPPublicKeyRing>(1);
+			pubKeyRing.add(keyRingGen.generatePublicKeyRing());
+			pgpPub = new PGPPublicKeyRingCollection(pubKeyRing);
+		} else {
+			pgpPub = PGPPublicKeyRingCollection.addPublicKeyRing(pgpPub, keyRingGen.generatePublicKeyRing());
+		}
+
+		PGPSecretKeyRing secRing = keyRingGen.generateSecretKeyRing();
+		if (pgpSec == null) {
+			Vector<PGPSecretKeyRing> secKeyRing = new Vector<PGPSecretKeyRing>(1);
+			secKeyRing.add(secRing);
+			pgpSec = new PGPSecretKeyRingCollection(secKeyRing);
+		} else {
+			pgpSec = PGPSecretKeyRingCollection.addSecretKeyRing(pgpSec, secRing);
+		}
+
+
+		//writeGpgFiles();
+
+		return secRing.getSecretKey();
+	}
+
+}
Index: /applications/editors/josm/plugins/trustosm/src/tools/NameGenerator.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/tools/NameGenerator.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/tools/NameGenerator.java	(revision 24389)
@@ -0,0 +1,298 @@
+package tools;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * This class is released under GNU general public license
+ * 
+ * Description: This class generates random names from syllables, and provides programmer a
+ * simple way to set a group of rules for generator to avoid unpronounceable and bizarre names.
+ * 
+ * SYLLABLE FILE REQUIREMENTS/FORMAT:
+ * 1) all syllables are separated by line break.
+ * 2) Syllable should not contain or start with whitespace, as this character is ignored and only first part of the syllable is read.
+ * 3) + and - characters are used to set rules, and using them in other way, may result in unpredictable results.
+ * 4) Empty lines are ignored.
+ * 
+ * SYLLABLE CLASSIFICATION:
+ * Name is usually composed from 3 different class of syllables, which include prefix, middle part and suffix.
+ * To declare syllable as a prefix in the file, insert "-" as a first character of the line.
+ * To declare syllable as a suffix in the file, insert "+" as a first character of the line.
+ * everything else is read as a middle part.
+ * 
+ * NUMBER OF SYLLABLES:
+ * Names may have any positive number of syllables. In case of 2 syllables, name will be composed from prefix and suffix.
+ * In case of 1 syllable, name will be chosen from amongst the prefixes.
+ * In case of 3 and more syllables, name will begin with prefix, is filled with middle parts and ended with suffix.
+ * 
+ * ASSIGNING RULES:
+ * I included a way to set 4 kind of rules for every syllable. To add rules to the syllables, write them right after the
+ * syllable and SEPARATE WITH WHITESPACE. (example: "aad +v -c"). The order of rules is not important.
+ * 
+ * RULES:
+ * 1) +v means that next syllable must definitely start with a vocal.
+ * 2) +c means that next syllable must definitely start with a consonant.
+ * 3) -v means that this syllable can only be added to another syllable, that ends with a vocal.
+ * 4) -c means that this syllable can only be added to another syllable, that ends with a consonant.
+ * So, our example: "aad +v -c" means that "aad" can only be after consonant and next syllable must start with vocal.
+ * Beware of creating logical mistakes, like providing only syllables ending with consonants, but expecting only vocals, which will be detected
+ * and RuntimeException will be thrown.
+ * 
+ * TO START:
+ * Create a new NameGenerator object, provide the syllable file, and create names using compose() method.
+ * 
+ * @author Joonas Vali, August 2009.
+ *
+ */
+public class NameGenerator {
+	ArrayList<String> pre = new ArrayList<String>();
+	ArrayList<String> mid = new ArrayList<String>();
+	ArrayList<String> sur = new ArrayList<String>();
+
+	final private static char[] vocals = {'a', 'e', 'i', 'o', 'u', 'ä', 'ö', 'õ', 'ü', 'y'};
+	final private static char[] consonants = {'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p',	'q', 'r', 's', 't', 'v', 'w', 'x', 'y'};
+
+	private String fileName;
+
+	/**
+	 * Create new random name generator object. refresh() is automatically called.
+	 * @param fileName insert file name, where syllables are located
+	 * @throws IOException
+	 */
+	public NameGenerator(String fileName) throws IOException{
+		this.fileName = fileName;
+		refresh();
+	}
+
+	/**
+	 * Change the file. refresh() is automatically called during the process.
+	 * @param fileName insert the file name, where syllables are located.
+	 * @throws IOException
+	 */
+	public void changeFile(String fileName) throws IOException{
+		if(fileName == null) throw new IOException("File name cannot be null");
+		this.fileName = fileName;
+		refresh();
+	}
+
+	/**
+	 * Refresh names from file. No need to call that method, if you are not changing the file during the operation of program, as this method
+	 * is called every time file name is changed or new NameGenerator object created.
+	 * @throws IOException
+	 */
+	public void refresh() throws IOException{
+
+		FileReader input = null;
+		BufferedReader bufRead;
+		String line;
+
+		input = new FileReader(fileName);
+
+		bufRead = new BufferedReader(input);
+		line="";
+
+		while(line != null){
+			line = bufRead.readLine();
+			if(line != null && !line.equals("")){
+				if(line.charAt(0) == '-'){
+					pre.add(line.substring(1).toLowerCase());
+				}
+				else if(line.charAt(0) == '+'){
+					sur.add(line.substring(1).toLowerCase());
+				}
+				else{
+					mid.add(line.toLowerCase());
+				}
+			}
+		}
+		bufRead.close();
+	}
+
+	private String upper(String s){
+		return s.substring(0,1).toUpperCase().concat(s.substring(1));
+	}
+
+	private boolean containsConsFirst(ArrayList<String> array){
+		for(String s: array){
+			if(consonantFirst(s)) return true;
+		}
+		return false;
+	}
+
+	private boolean containsVocFirst(ArrayList<String> array){
+		for(String s: array){
+			if(vocalFirst(s)) return true;
+		}
+		return false;
+	}
+
+	private boolean allowCons(ArrayList<String> array){
+		for(String s: array){
+			if(hatesPreviousVocals(s) || hatesPreviousConsonants(s) == false) return true;
+		}
+		return false;
+	}
+
+	private boolean allowVocs(ArrayList<String> array){
+		for(String s: array){
+			if(hatesPreviousConsonants(s) || hatesPreviousVocals(s) == false) return true;
+		}
+		return false;
+	}
+
+	private boolean expectsVocal(String s){
+		if(s.substring(1).contains("+v")) return true;
+		else return false;
+	}
+	private boolean expectsConsonant(String s){
+		if(s.substring(1).contains("+c")) return true;
+		else return false;
+	}
+	private boolean hatesPreviousVocals(String s){
+		if(s.substring(1).contains("-c")) return true;
+		else return false;
+	}
+	private boolean hatesPreviousConsonants(String s){
+		if(s.substring(1).contains("-v")) return true;
+		else return false;
+	}
+
+	private String pureSyl(String s){
+		s = s.trim();
+		if(s.charAt(0) == '+' || s.charAt(0) == '-') s = s.substring(1);
+		return s.split(" ")[0];
+	}
+
+	private boolean vocalFirst(String s){
+		return (String.copyValueOf(vocals).contains(String.valueOf(s.charAt(0)).toLowerCase()));
+	}
+
+	private boolean consonantFirst(String s){
+		return (String.copyValueOf(consonants).contains(String.valueOf(s.charAt(0)).toLowerCase()));
+	}
+
+	private boolean vocalLast(String s){
+		return (String.copyValueOf(vocals).contains(String.valueOf(s.charAt(s.length()-1)).toLowerCase()));
+	}
+
+	private boolean consonantLast(String s){
+		return (String.copyValueOf(consonants).contains(String.valueOf(s.charAt(s.length()-1)).toLowerCase()));
+	}
+
+
+	/**
+	 * Compose a new name.
+	 * @param syls The number of syllables used in name.
+	 * @return Returns composed name as a String
+	 * @throws RuntimeException when logical mistakes are detected inside chosen file, and program is unable to complete the name.
+	 */
+	public String compose(int syls){
+		if(syls > 2 && mid.size() == 0) throw new RuntimeException("You are trying to create a name with more than 3 parts, which requires middle parts, " +
+				"which you have none in the file "+fileName+". You should add some. Every word, which doesn't have + or - for a prefix is counted as a middle part.");
+		if(pre.size() == 0) throw new RuntimeException("You have no prefixes to start creating a name. add some and use \"-\" prefix, to identify it as a prefix for a name. (example: -asd)");
+		if(sur.size() == 0) throw new RuntimeException("You have no suffixes to end a name. add some and use \"+\" prefix, to identify it as a suffix for a name. (example: +asd)");
+		if(syls < 1) throw new RuntimeException("compose(int syls) can't have less than 1 syllable");
+		int expecting = 0; // 1 for vocal, 2 for consonant
+		int last = 0; // 1 for vocal, 2 for consonant
+		String name;
+		int a = (int)(Math.random() * pre.size());
+
+		if(vocalLast(pureSyl(pre.get(a)))) last = 1;
+		else last = 2;
+
+		if(syls > 2){
+			if(expectsVocal(pre.get(a))){
+				expecting = 1;
+				if(containsVocFirst(mid) == false) throw new RuntimeException("Expecting \"middle\" part starting with vocal, " +
+				"but there is none. You should add one, or remove requirement for one.. ");
+			}
+			if(expectsConsonant(pre.get(a))){
+				expecting = 2;
+				if(containsConsFirst(mid) == false) throw new RuntimeException("Expecting \"middle\" part starting with consonant, " +
+				"but there is none. You should add one, or remove requirement for one.. ");
+			}
+		}
+		else{
+			if(expectsVocal(pre.get(a))){
+				expecting = 1;
+				if(containsVocFirst(sur) == false) throw new RuntimeException("Expecting \"suffix\" part starting with vocal, " +
+				"but there is none. You should add one, or remove requirement for one.. ");
+			}
+			if(expectsConsonant(pre.get(a))){
+				expecting = 2;
+				if(containsConsFirst(sur) == false) throw new RuntimeException("Expecting \"suffix\" part starting with consonant, " +
+				"but there is none. You should add one, or remove requirement for one.. ");
+			}
+		}
+		if(vocalLast(pureSyl(pre.get(a))) && allowVocs(mid) == false) throw new RuntimeException("Expecting \"middle\" part that allows last character of prefix to be a vocal, " +
+				"but there is none. You should add one, or remove requirements that cannot be fulfilled.. the prefix used, was : \""+pre.get(a)+"\", which" +
+		"means there should be a part available, that has \"-v\" requirement or no requirements for previous syllables at all.");
+
+		if(consonantLast(pureSyl(pre.get(a))) && allowCons(mid) == false) throw new RuntimeException("Expecting \"middle\" part that allows last character of prefix to be a consonant, " +
+				"but there is none. You should add one, or remove requirements that cannot be fulfilled.. the prefix used, was : \""+pre.get(a)+"\", which" +
+		"means there should be a part available, that has \"-c\" requirement or no requirements for previous syllables at all.");
+
+		int b[] = new int[syls];
+		for(int i = 0; i<b.length-2; i++){
+
+			do{
+				b[i] = (int)(Math.random() * mid.size());
+				//System.out.println("exp " +expecting+" vocalF:"+vocalFirst(mid.get(b[i]))+" syl: "+mid.get(b[i]));
+			}
+			while(expecting == 1 && vocalFirst(pureSyl(mid.get(b[i]))) == false || expecting == 2 && consonantFirst(pureSyl(mid.get(b[i]))) == false
+					|| last == 1 && hatesPreviousVocals(mid.get(b[i])) || last == 2 && hatesPreviousConsonants(mid.get(b[i])));
+
+			expecting = 0;
+			if(expectsVocal(mid.get(b[i]))){
+				expecting = 1;
+				if(i < b.length-3 && containsVocFirst(mid) == false) throw new RuntimeException("Expecting \"middle\" part starting with vocal, " +
+				"but there is none. You should add one, or remove requirement for one.. ");
+				if(i == b.length-3 && containsVocFirst(sur) == false) throw new RuntimeException("Expecting \"suffix\" part starting with vocal, " +
+				"but there is none. You should add one, or remove requirement for one.. ");
+			}
+			if(expectsConsonant(mid.get(b[i]))){
+				expecting = 2;
+				if(i < b.length-3 && containsConsFirst(mid) == false) throw new RuntimeException("Expecting \"middle\" part starting with consonant, " +
+				"but there is none. You should add one, or remove requirement for one.. ");
+				if(i == b.length-3 && containsConsFirst(sur) == false) throw new RuntimeException("Expecting \"suffix\" part starting with consonant, " +
+				"but there is none. You should add one, or remove requirement for one.. ");
+			}
+			if(vocalLast(pureSyl(mid.get(b[i]))) && allowVocs(mid) == false && syls > 3) throw new RuntimeException("Expecting \"middle\" part that allows last character of last syllable to be a vocal, " +
+					"but there is none. You should add one, or remove requirements that cannot be fulfilled.. the part used, was : \""+mid.get(b[i])+"\", which " +
+			"means there should be a part available, that has \"-v\" requirement or no requirements for previous syllables at all.");
+
+			if(consonantLast(pureSyl(mid.get(b[i]))) && allowCons(mid) == false && syls > 3) throw new RuntimeException("Expecting \"middle\" part that allows last character of last syllable to be a consonant, " +
+					"but there is none. You should add one, or remove requirements that cannot be fulfilled.. the part used, was : \""+mid.get(b[i])+"\", which " +
+			"means there should be a part available, that has \"-c\" requirement or no requirements for previous syllables at all.");
+			if(i == b.length-3){
+				if(vocalLast(pureSyl(mid.get(b[i]))) && allowVocs(sur) == false) throw new RuntimeException("Expecting \"suffix\" part that allows last character of last syllable to be a vocal, " +
+						"but there is none. You should add one, or remove requirements that cannot be fulfilled.. the part used, was : \""+mid.get(b[i])+"\", which " +
+				"means there should be a suffix available, that has \"-v\" requirement or no requirements for previous syllables at all.");
+
+				if(consonantLast(pureSyl(mid.get(b[i]))) && allowCons(sur) == false) throw new RuntimeException("Expecting \"suffix\" part that allows last character of last syllable to be a consonant, " +
+						"but there is none. You should add one, or remove requirements that cannot be fulfilled.. the part used, was : \""+mid.get(b[i])+"\", which " +
+				"means there should be a suffix available, that has \"-c\" requirement or no requirements for previous syllables at all.");
+			}
+			if(vocalLast(pureSyl(mid.get(b[i])))) last = 1;
+			else last = 2;
+		}
+
+		int c;
+		do{
+			c = (int)(Math.random() * sur.size());
+		}
+		while(expecting == 1 && vocalFirst(pureSyl(sur.get(c))) == false || expecting == 2 && consonantFirst(pureSyl(sur.get(c))) == false
+				|| last == 1 && hatesPreviousVocals(sur.get(c)) || last == 2 && hatesPreviousConsonants(sur.get(c)));
+
+		name = upper(pureSyl(pre.get(a).toLowerCase()));
+		for(int i = 0; i<b.length-2; i++){
+			name = name.concat(pureSyl(mid.get(b[i]).toLowerCase()));
+		}
+		if(syls > 1)
+			name = name.concat(pureSyl(sur.get(c).toLowerCase()));
+		return name;
+	}
+}
Index: /applications/editors/josm/plugins/trustosm/src/tools/SpringUtilities.java
===================================================================
--- /applications/editors/josm/plugins/trustosm/src/tools/SpringUtilities.java	(revision 24389)
+++ /applications/editors/josm/plugins/trustosm/src/tools/SpringUtilities.java	(revision 24389)
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   - Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   - Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ *   - Neither the name of Oracle or the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */ 
+
+package tools;
+
+import javax.swing.*;
+import javax.swing.SpringLayout;
+import java.awt.*;
+
+/**
+ * A 1.4 file that provides utility methods for
+ * creating form- or grid-style layouts with SpringLayout.
+ * These utilities are used by several programs, such as
+ * SpringBox and SpringCompactGrid.
+ */
+public class SpringUtilities {
+    /**
+     * A debugging utility that prints to stdout the component's
+     * minimum, preferred, and maximum sizes.
+     */
+    public static void printSizes(Component c) {
+        System.out.println("minimumSize = " + c.getMinimumSize());
+        System.out.println("preferredSize = " + c.getPreferredSize());
+        System.out.println("maximumSize = " + c.getMaximumSize());
+    }
+
+    /**
+     * Aligns the first <code>rows</code> * <code>cols</code>
+     * components of <code>parent</code> in
+     * a grid. Each component is as big as the maximum
+     * preferred width and height of the components.
+     * The parent is made just big enough to fit them all.
+     *
+     * @param rows number of rows
+     * @param cols number of columns
+     * @param initialX x location to start the grid at
+     * @param initialY y location to start the grid at
+     * @param xPad x padding between cells
+     * @param yPad y padding between cells
+     */
+    public static void makeGrid(Container parent,
+                                int rows, int cols,
+                                int initialX, int initialY,
+                                int xPad, int yPad) {
+        SpringLayout layout;
+        try {
+            layout = (SpringLayout)parent.getLayout();
+        } catch (ClassCastException exc) {
+            System.err.println("The first argument to makeGrid must use SpringLayout.");
+            return;
+        }
+
+        Spring xPadSpring = Spring.constant(xPad);
+        Spring yPadSpring = Spring.constant(yPad);
+        Spring initialXSpring = Spring.constant(initialX);
+        Spring initialYSpring = Spring.constant(initialY);
+        int max = rows * cols;
+
+        //Calculate Springs that are the max of the width/height so that all
+        //cells have the same size.
+        Spring maxWidthSpring = layout.getConstraints(parent.getComponent(0)).
+                                    getWidth();
+        Spring maxHeightSpring = layout.getConstraints(parent.getComponent(0)).
+                                    getWidth();
+        for (int i = 1; i < max; i++) {
+            SpringLayout.Constraints cons = layout.getConstraints(
+                                            parent.getComponent(i));
+
+            maxWidthSpring = Spring.max(maxWidthSpring, cons.getWidth());
+            maxHeightSpring = Spring.max(maxHeightSpring, cons.getHeight());
+        }
+
+        //Apply the new width/height Spring. This forces all the
+        //components to have the same size.
+        for (int i = 0; i < max; i++) {
+            SpringLayout.Constraints cons = layout.getConstraints(
+                                            parent.getComponent(i));
+
+            cons.setWidth(maxWidthSpring);
+            cons.setHeight(maxHeightSpring);
+        }
+
+        //Then adjust the x/y constraints of all the cells so that they
+        //are aligned in a grid.
+        SpringLayout.Constraints lastCons = null;
+        SpringLayout.Constraints lastRowCons = null;
+        for (int i = 0; i < max; i++) {
+            SpringLayout.Constraints cons = layout.getConstraints(
+                                                 parent.getComponent(i));
+            if (i % cols == 0) { //start of new row
+                lastRowCons = lastCons;
+                cons.setX(initialXSpring);
+            } else { //x position depends on previous component
+                cons.setX(Spring.sum(lastCons.getConstraint(SpringLayout.EAST),
+                                     xPadSpring));
+            }
+
+            if (i / cols == 0) { //first row
+                cons.setY(initialYSpring);
+            } else { //y position depends on previous row
+                cons.setY(Spring.sum(lastRowCons.getConstraint(SpringLayout.SOUTH),
+                                     yPadSpring));
+            }
+            lastCons = cons;
+        }
+
+        //Set the parent's size.
+        SpringLayout.Constraints pCons = layout.getConstraints(parent);
+        pCons.setConstraint(SpringLayout.SOUTH,
+                            Spring.sum(
+                                Spring.constant(yPad),
+                                lastCons.getConstraint(SpringLayout.SOUTH)));
+        pCons.setConstraint(SpringLayout.EAST,
+                            Spring.sum(
+                                Spring.constant(xPad),
+                                lastCons.getConstraint(SpringLayout.EAST)));
+    }
+
+    /* Used by makeCompactGrid. */
+    private static SpringLayout.Constraints getConstraintsForCell(
+                                                int row, int col,
+                                                Container parent,
+                                                int cols) {
+        SpringLayout layout = (SpringLayout) parent.getLayout();
+        Component c = parent.getComponent(row * cols + col);
+        return layout.getConstraints(c);
+    }
+
+    /**
+     * Aligns the first <code>rows</code> * <code>cols</code>
+     * components of <code>parent</code> in
+     * a grid. Each component in a column is as wide as the maximum
+     * preferred width of the components in that column;
+     * height is similarly determined for each row.
+     * The parent is made just big enough to fit them all.
+     *
+     * @param rows number of rows
+     * @param cols number of columns
+     * @param initialX x location to start the grid at
+     * @param initialY y location to start the grid at
+     * @param xPad x padding between cells
+     * @param yPad y padding between cells
+     */
+    public static void makeCompactGrid(Container parent,
+                                       int rows, int cols,
+                                       int initialX, int initialY,
+                                       int xPad, int yPad) {
+        SpringLayout layout;
+        try {
+            layout = (SpringLayout)parent.getLayout();
+        } catch (ClassCastException exc) {
+            System.err.println("The first argument to makeCompactGrid must use SpringLayout.");
+            return;
+        }
+
+        //Align all cells in each column and make them the same width.
+        Spring x = Spring.constant(initialX);
+        for (int c = 0; c < cols; c++) {
+            Spring width = Spring.constant(0);
+            for (int r = 0; r < rows; r++) {
+                width = Spring.max(width,
+                                   getConstraintsForCell(r, c, parent, cols).
+                                       getWidth());
+            }
+            for (int r = 0; r < rows; r++) {
+                SpringLayout.Constraints constraints =
+                        getConstraintsForCell(r, c, parent, cols);
+                constraints.setX(x);
+                constraints.setWidth(width);
+            }
+            x = Spring.sum(x, Spring.sum(width, Spring.constant(xPad)));
+        }
+
+        //Align all cells in each row and make them the same height.
+        Spring y = Spring.constant(initialY);
+        for (int r = 0; r < rows; r++) {
+            Spring height = Spring.constant(0);
+            for (int c = 0; c < cols; c++) {
+                height = Spring.max(height,
+                                    getConstraintsForCell(r, c, parent, cols).
+                                        getHeight());
+            }
+            for (int c = 0; c < cols; c++) {
+                SpringLayout.Constraints constraints =
+                        getConstraintsForCell(r, c, parent, cols);
+                constraints.setY(y);
+                constraints.setHeight(height);
+            }
+            y = Spring.sum(y, Spring.sum(height, Spring.constant(yPad)));
+        }
+
+        //Set the parent's size.
+        SpringLayout.Constraints pCons = layout.getConstraints(parent);
+        pCons.setConstraint(SpringLayout.SOUTH, y);
+        pCons.setConstraint(SpringLayout.EAST, x);
+    }
+}
