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