Index: /trunk/resources/data/tagging-preset.xsd
===================================================================
--- /trunk/resources/data/tagging-preset.xsd	(revision 17609)
+++ /trunk/resources/data/tagging-preset.xsd	(revision 17610)
@@ -332,5 +332,13 @@
             <annotation>
                 <documentation><![CDATA[
-                    A comma separated list of alternative keys to use for auto completion of <text<.
+                    A comma separated list of alternative keys to use for auto completion of <text>.
+                ]]></documentation>
+            </annotation>
+        </attribute>
+        <attribute name="value_template" type="string">
+            <annotation>
+                <documentation><![CDATA[
+                    A template to generate the value automatically based on other <text> values of this preset.
+                    For instance, "Bus {ref}: {from} → {to}" can be used to generate the name of a bus route relation.
                 ]]></documentation>
             </annotation>
Index: /trunk/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPreset.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPreset.java	(revision 17609)
+++ /trunk/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPreset.java	(revision 17610)
@@ -329,5 +329,6 @@
 
         boolean presetInitiallyMatches = !selected.isEmpty() && selected.stream().allMatch(this);
-        final TaggingPresetItemGuiSupport itemGuiSupport = TaggingPresetItemGuiSupport.create(presetInitiallyMatches, selected);
+        final TaggingPresetItemGuiSupport itemGuiSupport = TaggingPresetItemGuiSupport.create(
+                presetInitiallyMatches, selected, this::getChangedTags);
         JPanel items = new JPanel(new GridBagLayout());
         TaggingPresetItem previous = null;
@@ -363,4 +364,7 @@
         tb.setFocusable(false);
         p.add(tb, GBC.std(1, 0).anchor(GBC.LINE_END));
+
+        // Trigger initial updates
+        itemGuiSupport.fireItemValueModified(null, null, null);
         return p;
     }
Index: /trunk/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetItemGuiSupport.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetItemGuiSupport.java	(revision 17609)
+++ /trunk/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetItemGuiSupport.java	(revision 17610)
@@ -3,7 +3,15 @@
 
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Tag;
+import org.openstreetmap.josm.data.osm.search.SearchCompiler;
+import org.openstreetmap.josm.gui.tagging.presets.items.KeyedItem;
+import org.openstreetmap.josm.tools.ListenerList;
+import org.openstreetmap.josm.tools.template_engine.TemplateEngineDataProvider;
 
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
 
 /**
@@ -12,12 +20,30 @@
  * @since xxx
  */
