Index: applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/ITagSelectorListener.java
===================================================================
--- applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/ITagSelectorListener.java	(revision 14330)
+++ applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/ITagSelectorListener.java	(revision 14330)
@@ -0,0 +1,9 @@
+package org.openstreetmap.josm.plugins.tageditor.tagspec.ui;
+
+import org.openstreetmap.josm.plugins.tageditor.tagspec.KeyValuePair;
+
+
+public interface ITagSelectorListener {
+
+	public void itemSelected(KeyValuePair pair);
+}
Index: applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/KeyValueCellRenderer.java
===================================================================
--- applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/KeyValueCellRenderer.java	(revision 14330)
+++ applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/KeyValueCellRenderer.java	(revision 14330)
@@ -0,0 +1,40 @@
+package org.openstreetmap.josm.plugins.tageditor.tagspec.ui;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+import java.util.logging.Logger;
+
+import javax.swing.JLabel;
+import javax.swing.JTable;
+import javax.swing.table.TableCellRenderer;
+
+public class KeyValueCellRenderer extends JLabel implements TableCellRenderer  {
+	
+	private static Logger logger = Logger.getLogger(KeyValueCellRenderer.class.getName());
+	public static final Color BG_COLOR_SELECTED = new Color(143,170,255);
+	
+
+	protected void init() {
+		setFont(new Font("Courier",Font.PLAIN,12));
+		setOpaque(true);
+	}
+	
+	public KeyValueCellRenderer() {
+		init();
+	}
+	
+	@Override
+	public Component getTableCellRendererComponent(JTable table, Object value,
+			boolean isSelected, boolean hasFocus, int rowIndex, int colIndex) {
+	
+		if (isSelected) {
+			setBackground(BG_COLOR_SELECTED);			
+		} else  {
+			setBackground(Color.WHITE);
+		}
+		setText((String)value);
+		setIcon(null);
+		return this; 
+	}
+}
Index: applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/TabularTagSelector.java
===================================================================
--- applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/TabularTagSelector.java	(revision 14330)
+++ applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/TabularTagSelector.java	(revision 14330)
@@ -0,0 +1,274 @@
+package org.openstreetmap.josm.plugins.tageditor.tagspec.ui;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextField;
+import javax.swing.KeyStroke;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import org.openstreetmap.josm.plugins.tageditor.tagspec.KeyValuePair;
+
+
+
+public class TabularTagSelector extends JPanel {
+
+	private TagsTable tagsTable;	
+	private JTextField tfFilter;
+	private JButton btnApply;
+	private JScrollPane scrollPane;
+	private ArrayList<ITagSelectorListener> listeners = new ArrayList<ITagSelectorListener>();
+
+	
+	protected JPanel buildFilterPanel() {
+		JPanel pnl = new JPanel();
+		JLabel lbl = new JLabel(tr("Search: "));
+		pnl.setLayout(new FlowLayout(FlowLayout.LEFT));
+		tfFilter = new JTextField(20);
+		pnl.add(lbl);
+		pnl.add(tfFilter,BorderLayout.CENTER);
+		JButton btn = new JButton(tr("Filter"));
+		pnl.add(btn);
+		btn.addActionListener(
+			new ActionListener() {
+				@Override
+				public void actionPerformed(ActionEvent e) {
+					filter(tfFilter.getText());						
+				}
+				
+			}
+		);
+		btn = new JButton(tr("Clear"));
+		pnl.add(btn);
+		btn.addActionListener(
+				new ActionListener() {
+					@Override
+					public void actionPerformed(ActionEvent e) {
+						tfFilter.setText("");
+						tfFilter.requestFocus();
+					}					
+				}
+			);		
+		return pnl;
+	}
+	
+	protected JScrollPane buildPresetGrid() {
+		
+		tagsTable = new TagsTable(new TagsTableModel(),new TagsTableColumnModel());
+		getModel().initFromTagSpecifications();
+		
+		scrollPane = new JScrollPane(tagsTable);
+		
+		scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+		scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
+		
+		// this adapters ensures that the width of the tag table columns is adjusted
+		// to the width of the scroll pane viewport. Also tried to overwrite 
+		// getPreferredViewportSize() in JTable, but did not work.
+		//
+		scrollPane.addComponentListener(
+				new ComponentAdapter() {
+					@Override public void componentResized(ComponentEvent e) {
+	                    super.componentResized(e);
+	                    Dimension d = scrollPane.getViewport().getExtentSize();
+	                    tagsTable.adjustColumnWidth(d.width);
+                    }
+				}
+		);
+		
+		// add the double click listener 
+		//
+		tagsTable.addMouseListener(new DoubleClickAdapter());
+		
+		// replace Enter action. apply the current preset on enter
+		//
+		tagsTable.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0));
+		ActionListener enterAction = new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				int rowNum = tagsTable.getSelectedRow();
+				if (rowNum >= 0) {
+					KeyValuePair item = getModel().getVisibleItem(rowNum);
+					fireItemSelected(item);
+				}
+			}
+		};
+		
+		tagsTable.registerKeyboardAction(
+			enterAction, 
+			"Enter", 
+			KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), 
+			JComponent.WHEN_FOCUSED
+		);
+		
+		return scrollPane;
+	}
+	
+	
+	protected JPanel buildControlButtonPanel() {
+		JPanel pnl = new JPanel();
+		pnl.setLayout(new FlowLayout(FlowLayout.LEFT));
+		btnApply = new JButton("Apply");
+		pnl.add(btnApply);
+		btnApply.addActionListener(
+			new ActionListener() {
+				@Override
+				public void actionPerformed(ActionEvent arg0) {
+					int row = tagsTable.getSelectedRow();
+					if (row >=0) {
+						KeyValuePair item = getModel().getVisibleItem(row);
+						fireItemSelected(item);
+					}
+				}				
+			}
+		);
+		return pnl;		
+	}
+	
+	protected void build() {
+		setLayout(new BorderLayout());
+		add(buildFilterPanel(), BorderLayout.NORTH);
+		add(buildPresetGrid(), BorderLayout.CENTER);
+		add(buildControlButtonPanel(), BorderLayout.SOUTH);
+		
+		// wire the text field for filter expressions to the prests
+		// table
+		//
+		tfFilter.getDocument().addDocumentListener(
+				new DocumentListener() {
+					@Override
+					public void changedUpdate(DocumentEvent arg0) {
+						onUpdate();
+					}
+
+					@Override
+					public void insertUpdate(DocumentEvent arg0) {
+						onUpdate();
+					}
+
+					@Override
+					public void removeUpdate(DocumentEvent arg0) {
+						onUpdate();
+					}
+				
+					protected void onUpdate() {
+					    filter(tfFilter.getText());
+					}
+				}
+			);
+		
+		tfFilter.addActionListener(
+				new ActionListener() {
+					@Override
+					public void actionPerformed(ActionEvent e) {
+						filter(tfFilter.getText());
+					}					
+				}
+		);
+		
+		// wire the apply button to the selection model of the preset table
+		// 
+		tagsTable.getSelectionModel().addListSelectionListener(
+			new ListSelectionListener() {
+	
+				@Override
+				public void valueChanged(ListSelectionEvent e) {
+					btnApply.setEnabled(tagsTable.getSelectedRowCount() != 0);
+				}
+			}
+		);
+		
+	
+		// load the set of presets and bind them to the preset table
+		//
+		tagsTable.getSelectionModel().clearSelection();
+		btnApply.setEnabled(false);
+	
+	}
+	
+	 public TabularTagSelector() {
+		build();
+	}
+	 
+	 
+	 public void filter(String filter) {
+		tagsTable.getSelectionModel().clearSelection();
+		getModel().filter(filter);
+		
+		tagsTable.scrollRectToVisible(tagsTable.getCellRect(0, 0, false));
+		
+		// we change the number of rows by applying a filter condition. Because
+		// the table is embedded in a JScrollPane which again may be embedded in
+		// other JScrollPanes or JSplitPanes it seems that we have to recalculate 
+		// the layout and repaint the component tree. Maybe there is a more efficient way
+		// to keep the GUI in sync with the number of rows in table. By trial
+		// and error I ended up with the following lines. 
+		// 
+		Component c = tagsTable;
+		while(c != null) {
+			c.doLayout();
+			c.repaint();
+			c = c.getParent();
+		}
+	}
+	 
+	 protected TagsTableModel getModel() {
+		 return (TagsTableModel)tagsTable.getModel();
+	 }	 
+
+
+	public void addTagSelectorListener(ITagSelectorListener listener) {
+		synchronized(this.listeners) {
+			if (listener != null && ! listeners.contains(listener)) {
+				listeners.add(listener);
+			}
+		}
+	}
+	
+	public void removeTagSelectorListener(ITagSelectorListener listener) {
+		synchronized(this.listeners) {
+			if (listener != null) {
+				listeners.remove(listener);
+			}
+		}
+	}
+	
+	protected void fireItemSelected(KeyValuePair pair) {
+		synchronized(this.listeners) {
+			for(ITagSelectorListener listener: listeners) {
+				listener.itemSelected(pair);
+			}
+		}
+	}
+
+	private class DoubleClickAdapter extends MouseAdapter {
+		@Override
+		public void mouseClicked(MouseEvent e) {
+			if (e.getClickCount() == 2) {
+				int rowNum = tagsTable.rowAtPoint(e.getPoint());
+				KeyValuePair pair = getModel().getVisibleItem(rowNum);
+				fireItemSelected(pair);
+			}
+		}
+	}
+}
Index: applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/TagsTable.java
===================================================================
--- applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/TagsTable.java	(revision 14330)
+++ applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/TagsTable.java	(revision 14330)
@@ -0,0 +1,42 @@
+package org.openstreetmap.josm.plugins.tageditor.tagspec.ui;
+
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import javax.swing.table.TableColumnModel;
+import javax.swing.table.TableModel;
+
+public class TagsTable extends JTable {
+	/**
+	 * initialize the table 
+	 */
+	protected void init() {				
+		setAutoResizeMode(JTable.AUTO_RESIZE_OFF);		
+		setRowSelectionAllowed(true);
+		setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+	}
+	
+	public TagsTable(TableModel model, TableColumnModel columnModel) {
+		super(model,columnModel);
+		init();
+	}
+	
+	/**
+	 * adjusts the width of the columns for the tag name and the tag value
+	 * to the width of the scroll panes viewport.
+	 * 
+	 * Note: {@see #getPreferredScrollableViewportSize()} did not work as expected
+	 * 
+	 * @param scrollPaneWidth the width of the scroll panes viewport
+	 */
+	public void adjustColumnWidth(int scrollPaneWidth) {
+		TableColumnModel tcm = getColumnModel();
+		int width = scrollPaneWidth;
+		width = width / 2;
+		if (width > 0) {
+			tcm.getColumn(0).setMinWidth(width);
+			tcm.getColumn(0).setMaxWidth(width);
+			tcm.getColumn(1).setMinWidth(width);
+			tcm.getColumn(1).setMaxWidth(width);			
+		}
+	}
+}
Index: applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/TagsTableColumnModel.java
===================================================================
--- applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/TagsTableColumnModel.java	(revision 14330)
+++ applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/TagsTableColumnModel.java	(revision 14330)
@@ -0,0 +1,34 @@
+package org.openstreetmap.josm.plugins.tageditor.tagspec.ui;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import javax.swing.table.DefaultTableColumnModel;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+
+public class TagsTableColumnModel extends DefaultTableColumnModel {
+	
+	protected void createColumns() {
+		TableCellRenderer renderer = new KeyValueCellRenderer();
+		
+		TableColumn col = null;
+		
+		// column 0 - Key   
+		col = new TableColumn(0);
+		col.setHeaderValue(tr("Key"));
+		col.setResizable(true);
+		col.setCellRenderer(renderer);
+		addColumn(col);
+		
+		// column 1 - Value   
+		col = new TableColumn(1);
+		col.setHeaderValue(tr("Value"));
+		col.setResizable(true);
+		col.setCellRenderer(renderer);
+		addColumn(col);
+	}
+
+	public TagsTableColumnModel() {
+		createColumns();
+	}
+}
Index: applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/TagsTableModel.java
===================================================================
--- applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/TagsTableModel.java	(revision 14330)
+++ applications/editors/josm/plugins/tageditor/src/org/openstreetmap/josm/plugins/tageditor/tagspec/ui/TagsTableModel.java	(revision 14330)
@@ -0,0 +1,126 @@
+package org.openstreetmap.josm.plugins.tageditor.tagspec.ui;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.swing.table.AbstractTableModel;
+
+import org.openstreetmap.josm.plugins.tageditor.tagspec.KeyValuePair;
+import org.openstreetmap.josm.plugins.tageditor.tagspec.TagSpecifications;
+
+
+public class TagsTableModel extends AbstractTableModel {
+	
+	static private Logger logger = Logger.getLogger(TagsTableModel.class.getName());
+	
+	private ArrayList<KeyValuePair> items = null;
+	private ArrayList<KeyValuePair> visibleItems = null;
+
+	public TagsTableModel() {
+		items = new ArrayList<KeyValuePair>();
+		visibleItems = new ArrayList<KeyValuePair>();
+	}
+	
+	protected void sort() {
+		Collections.sort(
+			items,
+			new Comparator<KeyValuePair>() {
+
+				@Override
+				public int compare(KeyValuePair self,
+						KeyValuePair other) {
+					int ret =self.getKey().compareToIgnoreCase(other.getKey());
+					
+					if (ret == 0) {
+						return self.getValue().compareToIgnoreCase(other.getValue()); 						
+					} else {
+						return ret;
+					}
+				}				
+			}				
+		);
+	}
+	
+	protected void clear() {
+		items.clear();
+		visibleItems.clear();
+	}
+	
+	public void initFromTagSpecifications() {
+		clear();
+		TagSpecifications spec;
+		
+		try {
+			spec = TagSpecifications.getInstance();
+		} catch(Exception e) {
+			logger.log(Level.SEVERE, "failed to init TagTableModel. Exception:" + e);
+			return; 
+		}
+		
+		items = spec.asList();
+		sort();
+		for(KeyValuePair item : items) {
+			visibleItems.add(item);
+		}
+	}
+	
+	@Override
+	public int getColumnCount() {
+		return 2;
+	}
+
+	@Override
+	public int getRowCount() {
+		return visibleItems.size();
+	}
+
+	@Override
+	public Object getValueAt(int row, int col) {
+		KeyValuePair pair = visibleItems.get(row);
+		switch(col) {
+		case 0: return pair.getKey();
+		case 1: return pair.getValue();
+		default:
+			/* should not happen */
+			throw new IllegalArgumentException("unexpected column number " + col);			 
+		}	
+	}
+	
+	public void filter(String filter) {
+		synchronized(this) {
+			if (filter == null || filter.trim().equals("")) {
+				visibleItems.clear();
+				for(KeyValuePair pair: items) {
+					visibleItems.add(pair);
+				}
+			} else { 
+				visibleItems.clear();
+				filter = filter.toLowerCase();
+				for(KeyValuePair pair: items) {
+					if (pair.getKey().toLowerCase().trim().startsWith(filter)
+						 ||  pair.getValue().toLowerCase().trim().startsWith(filter)) {
+						visibleItems.add(pair);
+					}
+				}	
+			}
+			fireTableDataChanged();
+			fireTableStructureChanged();
+		}
+	}
+
+	@Override
+	public boolean isCellEditable(int rowIndex, int columnIndex) {
+		return false; 
+	}
+
+	public KeyValuePair getVisibleItem(int row) {
+		if (row < 0 || row >= visibleItems.size()) {
+			throw new IndexOutOfBoundsException("row is out of bound: row=" + row);
+		}
+		return visibleItems.get(row);
+	}
+
+}
