Index: applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/UtilsPlugin2.java
===================================================================
--- applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/UtilsPlugin2.java	(revision 30202)
+++ applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/UtilsPlugin2.java	(revision 30337)
@@ -3,4 +3,5 @@
 
 
+import com.sun.org.apache.xpath.internal.operations.Mult;
 import javax.swing.JMenu;
 import javax.swing.JMenuItem;
@@ -28,4 +29,5 @@
 import org.openstreetmap.josm.plugins.utilsplugin2.customurl.UtilsPluginPreferences;
 import org.openstreetmap.josm.plugins.utilsplugin2.latlon.LatLonAction;
+import org.openstreetmap.josm.plugins.utilsplugin2.multitagger.MultiTagAction;
 import org.openstreetmap.josm.plugins.utilsplugin2.replacegeometry.ReplaceGeometryAction;
 import org.openstreetmap.josm.plugins.utilsplugin2.search.UtilsUnaryMatchFactory;
@@ -63,4 +65,5 @@
     JMenuItem wiki;
     JMenuItem latlon;
+    JMenuItem multiTag;
     
     JMenuItem replaceGeometry;
@@ -119,5 +122,6 @@
         
         selectURL = MainMenu.add(dataMenu, new ChooseURLAction());
-	
+        multiTag = MainMenu.add(dataMenu, new MultiTagAction());
+        
         // register search operators
         SearchCompiler.addMatchFactory(new UtilsUnaryMatchFactory());
@@ -155,4 +159,5 @@
 
         drawArc.setEnabled(enabled);
+        multiTag.setEnabled(enabled);
     }
     
