diff --git src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesCellRenderer.java src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesCellRenderer.java
index 58f9de53bc..06953cef48 100644
--- src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesCellRenderer.java
+++ src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesCellRenderer.java
@@ -16,6 +16,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
 
 import javax.swing.JLabel;
 import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.SwingConstants;
 import javax.swing.UIManager;
 import javax.swing.table.DefaultTableCellRenderer;
 import javax.swing.table.TableCellRenderer;
@@ -132,6 +134,18 @@ public class PropertiesCellRenderer extends DefaultTableCellRenderer {
             }
             ((JLabel) c).putClientProperty("html.disable", enableHTML ? null : Boolean.TRUE); // Fix #8730
             ((JLabel) c).setText(str);
+
+            // Needed for multiline values and doesn't hurt single line ones
+            if (column == 0) {
+                ((JLabel) c).setVerticalAlignment(SwingConstants.TOP);
+            } else if (!enableHTML) {
+                JTextArea jta = new JTextArea(str);
+                jta.setForeground(c.getForeground());
+                jta.setBackground(c.getBackground());
+
+                c = jta;
+            }
+
             if (DISCARDABLE.get()) {
                 String key = null;
                 if (column == 0) {
diff --git src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java
index de272a5f72..ec4afca39c 100644
--- src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java
+++ src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java
@@ -696,10 +696,11 @@ implements DataSelectionListener, ActiveLayerChangeListener, PropertyChangeListe
     }
 
     private void autoresizeTagTable() {
+        // resize table's rows and columns to fit content
         if (Boolean.TRUE.equals(PROP_AUTORESIZE_TAGS_TABLE.get())) {
-            // resize table's columns to fit content
             TableHelper.computeColumnsWidth(tagTable);
         }
+        TableHelper.computeRowsHeight(tagTable);
     }
 
     private void updateUi(IPrimitive primitive) {
diff --git src/org/openstreetmap/josm/gui/dialogs/properties/TagEditHelper.java src/org/openstreetmap/josm/gui/dialogs/properties/TagEditHelper.java
index 8124c7b60a..b6194c4c30 100644
--- src/org/openstreetmap/josm/gui/dialogs/properties/TagEditHelper.java
+++ src/org/openstreetmap/josm/gui/dialogs/properties/TagEditHelper.java
@@ -6,6 +6,8 @@ import static org.openstreetmap.josm.tools.I18n.tr;
 import static org.openstreetmap.josm.tools.I18n.trn;
 
 import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.awt.Color;
 import java.awt.Component;
 import java.awt.ComponentOrientation;
 import java.awt.Container;
@@ -42,6 +44,7 @@ import java.util.stream.IntStream;
 
 import javax.swing.AbstractAction;
 import javax.swing.Action;
+import javax.swing.BorderFactory;
 import javax.swing.Box;
 import javax.swing.ButtonGroup;
 import javax.swing.ImageIcon;
@@ -54,6 +57,7 @@ import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
 import javax.swing.JRadioButtonMenuItem;
+import javax.swing.JScrollPane;
 import javax.swing.JTable;
 import javax.swing.KeyStroke;
 import javax.swing.ListCellRenderer;
@@ -96,6 +100,7 @@ import org.openstreetmap.josm.gui.tagging.presets.items.KeyedItem;
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.util.WindowGeometry;
 import org.openstreetmap.josm.gui.widgets.JosmListCellRenderer;
+import org.openstreetmap.josm.gui.widgets.JosmTextArea;
 import org.openstreetmap.josm.gui.widgets.OrientationAction;
 import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
 import org.openstreetmap.josm.io.XmlWriter;
@@ -112,6 +117,17 @@ import org.openstreetmap.josm.tools.Utils;
  * @since 5633
  */
 public class TagEditHelper {
+    /**
+     * Default for {@link #MULTILINE_TAGS}.
+     * Values taken from fields marked as textareas in https://github.com/openstreetmap/id-tagging-schema
+     */
+    private static final List<String> DEFAULT_MULTILINE_TAGS = Arrays.asList("defibrillator:location", "description", "fixme",
+                                                                             "note", "comment", "inscription", "label");
+
+    /**
+     * Keys of tags for which line breaks are valid characters
+     */
+    public static final ListProperty MULTILINE_TAGS = new ListProperty("properties.multiline-tags", DEFAULT_MULTILINE_TAGS);
 
     private final JTable tagTable;
     private final DefaultTableModel tagData;
@@ -461,8 +477,12 @@ public class TagEditHelper {
      * @return the edited item with whitespaces removed
      * @since 18173
      */
-    public static String getEditItem(AutoCompComboBox<AutoCompletionItem> cb) {
-        return Utils.removeWhiteSpaces(cb.getEditorItemAsString());
+    public static String getEditItem(JComponent cb) {
+        if (cb instanceof JosmTextArea) {
+            return Utils.removeWhiteSpaces(Utils.MULTILINE_WHITESPACE_PATTERN, ((JosmTextArea) cb).getText());
+        } else {
+            return Utils.removeWhiteSpaces(((AutoCompComboBox<AutoCompletionItem>) cb).getEditorItemAsString());
+        }
     }
 
     /**
@@ -580,23 +600,52 @@ public class TagEditHelper {
 
             final String selection = m.size() != 1 ? KeyedItem.DIFFERENT_I18N : m.entrySet().iterator().next().getKey();
 
-            values = new AutoCompComboBox<>();
-            values.getModel().setComparator(Comparator.naturalOrder());
-            values.setRenderer(new TEHListCellRenderer(values, values.getRenderer(), valueCount.get(key)));
-            values.setEditable(true);
-            values.setPrototypeDisplayValue(new AutoCompletionItem(DUMMY));
-            values.getModel().addAllElements(valueList);
-            values.setSelectedItemText(selection);
+            textCards = new CardLayout();
+            textPanel = new JPanel(textCards);
+
+            // create a combobox for single line tags
+            AutoCompComboBox<AutoCompletionItem> vals = new AutoCompComboBox<>();
+            vals.getModel().setComparator(Comparator.naturalOrder());
+            vals.setRenderer(new TEHListCellRenderer(vals, vals.getRenderer(), valueCount.get(key)));
+            vals.setEditable(true);
+            vals.setPrototypeDisplayValue(new AutoCompletionItem(DUMMY));
+            vals.getModel().addAllElements(valueList);
+            vals.setSelectedItemText(selection);
+            values = vals;
+
+            // create a sub-panel to properly size the single line combobox
+            JPanel singleLinePanel = new JPanel(new GridBagLayout());
+            singleLinePanel.setBorder(BorderFactory.createLineBorder(Color.gray));
+            singleLinePanel.add(values, GBC.eol().fill(GridBagConstraints.HORIZONTAL).anchor(GridBagConstraints.NORTH).weight(1, 1));
+
+            // create a text area for multiline tags
+            values_multiline = new JosmTextArea();
+            values_multiline.setText(selection);
+            // Scroll multiline text area back to the top
+            values_multiline.setCaretPosition(0);
+            values_multiline.setRows(5);
+            values_multiline.setFont(vals.getFont());
+
+            // add both cards
+            textPanel.add(singleLinePanel, SINGLELINE);
+            textPanel.add(new JScrollPane(values_multiline), MULTILINE);
 
             p.add(Box.createVerticalStrut(5), GBC.eol());
-            p.add(new JLabel(tr("Value")), GBC.std());
+            p.add(new JLabel(tr("Value")), GBC.std().anchor(GridBagConstraints.NORTH));
             p.add(Box.createHorizontalStrut(10), GBC.std());
-            p.add(values, GBC.eol().fill(GridBagConstraints.HORIZONTAL));
+            p.add(textPanel, GBC.eol().fill(GridBagConstraints.HORIZONTAL));
             p.add(Box.createVerticalStrut(2), GBC.eol());
 
             p.applyComponentOrientation(OrientationAction.getDefaultComponentOrientation());
             keys.applyComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
             values.applyComponentOrientation(OrientationAction.getNamelikeOrientation(keys.getText()));
+            values_multiline.applyComponentOrientation(values.getComponentOrientation());
+
+            // immediately show the multiline card if the key is multiline
+            if (TagEditHelper.MULTILINE_TAGS.get().contains(key)) {
+                multiline = true;
+                textCards.show(textPanel, MULTILINE);
+            }
 
             setContent(mainPanel, false);
 
@@ -615,7 +664,7 @@ public class TagEditHelper {
 
         @Override
         public void performTagEdit() {
-            String value = getEditItem(values);
+            String value = getEditItem(multiline ? values_multiline : values);
             value = Normalizer.normalize(value, Normalizer.Form.NFC);
             if (value.isEmpty()) {
                 value = null; // delete the key
@@ -628,7 +677,8 @@ public class TagEditHelper {
             }
             if (key.equals(newkey) && KeyedItem.DIFFERENT_I18N.equals(value))
                 return;
-            if (value != null && key.equals(getEditItem(keys)) && m.size() == 1 && m.containsKey(getEditItem(values)))
+            if (value != null && key.equals(getEditItem(keys)) && m.size() == 1
+                    && m.containsKey(getEditItem(multiline ? values_multiline : values)))
                 return; // see #22814: avoid to create a command that wouldn't change anything
             if (key.equals(newkey) || value == null) {
                 UndoRedoHandler.getInstance().add(new ChangePropertyCommand(sel, newkey, value));
@@ -669,8 +719,21 @@ public class TagEditHelper {
     }
 
     protected abstract class AbstractTagsDialog extends ExtendedDialog implements AutoCompListener, FocusListener, PopupMenuListener {
+        // name to select which card to show for the value field
+        protected static final String SINGLELINE = "singleline";
+        protected static final String MULTILINE = "multiline";
+
         protected AutoCompComboBox<AutoCompletionItem> keys;
+
+        // maintain two separate components for single line and multiline values
         protected AutoCompComboBox<AutoCompletionItem> values;
+        protected JosmTextArea values_multiline;
+
+        // select which of the two to show with a card layout
+        protected CardLayout textCards;
+        protected JPanel textPanel;
+        protected boolean multiline;
+
         protected boolean initialFocusOnKey = true;
         /**
          * The 'values' model is currently holding values for this key. Used for lazy-loading of values.
@@ -800,8 +863,36 @@ public class TagEditHelper {
         public void focusLost(FocusEvent e) {
             // update the values combobox orientation if the key changed
             values.applyComponentOrientation(OrientationAction.getNamelikeOrientation(keys.getText()));
+            values_multiline.applyComponentOrientation(values.getComponentOrientation());
             // update the auto-completion setting for the given tag key
             values.setAutocompleteEnabled(autocompleteValuesForKey(keys.getText()));
+
+            boolean previousMultiline = multiline;
+            if (TagEditHelper.MULTILINE_TAGS.get().contains(keys.getText())) {
+                multiline = true;
+            } else {
+                multiline = false;
+
+            }
+
+            if (previousMultiline != multiline) {
+                // update which values field is shown and sync text between them
+                if (multiline) {
+                    values_multiline.setText(values.getText());
+                    textCards.show(textPanel, MULTILINE);
+                } else {
+                    values.setText(values_multiline.getText());
+                    textCards.show(textPanel, SINGLELINE);
+                }
+
+                // user focused one values field after changing to/from a multiline key.
+                // redirect the focus request to the text field that's now shown.
+                if (e.getOppositeComponent() == values.getEditorComponent()) {
+                    SwingUtilities.invokeLater(() -> values_multiline.requestFocusInWindow());
+                } else if (e.getOppositeComponent() == values_multiline) {
+                    SwingUtilities.invokeLater(() -> values.getEditorComponent().requestFocusInWindow());
+                }
+            }
         }
 
         protected void updateOkButtonIcon() {
@@ -881,13 +972,31 @@ public class TagEditHelper {
             mainPanel.add(keys, GBC.eop().fill(GridBagConstraints.HORIZONTAL));
             mainPanel.add(new JLabel(tr("Choose a value")), GBC.eol());
 
+            textCards = new CardLayout();
+            textPanel = new JPanel(textCards);
+
+            // create a combobox for single line tags
             values = new AutoCompComboBox<>();
             values.setPrototypeDisplayValue(new AutoCompletionItem(DUMMY));
             values.setEditable(true);
             values.getModel().setComparator(Comparator.naturalOrder());
             values.setAutocompleteEnabled(AUTOCOMPLETE_VALUES.get());
 
-            mainPanel.add(values, GBC.eop().fill(GridBagConstraints.HORIZONTAL));
+            // create a sub-panel to properly size the single line combobox
+            JPanel singleLinePanel = new JPanel(new GridBagLayout());
+            singleLinePanel.setBorder(BorderFactory.createLineBorder(Color.gray));
+            singleLinePanel.add(values, GBC.eol().fill(GridBagConstraints.HORIZONTAL).anchor(GridBagConstraints.NORTH).weight(1, 1));
+
+            // create a text area for multiline tags
+            values_multiline = new JosmTextArea();
+            values_multiline.setRows(5);
+            values_multiline.setFont(values.getFont());
+
+            // add both cards
+            textPanel.add(singleLinePanel, SINGLELINE);
+            textPanel.add(new JScrollPane(values_multiline), MULTILINE);
+
+            mainPanel.add(textPanel, GBC.eop().fill(GridBagConstraints.HORIZONTAL));
 
             cacheRecentTags();
             autocomplete = AutoCompletionManager.of(OsmDataManager.getInstance().getActiveDataSet());
@@ -907,8 +1016,17 @@ public class TagEditHelper {
                     .ifPresent(tag -> {
                         keys.setSelectedItemText(tag.getKey());
                         values.setSelectedItemText(tag.getValue());
+                        values_multiline.setText(tag.getValue());
+                        // Scroll multiline text area back to the top
+                        values_multiline.setCaretPosition(0);
                     });
 
+            // immediately show the multiline field if the key is multiline
+            if (TagEditHelper.MULTILINE_TAGS.get().contains(keys.getText())) {
+                multiline = true;
+                textCards.show(textPanel, MULTILINE);
+            }
+
 
             keys.addActionListener(ignore -> updateOkButtonIcon());
             values.addActionListener(ignore -> updateOkButtonIcon());
@@ -1096,7 +1214,17 @@ public class TagEditHelper {
                         // fix #7951, #8298 - update list of values before setting value (?)
                         updateValueModel(autocomplete, DEFAULT_AC_ITEM_COMPARATOR);
                         values.setSelectedItemText(t.getValue());
-                        values.requestFocus();
+                        values_multiline.setText(t.getValue());
+
+                        if (TagEditHelper.MULTILINE_TAGS.get().contains(t.getKey())) {
+                            multiline = true;
+                            textCards.show(textPanel, MULTILINE);
+                            SwingUtilities.invokeLater(() -> values_multiline.requestFocusInWindow());
+                        } else {
+                            multiline = false;
+                            textCards.show(textPanel, SINGLELINE);
+                            SwingUtilities.invokeLater(() -> values.getEditorComponent().requestFocusInWindow());
+                        }
                     }
                 };
                 /* POSSIBLE SHORTCUTS: 1,2,3,4,5,6,7,8,9,0=10 */
@@ -1284,7 +1412,7 @@ public class TagEditHelper {
          */
         private void performTagAdding(Collection<OsmPrimitive> selection) {
             String key = getEditItem(keys);
-            String value = getEditItem(values);
+            String value = getEditItem(multiline ? values_multiline : values);
             if (key.isEmpty() || value.isEmpty())
                 return;
             for (Tagged osm : selection) {
@@ -1310,6 +1438,7 @@ public class TagEditHelper {
         protected void clearEntries() {
             keys.getEditor().setItem("");
             values.getEditor().setItem("");
+            values_multiline.setText("");
         }
 
         /**
diff --git src/org/openstreetmap/josm/gui/history/HistoryBrowser.java src/org/openstreetmap/josm/gui/history/HistoryBrowser.java
index 3c747f7fe1..8be3849e20 100644
--- src/org/openstreetmap/josm/gui/history/HistoryBrowser.java
+++ src/org/openstreetmap/josm/gui/history/HistoryBrowser.java
@@ -176,5 +176,6 @@ public class HistoryBrowser extends JPanel implements Destroyable, ChangeListene
     @Override
     public void stateChanged(ChangeEvent e) {
         tagInfoViewer.adjustWidths();
+        tagInfoViewer.adjustHeights();
     }
 }
diff --git src/org/openstreetmap/josm/gui/history/TagInfoViewer.java src/org/openstreetmap/josm/gui/history/TagInfoViewer.java
index 354bafcb52..4d90a3d0e4 100644
--- src/org/openstreetmap/josm/gui/history/TagInfoViewer.java
+++ src/org/openstreetmap/josm/gui/history/TagInfoViewer.java
@@ -123,6 +123,25 @@ public class TagInfoViewer extends HistoryViewerPanel {
         adjustWidths(current, maxWidth);
     }
 
+    /**
+     * Use current data to adjust preferredHeight for both tables.
+     */
+    public void adjustHeights() {
+        // Update row height so the content fits for both tables individually
+        TableHelper.computeRowsHeight(reference);
+        TableHelper.computeRowsHeight(current);
+
+        // Now set height to the max of the two so the rows align again
+        for (int row = 0; row < reference.getRowCount(); row++) {
+            int referenceHeight = reference.getRowHeight(row);
+            int currentHeight = current.getRowHeight(row);
+
+            int maxHeight = Math.max(referenceHeight, currentHeight);
+            reference.setRowHeight(row, maxHeight);
+            current.setRowHeight(row, maxHeight);
+        }
+    }
+
     private static void adjustWidths(JTable table, int maxWidth) {
         for (int column = 0; column < table.getColumnCount(); column++) {
             TableHelper.adjustColumnWidth(table, column, maxWidth);
diff --git src/org/openstreetmap/josm/gui/history/TagTableCellRenderer.java src/org/openstreetmap/josm/gui/history/TagTableCellRenderer.java
index 6502e22337..d202e9f974 100644
--- src/org/openstreetmap/josm/gui/history/TagTableCellRenderer.java
+++ src/org/openstreetmap/josm/gui/history/TagTableCellRenderer.java
@@ -5,11 +5,11 @@ import static org.openstreetmap.josm.tools.I18n.tr;
 
 import java.awt.Color;
 import java.awt.Component;
+import java.awt.ComponentOrientation;
 
 import javax.swing.BorderFactory;
-import javax.swing.JLabel;
 import javax.swing.JTable;
-import javax.swing.SwingConstants;
+import javax.swing.JTextArea;
 import javax.swing.table.TableCellRenderer;
 
 import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
@@ -19,7 +19,7 @@ import org.openstreetmap.josm.gui.util.GuiHelper;
  * The {@link TableCellRenderer} for a list of tags in {@link HistoryBrowser}
  *
  */
-public class TagTableCellRenderer extends JLabel implements TableCellRenderer {
+public class TagTableCellRenderer extends JTextArea implements TableCellRenderer {
     /**
      * The background color for a selected row that has the focus.
      */
@@ -73,7 +73,14 @@ public class TagTableCellRenderer extends JLabel implements TableCellRenderer {
 
         setText(text);
         setToolTipText(tooltip != null ? tooltip : text);
-        setHorizontalAlignment(column == TagTableColumnModel.COLUMN_VERSION ? SwingConstants.TRAILING : SwingConstants.LEADING);
+        // Abuse component orientation since JTextAreas don't have setHorizontalAlignment
+        ComponentOrientation orientation;
+        if (column == TagTableColumnModel.COLUMN_VERSION) {
+            orientation = ComponentOrientation.RIGHT_TO_LEFT;
+        } else {
+            orientation = ComponentOrientation.UNKNOWN;
+        }
+        setComponentOrientation(orientation);
         TwoColumnDiff.Item.DiffItemType diffItemType = model.getDiffItemType(key, column == TagTableColumnModel.COLUMN_VALUE);
         GuiHelper.setBackgroundReadable(this, diffItemType.getColor(isSelected, table.hasFocus()));
         return this;
diff --git src/org/openstreetmap/josm/gui/tagging/presets/items/Text.java src/org/openstreetmap/josm/gui/tagging/presets/items/Text.java
index 32f83243c4..dbf0296729 100644
--- src/org/openstreetmap/josm/gui/tagging/presets/items/Text.java
+++ src/org/openstreetmap/josm/gui/tagging/presets/items/Text.java
@@ -5,13 +5,13 @@ import static org.openstreetmap.josm.tools.I18n.tr;
 
 import java.awt.Color;
 import java.awt.Component;
+import java.awt.Container;
 import java.awt.GridBagLayout;
 import java.awt.Insets;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.regex.Pattern;
 
 import javax.swing.AbstractButton;
 import javax.swing.BorderFactory;
@@ -20,7 +20,10 @@ import javax.swing.JButton;
 import javax.swing.JComponent;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
+import javax.swing.JScrollPane;
 import javax.swing.JToggleButton;
+import javax.swing.JViewport;
+import javax.swing.text.JTextComponent;
 
 import org.openstreetmap.josm.data.osm.Tag;
 import org.openstreetmap.josm.data.tagging.ac.AutoCompletionItem;
@@ -32,6 +35,7 @@ import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItem;
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItemGuiSupport;
 import org.openstreetmap.josm.gui.util.DocumentAdapter;
 import org.openstreetmap.josm.gui.widgets.JosmComboBox;
+import org.openstreetmap.josm.gui.widgets.JosmTextArea;
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
 import org.openstreetmap.josm.gui.widgets.OrientationAction;
 import org.openstreetmap.josm.tools.GBC;
@@ -46,8 +50,6 @@ import org.xml.sax.SAXException;
  * Text field type.
  */
 public class Text extends KeyedItem {
-    private static final Pattern MULTILINE_WHITESPACE_PATTERN = Pattern.compile("[\\s&&[^\n]]+");
-
     private static int auto_increment_selected; // NOSONAR
 
     /** The default value for the item. If not specified, the current value of the key is chosen as default (if applicable). Defaults to "". */
@@ -82,26 +84,47 @@ public class Text extends KeyedItem {
         }
         getAllForKeys(keys).forEach(model::addElement);
 
-        AutoCompTextField<AutoCompletionItem> textField;
+        JTextComponent textField;
         AutoCompComboBoxEditor<AutoCompletionItem> editor = null;
 
         // find out if our key is already used in the selection.
         Usage usage = determineTextUsage(support.getSelected(), key);
 
         if (usage.unused() || usage.hasUniqueValue()) {
-            textField = new AutoCompTextField<>();
+            if (this.multiline) {
+                JosmTextArea area = new JosmTextArea();
+                area.setBorder((new JosmTextField()).getBorder());
+                area.setRows(5);
+
+                textField = area;
+            } else {
+                textField = new AutoCompTextField<AutoCompletionItem>();
+            }
         } else {
             editor = new AutoCompComboBoxEditor<>();
             textField = editor.getEditorComponent();
         }
-        textField.setModel(model);
-        value = textField;
 
-        if (length > 0) {
-            textField.setMaxTextLength(length);
+        if (this.multiline) {
+            value = new JScrollPane(textField);
+        } else {
+            value = textField;
+        }
+
+        if (textField instanceof AutoCompTextField<?>) {
+            AutoCompTextField<AutoCompletionItem> tf = (AutoCompTextField<AutoCompletionItem>) textField;
+            tf.setModel(model);
+            if (length > 0) {
+                tf.setMaxTextLength(length);
+            }
         }
+
         if (TaggingPresetItem.DISPLAY_KEYS_AS_HINT.get()) {
-            textField.setHint(key);
+            if (textField instanceof JosmTextField) {
+                ((JosmTextField) textField).setHint(key);
+            } else {
+                ((JosmTextArea) textField).setHint(key);
+            }
         }
         if (usage.unused()) {
             if (auto_increment_selected != 0 && auto_increment != null) {
@@ -123,12 +146,13 @@ public class Text extends KeyedItem {
                 // selected osm primitives are tagged and filling default values feature is disabled
                 textField.setText("");
             }
-            value = textField;
             originalValue = null;
         } else if (usage.hasUniqueValue()) {
             // all objects use the same value
             textField.setText(usage.getFirst());
-            value = textField;
+            // Scroll multiline text areas back to the top
+            textField.setCaretPosition(0);
+
             originalValue = usage.getFirst();
         }
         if (editor != null) {
@@ -222,10 +246,10 @@ public class Text extends KeyedItem {
     private static String getValue(Component comp) {
         if (comp instanceof JosmComboBox) {
             return ((JosmComboBox<?>) comp).getEditorItemAsString();
-        } else if (comp instanceof JosmTextField) {
-            return ((JosmTextField) comp).getText();
-        } else if (comp instanceof JPanel) {
-            return getValue(((JPanel) comp).getComponent(0));
+        } else if (comp instanceof JTextComponent) {
+            return ((JTextComponent) comp).getText();
+        } else if (comp instanceof JPanel || comp instanceof JScrollPane || comp instanceof JViewport) {
+            return getValue(((Container) comp).getComponent(0));
         } else {
             return null;
         }
@@ -243,7 +267,7 @@ public class Text extends KeyedItem {
 
         if (this.normalize) {
             if (this.multiline) {
-                v = Utils.removeWhiteSpaces(MULTILINE_WHITESPACE_PATTERN, v);
+                v = Utils.removeWhiteSpaces(Utils.MULTILINE_WHITESPACE_PATTERN, v);
             } else {
                 v = Utils.removeWhiteSpaces(v);
             }
@@ -285,7 +309,7 @@ public class Text extends KeyedItem {
         }
     }
 
-    private void setupListeners(AutoCompTextField<AutoCompletionItem> textField, TaggingPresetItemGuiSupport support) {
+    private void setupListeners(JTextComponent textField, TaggingPresetItemGuiSupport support) {
         // value_templates don't work well with multiple selected items because,
         // as the command queue is currently implemented, we can only save
         // the same value to all selected primitives, which is probably not
diff --git src/org/openstreetmap/josm/gui/util/TableHelper.java src/org/openstreetmap/josm/gui/util/TableHelper.java
index 51ede3f1d0..04a6d71523 100644
--- src/org/openstreetmap/josm/gui/util/TableHelper.java
+++ src/org/openstreetmap/josm/gui/util/TableHelper.java
@@ -77,6 +77,17 @@ public final class TableHelper {
         return maxwidth;
     }
 
+    static int getMaxHeight(JTable tbl, int row) {
+        int maxheight = 0;
+        for (int col = 0; col < tbl.getColumnCount(); col++) {
+            TableCellRenderer tcr = tbl.getCellRenderer(row, col);
+            Object val = tbl.getValueAt(row, col);
+            Component comp = tcr.getTableCellRendererComponent(tbl, val, false, false, row, col);
+            maxheight = Math.max(comp.getPreferredSize().height, maxheight);
+        }
+        return maxheight;
+    }
+
     /**
      * adjust the preferred width of column col to the maximum preferred width of the cells (including header)
      * @param tbl table
@@ -106,6 +117,17 @@ public final class TableHelper {
         tbl.getColumnModel().getColumn(col).setPreferredWidth(Math.min(maxwidth+10, maxColumnWidth));
     }
 
+    /**
+     * adjust the preferred height of the given row to the maximum preferred height of the cells
+     * requires JTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+     * @param tbl table
+     * @param row row index
+     */
+    public static void adjustRowHeight(JTable tbl, int row) {
+        int maxheight = getMaxHeight(tbl, row);
+        tbl.setRowHeight(row, maxheight);
+    }
+
     /**
      * adjust the table's columns to fit their content best
      * requires JTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
@@ -118,6 +140,17 @@ public final class TableHelper {
         }
     }
 
+    /**
+     * adjust the table's rows to fit their content best
+     * requires JTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+     * @param tbl table
+     */
+    public static void computeRowsHeight(JTable tbl) {
+        for (int row = 0; row < tbl.getRowCount(); row++) {
+            adjustRowHeight(tbl, row);
+        }
+    }
+
     /**
      * Returns a stream of all of the selected indices in the selection model, in increasing order.
      *
diff --git src/org/openstreetmap/josm/gui/widgets/JosmTextArea.java src/org/openstreetmap/josm/gui/widgets/JosmTextArea.java
index a01ef09e40..f5dddca9ca 100644
--- src/org/openstreetmap/josm/gui/widgets/JosmTextArea.java
+++ src/org/openstreetmap/josm/gui/widgets/JosmTextArea.java
@@ -1,24 +1,42 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.gui.widgets;
 
+import java.awt.Color;
+import java.awt.ComponentOrientation;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
 import java.awt.KeyboardFocusManager;
+import java.awt.RenderingHints;
 import java.awt.event.FocusEvent;
 import java.awt.event.FocusListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
 
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
 import javax.swing.JTextArea;
+import javax.swing.RepaintManager;
+import javax.swing.UIManager;
+import javax.swing.text.BadLocationException;
 import javax.swing.text.Document;
 
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.MapFrame;
 import org.openstreetmap.josm.tools.Destroyable;
+import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Utils;
 
 /**
  * Subclass of {@link JTextArea} that adds a "native" context menu (cut/copy/paste/select all).
  * @since 5886
  */
-public class JosmTextArea extends JTextArea implements Destroyable, FocusListener {
+public class JosmTextArea extends JTextArea implements Destroyable, FocusListener, PropertyChangeListener {
 
     private final PopupMenuLauncher launcher;
+    private OrientationAction orientationAction;
+    private String hint;
 
     /**
      * Constructs a new {@code JosmTextArea}. A default model is set, the initial string
@@ -91,9 +109,109 @@ public class JosmTextArea extends JTextArea implements Destroyable, FocusListene
     public JosmTextArea(Document doc, String text, int rows, int columns) {
         super(doc, text, rows, columns);
         launcher = TextContextualPopupMenu.enableMenuFor(this, true);
+
+        // the menu and hotkey to change text orientation
+        orientationAction = new OrientationAction(this);
+        orientationAction.addPropertyChangeListener(this);
+        JPopupMenu menu = launcher.getMenu();
+        menu.addSeparator();
+        menu.add(new JMenuItem(orientationAction));
+        getInputMap().put(OrientationAction.getShortcutKey(), orientationAction);
+
         addFocusListener(this);
     }
 
+    /**
+     * Replies the hint displayed when no text has been entered.
+     * @return the hint
+     */
+    public final String getHint() {
+        return hint;
+    }
+
+    /**
+     * Sets the hint to display when no text has been entered.
+     * @param hint the hint to set
+     * @return the old hint
+     */
+    public String setHint(String hint) {
+        String old = this.hint;
+        this.hint = hint;
+        return old;
+    }
+
+    /**
+     * Return true if the text area should display the hint text.
+     *
+     * @return whether to display the hint text
+     */
+    public boolean displayHint() {
+        return !Utils.isEmpty(hint) && getText().isEmpty() && !isFocusOwner();
+    }
+
+    /**
+     * Returns the color for hint texts.
+     * @return the Color for hint texts
+     */
+    public static Color getHintTextColor() {
+        Color color = UIManager.getColor("TextField[Disabled].textForeground"); // Nimbus?
+        if (color == null)
+            color = UIManager.getColor("TextField.inactiveForeground");
+        if (color == null)
+            color = Color.GRAY;
+        return color;
+    }
+
+    /**
+     * Returns the font for hint texts.
+     * @return the font for hint texts
+     */
+    public static Font getHintFont() {
+        return UIManager.getFont("TextField.font");
+    }
+
+    @Override
+    public void paintComponent(Graphics g) {
+        super.paintComponent(g);
+
+        if (displayHint()) {
+            drawHint(g);
+        }
+    }
+
+    /**
+     * Draws the hint text over the editor component.
+     *
+     * @param g the graphics context
+     */
+    public void drawHint(Graphics g) {
+        int x;
+        try {
+            x = (int) Math.round(modelToView2D(0).getX());
+        } catch (BadLocationException exc) {
+            Logging.trace(exc);
+            return; // can't happen
+        }
+        // Taken from http://stackoverflow.com/a/24571681/2257172
+        if (g instanceof Graphics2D) {
+            ((Graphics2D) g).setRenderingHint(
+                    RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+        }
+        g.setColor(getHintTextColor());
+        g.setFont(getHintFont());
+        if (getComponentOrientation().isLeftToRight()) {
+            g.drawString(getHint(), x, getBaseline(getWidth(), getHeight()));
+        } else {
+            FontMetrics metrics = g.getFontMetrics(g.getFont());
+            int dx = metrics.stringWidth(getHint());
+            g.drawString(getHint(), x - dx, getBaseline(getWidth(), getHeight()));
+        }
+        // Needed to avoid endless repaint loop if we accidentally draw over the insets. This may
+        // easily happen because a change in text orientation invalidates the text area and
+        // following that the preferred size gets smaller. (Bug in Swing?)
+        RepaintManager.currentManager(this).markCompletelyClean(this);
+    }
+
     /**
      * Restore default behaviour of focus transfer with TAB, overridden by {@link JTextArea}.
      * @return {@code this}
@@ -112,6 +230,8 @@ public class JosmTextArea extends JTextArea implements Destroyable, FocusListene
         if (map != null) {
             map.keyDetector.setEnabled(false);
         }
+
+        repaint(); // get rid of hint
     }
 
     @Override
@@ -120,6 +240,8 @@ public class JosmTextArea extends JTextArea implements Destroyable, FocusListene
         if (map != null) {
             map.keyDetector.setEnabled(true);
         }
+
+        repaint(); // paint hint
     }
 
     @Override
@@ -127,4 +249,12 @@ public class JosmTextArea extends JTextArea implements Destroyable, FocusListene
         removeFocusListener(this);
         TextContextualPopupMenu.disableMenuFor(this, launcher);
     }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        // command from the menu / shortcut key
+        if ("orientationAction".equals(evt.getPropertyName())) {
+            setComponentOrientation((ComponentOrientation) evt.getNewValue());
+        }
+    }
 }
diff --git src/org/openstreetmap/josm/tools/Utils.java src/org/openstreetmap/josm/tools/Utils.java
index fd55277504..b4c29766d1 100644
--- src/org/openstreetmap/josm/tools/Utils.java
+++ src/org/openstreetmap/josm/tools/Utils.java
@@ -71,6 +71,7 @@ public final class Utils {
 
     /** Pattern matching white spaces */
     public static final Pattern WHITE_SPACES_PATTERN = Pattern.compile("\\s+", Pattern.UNICODE_CHARACTER_CLASS);
+    public static final Pattern MULTILINE_WHITESPACE_PATTERN = Pattern.compile("[\\s&&[^\n]]+");
 
     private static final long MILLIS_OF_SECOND = TimeUnit.SECONDS.toMillis(1);
     private static final long MILLIS_OF_MINUTE = TimeUnit.MINUTES.toMillis(1);
diff --git test/unit/org/openstreetmap/josm/gui/dialogs/properties/PropertiesCellRendererTest.java test/unit/org/openstreetmap/josm/gui/dialogs/properties/PropertiesCellRendererTest.java
index 45902efd40..b3bdbdfb9a 100644
--- test/unit/org/openstreetmap/josm/gui/dialogs/properties/PropertiesCellRendererTest.java
+++ test/unit/org/openstreetmap/josm/gui/dialogs/properties/PropertiesCellRendererTest.java
@@ -3,9 +3,11 @@ package org.openstreetmap.josm.gui.dialogs.properties;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
+import java.awt.Component;
 import java.util.function.IntFunction;
 
 import javax.swing.JLabel;
+import javax.swing.JTextArea;
 import javax.swing.JTable;
 import javax.swing.table.DefaultTableModel;
 
@@ -32,8 +34,15 @@ class PropertiesCellRendererTest {
                 new Object[]{"elevation", "314159"},
         }, new Object[]{"key", "value"});
         JTable table = new JTable(tableModel);
-        IntFunction<String> getLabel = row -> ((JLabel) renderer.getTableCellRendererComponent(
-                table, table.getValueAt(row, 1), false, false, row, 1)).getText();
+        IntFunction<String> getLabel = row -> {
+            Component comp = renderer.getTableCellRendererComponent(
+                table, table.getValueAt(row, 1), false, false, row, 1);
+            if (comp instanceof JLabel) {
+                return ((JLabel) comp).getText();
+            } else {
+                return ((JTextArea) comp).getText();
+            }
+        };
         assertEquals("<html><body><span color='#FF0000'>\u25A0</span> red</body></html>", getLabel.apply(0));
         assertEquals("<html><body><span color='#008000'>\u25A0</span> green</body></html>", getLabel.apply(1));
         assertEquals("<html><body><span color='#123'>\u25A0</span> #123</body></html>", getLabel.apply(2));
diff --git test/unit/org/openstreetmap/josm/gui/tagging/presets/items/TextTest.java test/unit/org/openstreetmap/josm/gui/tagging/presets/items/TextTest.java
index e03e925e57..8f2d3a5417 100644
--- test/unit/org/openstreetmap/josm/gui/tagging/presets/items/TextTest.java
+++ test/unit/org/openstreetmap/josm/gui/tagging/presets/items/TextTest.java
@@ -14,6 +14,9 @@ import java.util.Map;
 
 import javax.swing.JComponent;
 import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JViewport;
+import javax.swing.text.JTextComponent;
 
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -24,6 +27,7 @@ import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItemGuiSupport;
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItemTest;
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType;
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
+import org.openstreetmap.josm.gui.widgets.JosmTextArea;
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
 import org.openstreetmap.josm.testutils.annotations.Main;
 
@@ -66,11 +70,16 @@ class TextTest implements TaggingPresetItemTest {
         final JPanel panel = new JPanel();
         text.addToPanel(panel, TaggingPresetItemGuiSupport.create(false));
         JComponent value = assertInstanceOf(JComponent.class, panel.getComponent(1));
-        while (value instanceof JPanel) {
+        while (value instanceof JPanel || value instanceof JScrollPane || value instanceof JViewport) {
             value = (JComponent) value.getComponent(0);
         }
-        final JosmTextField textField = assertInstanceOf(JosmTextField.class, value,
-            "Until we support multiline editing, this should be a text field");
+        final JTextComponent textField;
+        if (value instanceof JosmTextField) {
+            textField = (JosmTextField) value;
+        } else {
+            textField = assertInstanceOf(JosmTextArea.class, value,
+                "This should be either a JosmTextField or a JosmTextArea");
+        }
         textField.setText(toTest);
         text.addCommands(changeCommands);
         assertTrue(text.multiline);
