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