001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.io; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.BorderLayout; 007import java.awt.GridBagLayout; 008import java.awt.event.ActionEvent; 009import java.awt.event.ActionListener; 010import java.awt.event.FocusAdapter; 011import java.awt.event.FocusEvent; 012import java.awt.event.KeyAdapter; 013import java.awt.event.KeyEvent; 014import java.util.Arrays; 015import java.util.Collections; 016import java.util.LinkedList; 017import java.util.List; 018import java.util.concurrent.TimeUnit; 019 020import javax.swing.Action; 021import javax.swing.BorderFactory; 022import javax.swing.JEditorPane; 023import javax.swing.JPanel; 024import javax.swing.event.ChangeEvent; 025import javax.swing.event.ChangeListener; 026import javax.swing.event.HyperlinkEvent; 027 028import org.openstreetmap.josm.Main; 029import org.openstreetmap.josm.data.osm.Changeset; 030import org.openstreetmap.josm.gui.widgets.HistoryComboBox; 031import org.openstreetmap.josm.gui.widgets.JMultilineLabel; 032import org.openstreetmap.josm.tools.CheckParameterUtil; 033import org.openstreetmap.josm.tools.GBC; 034import org.openstreetmap.josm.tools.Utils; 035 036/** 037 * BasicUploadSettingsPanel allows to enter the basic parameters required for uploading data. 038 * @since 2599 039 */ 040public class BasicUploadSettingsPanel extends JPanel { 041 public static final String HISTORY_KEY = "upload.comment.history"; 042 public static final String HISTORY_LAST_USED_KEY = "upload.comment.last-used"; 043 public static final String HISTORY_MAX_AGE_KEY = "upload.comment.max-age"; 044 public static final String SOURCE_HISTORY_KEY = "upload.source.history"; 045 046 /** the history combo box for the upload comment */ 047 private final HistoryComboBox hcbUploadComment = new HistoryComboBox(); 048 private final HistoryComboBox hcbUploadSource = new HistoryComboBox(); 049 /** the panel with a summary of the upload parameters */ 050 private final UploadParameterSummaryPanel pnlUploadParameterSummary = new UploadParameterSummaryPanel(); 051 /** the changeset comment model */ 052 private final transient ChangesetCommentModel changesetCommentModel; 053 private final transient ChangesetCommentModel changesetSourceModel; 054 055 protected JPanel buildUploadCommentPanel() { 056 JPanel pnl = new JPanel(new GridBagLayout()); 057 058 JEditorPane commentLabel = new JMultilineLabel("<html><b>" + tr("Provide a brief comment for the changes you are uploading:")); 059 pnl.add(commentLabel, GBC.eol().insets(0, 5, 10, 3).fill(GBC.HORIZONTAL)); 060 hcbUploadComment.setToolTipText(tr("Enter an upload comment")); 061 hcbUploadComment.setMaxTextLength(Changeset.MAX_CHANGESET_TAG_LENGTH); 062 List<String> cmtHistory = new LinkedList<>(Main.pref.getCollection(HISTORY_KEY, new LinkedList<String>())); 063 Collections.reverse(cmtHistory); // we have to reverse the history, because ComboBoxHistory will reverse it again in addElement() 064 hcbUploadComment.setPossibleItems(cmtHistory); 065 CommentModelListener commentModelListener = new CommentModelListener(hcbUploadComment, changesetCommentModel); 066 hcbUploadComment.getEditor().addActionListener(commentModelListener); 067 hcbUploadComment.getEditorComponent().addFocusListener(commentModelListener); 068 pnl.add(hcbUploadComment, GBC.eol().fill(GBC.HORIZONTAL)); 069 070 JEditorPane sourceLabel = new JMultilineLabel("<html><b>" + tr("Specify the data source for the changes") 071 + "</b> (<a href=\"urn:changeset-source\">" + tr("obtain from current layers") + "</a>)<b>:</b>"); 072 sourceLabel.addHyperlinkListener(e -> { 073 if (HyperlinkEvent.EventType.ACTIVATED.equals(e.getEventType())) { 074 final String source = Main.map.mapView.getLayerInformationForSourceTag(); 075 hcbUploadSource.setText(Utils.shortenString(source, Changeset.MAX_CHANGESET_TAG_LENGTH)); 076 // Fix #9965 077 changesetSourceModel.setComment(hcbUploadSource.getText()); 078 } 079 }); 080 pnl.add(sourceLabel, GBC.eol().insets(0, 8, 10, 3).fill(GBC.HORIZONTAL)); 081 082 hcbUploadSource.setToolTipText(tr("Enter a source")); 083 hcbUploadSource.setMaxTextLength(Changeset.MAX_CHANGESET_TAG_LENGTH); 084 List<String> sourceHistory = new LinkedList<>(Main.pref.getCollection(SOURCE_HISTORY_KEY, getDefaultSources())); 085 Collections.reverse(sourceHistory); // we have to reverse the history, because ComboBoxHistory will reverse it again in addElement() 086 hcbUploadSource.setPossibleItems(sourceHistory); 087 CommentModelListener sourceModelListener = new CommentModelListener(hcbUploadSource, changesetSourceModel); 088 hcbUploadSource.getEditor().addActionListener(sourceModelListener); 089 hcbUploadSource.getEditorComponent().addFocusListener(sourceModelListener); 090 pnl.add(hcbUploadSource, GBC.eol().fill(GBC.HORIZONTAL)); 091 return pnl; 092 } 093 094 /** 095 * Returns the default list of sources. 096 * @return the default list of sources 097 */ 098 public static List<String> getDefaultSources() { 099 return Arrays.asList("knowledge", "survey", "Bing"); 100 } 101 102 protected void build() { 103 setLayout(new BorderLayout()); 104 setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); 105 add(buildUploadCommentPanel(), BorderLayout.NORTH); 106 add(pnlUploadParameterSummary, BorderLayout.CENTER); 107 } 108 109 /** 110 * Creates the panel 111 * 112 * @param changesetCommentModel the model for the changeset comment. Must not be null 113 * @param changesetSourceModel the model for the changeset source. Must not be null. 114 * @throws IllegalArgumentException if {@code changesetCommentModel} is null 115 */ 116 public BasicUploadSettingsPanel(ChangesetCommentModel changesetCommentModel, ChangesetCommentModel changesetSourceModel) { 117 CheckParameterUtil.ensureParameterNotNull(changesetCommentModel, "changesetCommentModel"); 118 CheckParameterUtil.ensureParameterNotNull(changesetSourceModel, "changesetSourceModel"); 119 this.changesetCommentModel = changesetCommentModel; 120 this.changesetSourceModel = changesetSourceModel; 121 changesetCommentModel.addChangeListener(new ChangesetCommentChangeListener(hcbUploadComment)); 122 changesetSourceModel.addChangeListener(new ChangesetCommentChangeListener(hcbUploadSource)); 123 build(); 124 } 125 126 public void setUploadTagDownFocusTraversalHandlers(final Action handler) { 127 setHistoryComboBoxDownFocusTraversalHandler(handler, hcbUploadComment); 128 setHistoryComboBoxDownFocusTraversalHandler(handler, hcbUploadSource); 129 } 130 131 public void setHistoryComboBoxDownFocusTraversalHandler(final Action handler, final HistoryComboBox hcb) { 132 hcb.getEditor().addActionListener(handler); 133 hcb.getEditorComponent().addKeyListener(new HistoryComboBoxKeyAdapter(hcb, handler)); 134 } 135 136 /** 137 * Remembers the user input in the preference settings 138 */ 139 public void rememberUserInput() { 140 // store the history of comments 141 hcbUploadComment.addCurrentItemToHistory(); 142 Main.pref.putCollection(HISTORY_KEY, hcbUploadComment.getHistory()); 143 Main.pref.putInteger(HISTORY_LAST_USED_KEY, (int) (TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))); 144 // store the history of sources 145 hcbUploadSource.addCurrentItemToHistory(); 146 Main.pref.putCollection(SOURCE_HISTORY_KEY, hcbUploadSource.getHistory()); 147 } 148 149 /** 150 * Initializes the panel for user input 151 */ 152 public void startUserInput() { 153 hcbUploadComment.requestFocusInWindow(); 154 hcbUploadComment.getEditorComponent().requestFocusInWindow(); 155 } 156 157 /** 158 * Initializes editing of upload comment. 159 */ 160 public void initEditingOfUploadComment() { 161 hcbUploadComment.getEditor().selectAll(); 162 hcbUploadComment.requestFocusInWindow(); 163 } 164 165 /** 166 * Initializes editing of upload source. 167 */ 168 public void initEditingOfUploadSource() { 169 hcbUploadSource.getEditor().selectAll(); 170 hcbUploadSource.requestFocusInWindow(); 171 } 172 173 public UploadParameterSummaryPanel getUploadParameterSummaryPanel() { 174 return pnlUploadParameterSummary; 175 } 176 177 static final class HistoryComboBoxKeyAdapter extends KeyAdapter { 178 private final HistoryComboBox hcb; 179 private final Action handler; 180 181 HistoryComboBoxKeyAdapter(HistoryComboBox hcb, Action handler) { 182 this.hcb = hcb; 183 this.handler = handler; 184 } 185 186 @Override 187 public void keyTyped(KeyEvent e) { 188 if (e.getKeyCode() == KeyEvent.VK_TAB) { 189 handler.actionPerformed(new ActionEvent(hcb, 0, "focusDown")); 190 } 191 } 192 } 193 194 /** 195 * Updates the changeset comment model upon changes in the input field. 196 */ 197 static class CommentModelListener extends FocusAdapter implements ActionListener { 198 199 private final HistoryComboBox source; 200 private final ChangesetCommentModel destination; 201 202 CommentModelListener(HistoryComboBox source, ChangesetCommentModel destination) { 203 this.source = source; 204 this.destination = destination; 205 } 206 207 @Override 208 public void actionPerformed(ActionEvent e) { 209 destination.setComment(source.getText()); 210 } 211 212 @Override 213 public void focusLost(FocusEvent e) { 214 destination.setComment(source.getText()); 215 } 216 } 217 218 /** 219 * Observes the changeset comment model and keeps the comment input field 220 * in sync with the current changeset comment 221 */ 222 static class ChangesetCommentChangeListener implements ChangeListener { 223 224 private final HistoryComboBox destination; 225 226 ChangesetCommentChangeListener(HistoryComboBox destination) { 227 this.destination = destination; 228 } 229 230 @Override 231 public void stateChanged(ChangeEvent e) { 232 if (!(e.getSource() instanceof ChangesetCommentModel)) return; 233 String newComment = ((ChangesetCommentModel) e.getSource()).getComment(); 234 if (!destination.getText().equals(newComment)) { 235 destination.setText(newComment); 236 } 237 } 238 } 239}