-public class TaggingPresetItemGuiSupport {
+public final class TaggingPresetItemGuiSupport implements TemplateEngineDataProvider {
 
     private final Collection<OsmPrimitive> selected;
     private final boolean presetInitiallyMatches;
+    private final Supplier<Collection<Tag>> changedTagsSupplier;
+    private final ListenerList<ChangeListener> listeners = ListenerList.create();
 
-    private TaggingPresetItemGuiSupport(boolean presetInitiallyMatches, Collection<OsmPrimitive> selected) {
+    /**
+     * Interface to notify listeners that a preset item input as changed.
+     * @since xxx
+     */
+    public interface ChangeListener {
+        /**
+         * Notifies this listener that a preset item input as changed.
+         * @param source the source of this event
+         * @param key the tag key
+         * @param newValue the new tag value
+         */
+        void itemValueModified(TaggingPresetItem source, String key, String newValue);
+    }
+
+    private TaggingPresetItemGuiSupport(
+            boolean presetInitiallyMatches, Collection<OsmPrimitive> selected, Supplier<Collection<Tag>> changedTagsSupplier) {
         this.selected = selected;
         this.presetInitiallyMatches = presetInitiallyMatches;
+        this.changedTagsSupplier = changedTagsSupplier;
     }
 
@@ -47,6 +73,7 @@
      * @return the new {@code TaggingPresetItemGuiSupport}
      */
-    public static TaggingPresetItemGuiSupport create(boolean presetInitiallyMatches, Collection<OsmPrimitive> selected) {
-        return new TaggingPresetItemGuiSupport(presetInitiallyMatches, selected);
+    public static TaggingPresetItemGuiSupport create(
+            boolean presetInitiallyMatches, Collection<OsmPrimitive> selected, Supplier<Collection<Tag>> changedTagsSupplier) {
+        return new TaggingPresetItemGuiSupport(presetInitiallyMatches, selected, changedTagsSupplier);
     }
 
@@ -58,6 +85,45 @@
      * @return the new {@code TaggingPresetItemGuiSupport}
      */
-    public static TaggingPresetItemGuiSupport create(boolean presetInitiallyMatches, OsmPrimitive... selected) {
-        return new TaggingPresetItemGuiSupport(presetInitiallyMatches, Arrays.asList(selected));
+    public static TaggingPresetItemGuiSupport create(
+            boolean presetInitiallyMatches, OsmPrimitive... selected) {
+        return new TaggingPresetItemGuiSupport(presetInitiallyMatches, Arrays.asList(selected), Collections::emptyList);
+    }
+
+    @Override
+    public Collection<String> getTemplateKeys() {
+        return changedTagsSupplier.get().stream().map(Tag::getKey).collect(Collectors.toList());
+    }
+
+    @Override
+    public Object getTemplateValue(String key, boolean special) {
+        return changedTagsSupplier.get().stream()
+                .filter(tag -> key.equals(tag.getKey()))
+                .findFirst().map(Tag::getValue).orElseGet(() -> {
+                    KeyedItem.Usage usage = KeyedItem.determineTextUsage(getSelected(), key);
+                    return usage.hasUniqueValue() ? usage.getFirst() : null;
+                });
+    }
+
+    @Override
+    public boolean evaluateCondition(SearchCompiler.Match condition) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Adds a new change listener
+     * @param listener the listener to add
+     */
+    public void addListener(ChangeListener listener) {
+        listeners.addListener(listener);
+    }
+
+    /**
+     * Notifies all listeners that a preset item input as changed.
+     * @param source the source of this event
+     * @param key the tag key
+     * @param newValue the new tag value
+     */
+    public void fireItemValueModified(TaggingPresetItem source, String key, String newValue) {
+        listeners.fireEvent(e -> e.itemValueModified(source, key, newValue));
     }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/tagging/presets/items/KeyedItem.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/tagging/presets/items/KeyedItem.java	(revision 17609)
+++ /trunk/src/org/openstreetmap/josm/gui/tagging/presets/items/KeyedItem.java	(revision 17610)
@@ -96,6 +96,8 @@
     /**
      * Usage information on a key
-     */
-    protected static class Usage {
+     *
+     * TODO merge with {@link org.openstreetmap.josm.data.osm.TagCollection}
+     */
+    public static class Usage {
         /**
          * A set of values that were used for this key.
@@ -139,5 +141,11 @@
     }
 
-    protected static Usage determineTextUsage(Collection<OsmPrimitive> sel, String key) {
+    /**
+     * Computes the tag usage for the given key from the given primitives
+     * @param sel the primitives
+     * @param key the key
+     * @return the tag usage
+     */
+    public static Usage determineTextUsage(Collection<OsmPrimitive> sel, String key) {
         Usage returnValue = new Usage();
         for (OsmPrimitive s : sel) {
Index: /trunk/src/org/openstreetmap/josm/gui/tagging/presets/items/Text.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/tagging/presets/items/Text.java	(revision 17609)
+++ /trunk/src/org/openstreetmap/josm/gui/tagging/presets/items/Text.java	(revision 17610)
@@ -4,4 +4,5 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.awt.Color;
 import java.awt.Component;
 import java.awt.GridBagLayout;
@@ -21,4 +22,6 @@
 import javax.swing.JPanel;
 import javax.swing.JToggleButton;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
 
 import org.openstreetmap.josm.data.osm.Tag;
@@ -32,4 +35,8 @@
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.Utils;
+import org.openstreetmap.josm.tools.template_engine.ParseError;
+import org.openstreetmap.josm.tools.template_engine.TemplateEntry;
+import org.openstreetmap.josm.tools.template_engine.TemplateParser;
+import org.xml.sax.SAXException;
 
 /**
@@ -59,4 +66,5 @@
 
     private JComponent value;
+    private transient TemplateEntry valueTemplate;
 
     @Override
@@ -114,4 +122,6 @@
         }
         initializeLocaleText(null);
+
+        setupListeners(textField, support);
 
         // if there's an auto_increment setting, then wrap the text field
@@ -229,3 +239,40 @@
         return Collections.singleton(default_);
     }
+
+    public void setValue_template(String pattern) throws SAXException {
+        try {
+            this.valueTemplate = new TemplateParser(pattern).parse();
+        } catch (ParseError e) {
+            Logging.error("Error while parsing " + pattern + ": " + e.getMessage());
+            throw new SAXException(e);
+        }
+    }
+
+    private void setupListeners(AutoCompletingTextField textField, TaggingPresetItemGuiSupport support) {
+        textField.getDocument().addDocumentListener(new DocumentListener() {
+            @Override
+            public void insertUpdate(DocumentEvent e) {
+                support.fireItemValueModified(Text.this, key, textField.getText());
+            }
+
+            @Override
+            public void removeUpdate(DocumentEvent e) {
+                support.fireItemValueModified(Text.this, key, textField.getText());
+            }
+
+            @Override
+            public void changedUpdate(DocumentEvent e) {
+                support.fireItemValueModified(Text.this, key, textField.getText());
+            }
+        });
+
+        if (valueTemplate != null) {
+            textField.setForeground(Color.BLUE);
+            support.addListener((source, key, newValue) -> {
+                if (source != this) {
+                    textField.setItem(valueTemplate.getText(support));
+                }
+            });
+        }
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/tools/template_engine/TemplateEntry.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/template_engine/TemplateEntry.java	(revision 17609)
+++ /trunk/src/org/openstreetmap/josm/tools/template_engine/TemplateEntry.java	(revision 17610)
@@ -10,4 +10,16 @@
  */
 public interface TemplateEntry {
+
+    /**
+     * Execute this template by generating text for a given data provider.
+     * @param dataProvider the data provider from which information should be compiled to a string
+     * @return the generated text
+     */
+    default String getText(TemplateEngineDataProvider dataProvider) {
+        StringBuilder sb = new StringBuilder();
+        appendText(sb, dataProvider);
+        return sb.toString();
+    }
+
     /**
      * Execute this template by generating text for a given data provider.