Index: applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/multitagger/MultiTagAction.java
===================================================================
--- applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/multitagger/MultiTagAction.java	(revision 30337)
+++ applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/multitagger/MultiTagAction.java	(revision 30337)
@@ -0,0 +1,46 @@
+package org.openstreetmap.josm.plugins.utilsplugin2.multitagger;
+
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.Collection;
+import org.openstreetmap.josm.tools.Shortcut;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+public final class MultiTagAction extends JosmAction {
+
+    MultiTagDialog dlg;
+    
+    public MultiTagAction() {
+        super(tr("Tag multiple objects [alpha]"), "bug", tr("Edit tags of object list in table"),
+                Shortcut.registerShortcut("multitag", tr("Edit: {0}", tr("Tag multiple objects")), KeyEvent.VK_T, Shortcut.CTRL), true, true);
+        putValue("help", ht("/Action/MultiTag"));
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        if (!isEnabled())
+            return;
+        dlg = new MultiTagDialog();
+        dlg.selectionChanged(getCurrentDataSet().getSelected());
+        dlg.showDialog();
+    }
+
+
+    @Override
+    protected void updateEnabledState() {
+        setEnabled(getEditLayer()!=null);
+    }
+    
+    @Override
+    protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
+        setEnabled(getEditLayer()!=null);
+        if (dlg!=null && dlg.isVisible()) {
+            dlg.selectionChanged(selection);
+        }
+    }
+    
+}
Index: applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/multitagger/MultiTagDialog.java
===================================================================
--- applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/multitagger/MultiTagDialog.java	(revision 30337)
+++ applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/multitagger/MultiTagDialog.java	(revision 30337)
@@ -0,0 +1,255 @@
+package org.openstreetmap.josm.plugins.utilsplugin2.multitagger;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EventObject;
+import java.util.LinkedList;
+import java.util.List;
+import javax.swing.AbstractAction;
+import static javax.swing.Action.SHORT_DESCRIPTION;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.KeyStroke;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.CellEditorListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.TableCellEditor;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.AutoScaleAction;
+import org.openstreetmap.josm.actions.search.SearchAction;
+import org.openstreetmap.josm.data.SelectionChangedListener;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.preferences.StringProperty;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.tagging.TagCellEditor;
+import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
+import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
+import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;
+import org.openstreetmap.josm.gui.util.HighlightHelper;
+import org.openstreetmap.josm.gui.util.TableHelper;
+import org.openstreetmap.josm.gui.widgets.HistoryComboBox;
+import org.openstreetmap.josm.tools.GBC;
+import static org.openstreetmap.josm.tools.I18n.tr;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.WindowGeometry;
+
+/**
+ * Dialog fo redinitg multible object tags
+ */
+public class MultiTagDialog extends ExtendedDialog implements SelectionChangedListener {
+
+    private final MultiTaggerTableModel tableModel = new MultiTaggerTableModel();
+    private final JTable tbl = new JTable(tableModel);
+    //
+    private final HighlightHelper highlightHelper = new HighlightHelper();
+    private final HistoryComboBox tagSetSelector = new HistoryComboBox();
+    
+    private static final String HISTORY_KEY = "utilsplugin2.multitaghistory";
+    String defaultHistory[] = {"addr:street, addr:housenumber, building",
+        "highway, name, ${id}, ${length}, ${type}",
+        "name name:en name:ru name:de"};
+    private final StringProperty LAST_TAGS = new StringProperty("utilsplugin2.multitag.last", defaultHistory[0]);
+    TagCellEditor cellEditor;
+    
+            
+    public MultiTagDialog() {
+        super(Main.parent,  tr("Edit tags"), new String[]{tr("Ok"), tr("Cancel")}, false);
+        JPanel pnl = new JPanel(new GridBagLayout());
+        tbl.setFillsViewportHeight(true);
+        tbl.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        tbl.addMouseListener(tableMouseAdapter);
+        tbl.setRowSelectionAllowed(true);
+        tbl.setColumnSelectionAllowed(true);
+        tbl.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
+        loadHistory();
+        
+        tagSetSelector.addItemListener(tagSetChanger);
+        tagSetSelector.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
+           .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, true), "applyTagSet");
+        tagSetSelector.getActionMap().put("applyTagSet", tagSetChanger);
+        
+        tagSetChanger.itemStateChanged(null);
+        pnl.add(tagSetSelector,GBC.std().fill(GBC.HORIZONTAL));
+        pnl.add(new JButton(new DeleteFromHistoryAction()),GBC.std());
+        pnl.add(new JButton(new FindMatchingAction()),GBC.eol());
+        pnl.add(tbl.getTableHeader(),GBC.eop().fill(GBC.HORIZONTAL));
+
+        pnl.add(new JScrollPane(tbl), GBC.eol().fill(GBC.BOTH));
+        tbl.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
+        tbl.getSelectionModel().addListSelectionListener(selectionListener);
+        setContent(pnl);
+        setDefaultButton(-1);
+        
+        WindowGeometry defaultGeometry = WindowGeometry.centerInWindow(Main.parent, new Dimension(500, 500));
+        setRememberWindowGeometry(getClass().getName() + ".geometry", defaultGeometry);
+        
+
+    }
+
+    private void loadHistory() {
+        List<String> cmtHistory = new LinkedList<String>(
+                Main.pref.getCollection(HISTORY_KEY, Arrays.asList(defaultHistory)));
+        Collections.reverse(cmtHistory);
+        tagSetSelector.setPossibleItems(cmtHistory);
+        tagSetSelector.setText(LAST_TAGS.get());
+    }
+
+    @Override
+    protected void buttonAction(int buttonIndex, ActionEvent evt) {
+        highlightHelper.clear();
+        tbl.getSelectionModel().removeListSelectionListener(selectionListener);
+        super.buttonAction(buttonIndex, evt); 
+    }
+    
+    @Override
+    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
+        tableModel.selectionChanged(newSelection);
+    }
+    
+    private OsmPrimitive getSelectedPrimitive() {
+        int idx = tbl.getSelectedRow();
+        if (idx>=0) {
+            return tableModel.getPrimitiveAt(tbl.convertRowIndexToModel(idx));
+        } else {
+            return null;
+        }
+    }
+    
+    private final MouseAdapter tableMouseAdapter = new MouseAdapter() {
+        @Override
+        public void mouseClicked(MouseEvent e) {
+            if (e.getButton() == MouseEvent.BUTTON3) {
+                AutoScaleAction.zoomTo(Collections.singletonList(getSelectedPrimitive()));
+            }
+        }
+        
+    };
+    private final ListSelectionListener selectionListener = new ListSelectionListener() {
+        @Override
+        public void valueChanged(ListSelectionEvent e) {
+            OsmPrimitive p = getSelectedPrimitive();
+            if (p != null && Main.isDisplayingMapView() ) {
+                if (highlightHelper.highlightOnly(p)) {
+                    Main.map.mapView.repaint();
+                }
+            }
+        }
+    };
+    
+    public void setAutoCompletion(boolean enable){
+        if (!enable) {
+            
+        }
+            
+        OsmDataLayer l = Main.main.getEditLayer();
+        AutoCompletionManager autocomplete = l.data.getAutoCompletionManager();
+        AutoCompletionList acList = new AutoCompletionList();
+          
+//        TagCellEditor editor = ((TagCellEditor) tagTable.getColumnModel().getColumn(0).getCellEditor());
+//        editor.setAutoCompletionManager(autocomplete);
+//        editor.setAutoCompletionList(acList);
+//        editor = ((TagCellEditor) tagTable.getColumnModel().getColumn(1).getCellEditor());
+//        editor.setAutoCompletionManager(autocomplete);
+//        editor.setAutoCompletionList(acList);
+    }
+    
+    private final TagSetChanger tagSetChanger = new TagSetChanger();
+
+    private void initAutocompletion() {
+        OsmDataLayer l = Main.main.getEditLayer();
+        AutoCompletionManager autocomplete = l.data.getAutoCompletionManager();
+        for (int i=0; i<tableModel.mainTags.length; i++) {
+                if (tableModel.isSpecialTag[i]) continue;
+                AutoCompletingTextField tf = new AutoCompletingTextField();
+                AutoCompletionList acList = new AutoCompletionList();
+                autocomplete.populateWithTagValues(acList, tableModel.mainTags[i]);
+                tf.setAutoCompletionList(acList);
+                tbl.getColumnModel().getColumn(i).setCellEditor(tf);
+        }
+    }
+
+    private class DeleteFromHistoryAction extends AbstractAction {
+        public DeleteFromHistoryAction() {
+            super("", ImageProvider.get("dialogs","delete"));
+            putValue(SHORT_DESCRIPTION, tr("Delete from history"));
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            String txt = tagSetSelector.getText();
+            System.out.println(txt);
+            List<String> history = tagSetSelector.getHistory();
+            history.remove(txt);
+            if (history.isEmpty()) {
+                history = Arrays.asList(defaultHistory);
+            }
+                
+            Main.pref.putCollection(HISTORY_KEY, history);
+            if (!history.isEmpty()) {
+                LAST_TAGS.put(history.get(0));
+            } 
+            loadHistory();
+        }
+    }
+   
+    private class FindMatchingAction extends AbstractAction {
+        public FindMatchingAction() {
+            super("", ImageProvider.get("dialogs","search"));
+            putValue(SHORT_DESCRIPTION, tr("Find primitives with these tags"));
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            SearchAction.search(tableModel.getSearchExpression(), SearchAction.SearchMode.replace);
+        }
+    }
+   
+    private class TagSetChanger extends AbstractAction implements ItemListener {
+        String oldTags;
+        
+ 
+        @Override
+        public void itemStateChanged(ItemEvent e) {
+            // skip text-changing enevts, we need only combobox-selecting ones
+            if (tagSetSelector.getSelectedIndex()<0) return;
+            actionPerformed(null);
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            String s = tagSetSelector.getText();
+            if (s==null || s.isEmpty() || s.equals(oldTags)) return;
+            oldTags = s;
+            Main.info("Multitagger tags="+s);
+            tagSetSelector.addCurrentItemToHistory();
+            Main.pref.putCollection(HISTORY_KEY, tagSetSelector.getHistory());
+            LAST_TAGS.put(tagSetSelector.getText());
+
+            tableModel.setupColumnsFromText(s);
+           
+            tbl.createDefaultColumnsFromModel();
+            tbl.setAutoCreateRowSorter(true);
+            for (int i=0; i<tableModel.getColumnCount(); i++) {
+                TableHelper.adjustColumnWidth(tbl, i, 100);
+            }
+            initAutocompletion();
+            tableModel.fireTableDataChanged();
+        }
+    };
+    
+}
Index: applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/multitagger/MultiTaggerTableModel.java
===================================================================
--- applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/multitagger/MultiTaggerTableModel.java	(revision 30337)
+++ applications/editors/josm/plugins/utilsplugin2/src/org/openstreetmap/josm/plugins/utilsplugin2/multitagger/MultiTaggerTableModel.java	(revision 30337)
@@ -0,0 +1,140 @@
+// License: GPL. Copyright 2013 by Alexei Kasatkin
+
+package org.openstreetmap.josm.plugins.utilsplugin2.multitagger;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import javax.swing.table.AbstractTableModel;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.ChangePropertyCommand;
+import org.openstreetmap.josm.data.SelectionChangedListener;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.tools.Geometry;
+
+/**
+ *
+ */
+public class MultiTaggerTableModel extends AbstractTableModel implements SelectionChangedListener {
+
+    ArrayList<OsmPrimitive> list = new ArrayList<OsmPrimitive>(50);
+    String mainTags[] = new String[]{};
+    boolean isSpecialTag[] = new boolean[]{};
+
+    
+    @Override
+    public int getRowCount() {
+        return list.size();
+    }
+
+    @Override
+    public int getColumnCount() {
+        return mainTags.length;
+    }
+
+    @Override
+    public Object getValueAt(int rowIndex, int columnIndex) {
+        if (!isSpecialTag[columnIndex]) {
+            return list.get(rowIndex).get(mainTags[columnIndex]);
+        }
+        String var = mainTags[columnIndex];
+        OsmPrimitive p = list.get(rowIndex);
+        if (var.equals("id")) {
+            return String.valueOf(p.getUniqueId());
+        } else if (var.equals("type")) {
+            return OsmPrimitiveType.from(p).toString().substring(0,1);
+        } else if (var.equals("area")) {
+            if (p.getType() == OsmPrimitiveType.CLOSEDWAY) {
+                return String.format("%.1f", Geometry.closedWayArea((Way) p));
+            } else {
+                return tr("not closed");
+            }
+        } else if (var.equals("length")) {
+            if (p instanceof Way) {
+                return String.format("%.1f", ((Way) p).getLength());
+            }
+        } 
+        return "";
+    }
+
+    @Override
+    public Class<?> getColumnClass(int columnIndex) {
+        return String.class;
+    }
+   
+    @Override
+    public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
+        Main.info("new selection: n="+newSelection.size());
+        list.clear();
+        list.addAll(newSelection);
+        fireTableDataChanged();
+    }
+
+    @Override
+    public boolean isCellEditable(int rowIndex, int columnIndex) {
+        return !isSpecialTag[columnIndex];
+    }
+
+    @Override
+    public void setValueAt(Object value, int rowIndex, int columnIndex) {
+        if (isSpecialTag[columnIndex]) return;
+        if (columnIndex >= getColumnCount() || rowIndex >= getRowCount()) return;
+        String val = ((String) value).trim();
+        OsmPrimitive sel = list.get(rowIndex);
+        String key = mainTags[columnIndex];
+        String newValue = sel.get(key);
+        if (newValue == null) newValue="";
+        if (!val.equals(newValue)) {
+            Main.main.undoRedo.add(new ChangePropertyCommand(sel, key, (String) value));
+        }
+    }
+    
+
+    @Override
+    public String getColumnName(int column) {
+        return mainTags[column];
+    }
+    
+    public OsmPrimitive getPrimitiveAt(int number) {
+        if (number<0 || number>=list.size()) {
+            return null;
+        } else {
+            return list.get(number);
+        }
+    }
+    
+    public void setupColumnsFromText(String txt) {
+        String[] tags = txt.trim().split("[\\s,]+");
+        mainTags = new String[tags.length];
+        isSpecialTag = new boolean[tags.length];
+        int i=0;
+        for (String t: tags) {
+            if (t.startsWith("${") && t.endsWith("}")) {
+                mainTags[i] = t.substring(2, t.length()-1).trim();
+                isSpecialTag[i] = true;
+            } else {
+                mainTags[i] = t;
+                isSpecialTag[i] = false;
+            }
+            i++;
+        }
+    }
+
+    public String getSearchExpression() {
+        StringBuilder sb = new StringBuilder();
+        boolean notFirst = false;
+        for (int i=0; i<mainTags.length; i++) {
+            if (!isSpecialTag[i]) {
+                 if (notFirst) sb.append(" | ");
+                 sb.append("\"");
+                 sb.append(mainTags[i]);
+                 sb.append("\":");
+                 notFirst = true;
+            }
+        };
+        return sb.toString();
+    }
+}
