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.Dimension;
008import java.awt.FlowLayout;
009import java.awt.event.ActionEvent;
010import java.awt.event.KeyEvent;
011import java.awt.event.WindowAdapter;
012import java.awt.event.WindowEvent;
013import java.util.ArrayList;
014import java.util.Collection;
015import java.util.List;
016
017import javax.swing.AbstractAction;
018import javax.swing.BorderFactory;
019import javax.swing.DefaultListModel;
020import javax.swing.JComponent;
021import javax.swing.JDialog;
022import javax.swing.JLabel;
023import javax.swing.JList;
024import javax.swing.JOptionPane;
025import javax.swing.JPanel;
026import javax.swing.JScrollPane;
027import javax.swing.KeyStroke;
028import javax.swing.event.ListSelectionEvent;
029import javax.swing.event.ListSelectionListener;
030
031import org.openstreetmap.josm.Main;
032import org.openstreetmap.josm.data.osm.Changeset;
033import org.openstreetmap.josm.gui.SideButton;
034import org.openstreetmap.josm.tools.ImageProvider;
035import org.openstreetmap.josm.tools.InputMapUtils;
036import org.openstreetmap.josm.tools.WindowGeometry;
037
038/**
039 * This dialog lets the user select changesets from a list of changesets.
040 * @since 2115
041 */
042public class CloseChangesetDialog extends JDialog {
043
044    /** the list */
045    private JList<Changeset> lstOpenChangesets;
046    /** true if the user canceled the dialog */
047    private boolean canceled;
048    /** the list model */
049    private DefaultListModel<Changeset> model;
050
051    private SideButton btnCloseChangesets;
052
053    protected JPanel buildTopPanel() {
054        JPanel pnl = new JPanel();
055        pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
056        pnl.setLayout(new BorderLayout());
057        pnl.add(new JLabel(tr("<html>Please select the changesets you want to close</html>")), BorderLayout.CENTER);
058        return pnl;
059    }
060
061    protected JPanel buildCenterPanel() {
062        JPanel pnl = new JPanel();
063        pnl.setLayout(new BorderLayout());
064        model = new DefaultListModel<>();
065        pnl.add(new JScrollPane(lstOpenChangesets = new JList<>(model)), BorderLayout.CENTER);
066        lstOpenChangesets.setCellRenderer(new ChangesetCellRenderer());
067        return pnl;
068    }
069
070    protected JPanel buildSouthPanel() {
071        JPanel pnl = new JPanel();
072        pnl.setLayout(new FlowLayout(FlowLayout.CENTER));
073
074        // -- close action
075        CloseAction closeAction = new CloseAction();
076        lstOpenChangesets.addListSelectionListener(closeAction);
077        pnl.add(btnCloseChangesets = new SideButton(closeAction));
078        InputMapUtils.enableEnter(btnCloseChangesets);
079
080        // -- cancel action
081        SideButton btn;
082        pnl.add(btn = new SideButton(new CancelAction()));
083        btn.setFocusable(true);
084        return pnl;
085    }
086
087    protected void build() {
088        setTitle(tr("Open changesets"));
089        getContentPane().setLayout(new BorderLayout());
090        getContentPane().add(buildTopPanel(), BorderLayout.NORTH);
091        getContentPane().add(buildCenterPanel(), BorderLayout.CENTER);
092        getContentPane().add(buildSouthPanel(), BorderLayout.SOUTH);
093
094        getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0), "escape");
095        getRootPane().getActionMap().put("escape", new CancelAction());
096        addWindowListener(new WindowEventHandler());
097    }
098
099    @Override
100    public void setVisible(boolean visible) {
101        if (visible) {
102            new WindowGeometry(
103                    getClass().getName() + ".geometry",
104                    WindowGeometry.centerInWindow(Main.parent, new Dimension(300,300))
105            ).applySafe(this);
106        } else if (isShowing()) { // Avoid IllegalComponentStateException like in #8775
107            new WindowGeometry(this).remember(getClass().getName() + ".geometry");
108        }
109        super.setVisible(visible);
110    }
111
112    /**
113     * Constructs a new {@code CloseChangesetDialog}.
114     */
115    public CloseChangesetDialog() {
116        super(JOptionPane.getFrameForComponent(Main.parent), ModalityType.DOCUMENT_MODAL);
117        build();
118    }
119
120    class CloseAction extends AbstractAction implements ListSelectionListener {
121        public CloseAction() {
122            putValue(NAME, tr("Close changesets"));
123            putValue(SMALL_ICON, ImageProvider.get("closechangeset"));
124            putValue(SHORT_DESCRIPTION, tr("Close the selected open changesets"));
125            refreshEnabledState();
126        }
127
128        @Override
129        public void actionPerformed(ActionEvent e) {
130            setCanceled(false);
131            setVisible(false);
132        }
133
134        protected void refreshEnabledState() {
135            List<Changeset> list = lstOpenChangesets.getSelectedValuesList();
136            setEnabled(list != null && !list.isEmpty());
137        }
138
139        @Override
140        public void valueChanged(ListSelectionEvent e) {
141            refreshEnabledState();
142        }
143    }
144
145    class CancelAction extends AbstractAction {
146
147        public CancelAction() {
148            putValue(NAME, tr("Cancel"));
149            putValue(SMALL_ICON, ImageProvider.get("cancel"));
150            putValue(SHORT_DESCRIPTION, tr("Cancel closing of changesets"));
151        }
152
153        public void cancel() {
154            setCanceled(true);
155            setVisible(false);
156        }
157
158        @Override
159        public void actionPerformed(ActionEvent e) {
160            cancel();
161        }
162    }
163
164    class WindowEventHandler extends WindowAdapter {
165
166        @Override
167        public void windowActivated(WindowEvent arg0) {
168            btnCloseChangesets.requestFocusInWindow();
169        }
170
171        @Override
172        public void windowClosing(WindowEvent arg0) {
173            new CancelAction().cancel();
174        }
175
176    }
177
178    /**
179     * Replies true if this dialog was canceled
180     * @return true if this dialog was canceled
181     */
182    public boolean isCanceled() {
183        return canceled;
184    }
185
186    /**
187     * Sets whether this dialog is canceled
188     *
189     * @param canceled true, if this dialog is canceld
190     */
191    protected void setCanceled(boolean canceled) {
192        this.canceled = canceled;
193    }
194
195    /**
196     * Sets the collection of changesets to be displayed
197     *
198     * @param changesets the collection of changesets. Assumes an empty collection if null
199     */
200    public void setChangesets(Collection<Changeset> changesets) {
201        if (changesets == null) {
202            changesets = new ArrayList<>();
203        }
204        model.removeAllElements();
205        for (Changeset cs: changesets) {
206            model.addElement(cs);
207        }
208        if (!changesets.isEmpty()) {
209            lstOpenChangesets.getSelectionModel().setSelectionInterval(0, changesets.size()-1);
210        }
211    }
212
213    /**
214     * Replies a collection with the changesets the user selected.
215     * Never null, but may be empty.
216     *
217     * @return a collection with the changesets the user selected.
218     */
219    public Collection<Changeset> getSelectedChangesets() {
220        return lstOpenChangesets.getSelectedValuesList();
221    }
222}