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