001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.io; 003 004import java.awt.BorderLayout; 005import java.util.Map; 006import java.util.Optional; 007 008import javax.swing.JPanel; 009import javax.swing.event.ChangeEvent; 010import javax.swing.event.ChangeListener; 011import javax.swing.event.TableModelEvent; 012import javax.swing.event.TableModelListener; 013 014import org.openstreetmap.josm.data.osm.Changeset; 015import org.openstreetmap.josm.gui.MainApplication; 016import org.openstreetmap.josm.gui.tagging.TagEditorPanel; 017import org.openstreetmap.josm.gui.tagging.TagModel; 018import org.openstreetmap.josm.spi.preferences.Config; 019import org.openstreetmap.josm.tools.CheckParameterUtil; 020 021/** 022 * Tag settings panel of upload dialog. 023 * @since 2599 024 */ 025public class TagSettingsPanel extends JPanel implements TableModelListener { 026 027 /** checkbox for selecting whether an atomic upload is to be used */ 028 private final TagEditorPanel pnlTagEditor = new TagEditorPanel(null, null, Changeset.MAX_CHANGESET_TAG_LENGTH); 029 /** the model for the changeset comment */ 030 private final transient ChangesetCommentModel changesetCommentModel; 031 private final transient ChangesetCommentModel changesetSourceModel; 032 private final transient ChangesetReviewModel changesetReviewModel; 033 034 /** 035 * Creates a new panel 036 * 037 * @param changesetCommentModel the changeset comment model. Must not be null. 038 * @param changesetSourceModel the changeset source model. Must not be null. 039 * @param changesetReviewModel the model for the changeset review. Must not be null. 040 * @throws IllegalArgumentException if {@code changesetCommentModel} is null 041 * @since 12719 (signature) 042 */ 043 public TagSettingsPanel(ChangesetCommentModel changesetCommentModel, ChangesetCommentModel changesetSourceModel, 044 ChangesetReviewModel changesetReviewModel) { 045 CheckParameterUtil.ensureParameterNotNull(changesetCommentModel, "changesetCommentModel"); 046 CheckParameterUtil.ensureParameterNotNull(changesetSourceModel, "changesetSourceModel"); 047 CheckParameterUtil.ensureParameterNotNull(changesetReviewModel, "changesetReviewModel"); 048 this.changesetCommentModel = changesetCommentModel; 049 this.changesetSourceModel = changesetSourceModel; 050 this.changesetReviewModel = changesetReviewModel; 051 changesetCommentModel.addChangeListener(new ChangesetCommentChangeListener("comment", "hashtags")); 052 changesetSourceModel.addChangeListener(new ChangesetCommentChangeListener("source")); 053 changesetReviewModel.addChangeListener(new ChangesetReviewChangeListener()); 054 build(); 055 pnlTagEditor.getModel().addTableModelListener(this); 056 } 057 058 protected void build() { 059 setLayout(new BorderLayout()); 060 add(pnlTagEditor, BorderLayout.CENTER); 061 } 062 063 protected void setProperty(String key, String value) { 064 String val = (value == null ? "" : value).trim(); 065 String commentInTag = getTagEditorValue(key); 066 if (val.equals(commentInTag)) 067 return; 068 069 if (val.isEmpty()) { 070 pnlTagEditor.getModel().delete(key); 071 return; 072 } 073 TagModel tag = pnlTagEditor.getModel().get(key); 074 if (tag == null) { 075 tag = new TagModel(key, val); 076 pnlTagEditor.getModel().add(tag); 077 } else { 078 pnlTagEditor.getModel().updateTagValue(tag, val); 079 } 080 } 081 082 protected String getTagEditorValue(String key) { 083 TagModel tag = pnlTagEditor.getModel().get(key); 084 return tag == null ? null : tag.getValue(); 085 } 086 087 /** 088 * Initialize panel from the given tags. 089 * @param tags the tags used to initialize the panel 090 */ 091 public void initFromTags(Map<String, String> tags) { 092 pnlTagEditor.getModel().initFromTags(tags); 093 } 094 095 /** 096 * Replies the map with the current tags in the tag editor model. 097 * @param keepEmpty {@code true} to keep empty tags 098 * @return the map with the current tags in the tag editor model. 099 */ 100 public Map<String, String> getTags(boolean keepEmpty) { 101 forceCommentFieldReload(); 102 return pnlTagEditor.getModel().getTags(keepEmpty); 103 } 104 105 /** 106 * Initializes the panel for user input 107 */ 108 public void startUserInput() { 109 pnlTagEditor.initAutoCompletion(MainApplication.getLayerManager().getEditLayer()); 110 } 111 112 /* -------------------------------------------------------------------------- */ 113 /* Interface TableChangeListener */ 114 /* -------------------------------------------------------------------------- */ 115 @Override 116 public void tableChanged(TableModelEvent e) { 117 changesetCommentModel.setComment(getTagEditorValue("comment")); 118 changesetSourceModel.setComment(getTagEditorValue("source")); 119 changesetReviewModel.setReviewRequested("yes".equals(getTagEditorValue("review_requested"))); 120 } 121 122 /** 123 * Force update the fields if the user is currently changing them. See #5676 124 */ 125 private void forceCommentFieldReload() { 126 setProperty("comment", changesetCommentModel.getComment()); 127 setProperty("source", changesetSourceModel.getComment()); 128 setProperty("review_requested", changesetReviewModel.isReviewRequested() ? "yes" : null); 129 } 130 131 /** 132 * Observes the changeset comment model and keeps the tag editor in sync 133 * with the current changeset comment 134 */ 135 class ChangesetCommentChangeListener implements ChangeListener { 136 137 private final String key; 138 private final String hashtagsKey; 139 140 ChangesetCommentChangeListener(String key) { 141 this(key, null); 142 } 143 144 ChangesetCommentChangeListener(String key, String hashtagsKey) { 145 this.key = key; 146 this.hashtagsKey = hashtagsKey; 147 } 148 149 @Override 150 public void stateChanged(ChangeEvent e) { 151 if (e.getSource() instanceof ChangesetCommentModel) { 152 ChangesetCommentModel model = ((ChangesetCommentModel) e.getSource()); 153 String newValue = model.getComment(); 154 String oldValue = Optional.ofNullable(getTagEditorValue(key)).orElse(""); 155 if (!oldValue.equals(newValue)) { 156 setProperty(key, newValue); 157 if (hashtagsKey != null && Config.getPref().getBoolean("upload.changeset.hashtags", true)) { 158 String newHashTags = String.join(";", model.findHashTags()); 159 String oldHashTags = Optional.ofNullable(getTagEditorValue(hashtagsKey)).orElse(""); 160 if (!oldHashTags.equals(newHashTags)) { 161 setProperty(hashtagsKey, newHashTags); 162 } 163 } 164 } 165 } 166 } 167 } 168 169 /** 170 * Observes the changeset review model and keeps the tag editor in sync 171 * with the current changeset review request 172 */ 173 class ChangesetReviewChangeListener implements ChangeListener { 174 175 private static final String KEY = "review_requested"; 176 177 @Override 178 public void stateChanged(ChangeEvent e) { 179 if (e.getSource() instanceof ChangesetReviewModel) { 180 boolean newState = ((ChangesetReviewModel) e.getSource()).isReviewRequested(); 181 boolean oldState = "yes".equals(Optional.ofNullable(getTagEditorValue(KEY)).orElse("")); 182 if (oldState != newState) { 183 setProperty(KEY, newState ? "yes" : null); 184 } 185 } 186 } 187 } 188}