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