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