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;
014import java.util.List;
015
016import javax.swing.AbstractAction;
017import javax.swing.BorderFactory;
018import javax.swing.DefaultListModel;
019import javax.swing.JDialog;
020import javax.swing.JLabel;
021import javax.swing.JList;
022import javax.swing.JPanel;
023import javax.swing.JScrollPane;
024import javax.swing.event.ListSelectionEvent;
025import javax.swing.event.ListSelectionListener;
026
027import org.openstreetmap.josm.data.osm.Changeset;
028import org.openstreetmap.josm.gui.MainApplication;
029import org.openstreetmap.josm.gui.SideButton;
030import org.openstreetmap.josm.gui.util.GuiHelper;
031import org.openstreetmap.josm.gui.util.WindowGeometry;
032import org.openstreetmap.josm.tools.ImageProvider;
033import org.openstreetmap.josm.tools.InputMapUtils;
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            List<Changeset> list = lstOpenChangesets.getSelectedValuesList();
131            setEnabled(list != null && !list.isEmpty());
132        }
133
134        @Override
135        public void valueChanged(ListSelectionEvent e) {
136            refreshEnabledState();
137        }
138    }
139
140    class CancelAction extends AbstractAction {
141
142        CancelAction() {
143            putValue(NAME, tr("Cancel"));
144            new ImageProvider("cancel").getResource().attachImageIcon(this, true);
145            putValue(SHORT_DESCRIPTION, tr("Cancel closing of changesets"));
146        }
147
148        public void cancel() {
149            setCanceled(true);
150            setVisible(false);
151        }
152
153        @Override
154        public void actionPerformed(ActionEvent e) {
155            cancel();
156        }
157    }
158
159    class WindowEventHandler extends WindowAdapter {
160
161        @Override
162        public void windowActivated(WindowEvent arg0) {
163            btnCloseChangesets.requestFocusInWindow();
164        }
165
166        @Override
167        public void windowClosing(WindowEvent arg0) {
168            new CancelAction().cancel();
169        }
170
171    }
172
173    /**
174     * Replies true if this dialog was canceled
175     * @return true if this dialog was canceled
176     */
177    public boolean isCanceled() {
178        return canceled;
179    }
180
181    /**
182     * Sets whether this dialog is canceled
183     *
184     * @param canceled true, if this dialog is canceld
185     */
186    protected void setCanceled(boolean canceled) {
187        this.canceled = canceled;
188    }
189
190    /**
191     * Sets the collection of changesets to be displayed
192     *
193     * @param changesets the collection of changesets. Assumes an empty collection if null
194     */
195    public void setChangesets(Collection<Changeset> changesets) {
196        if (changesets == null) {
197            changesets = new ArrayList<>();
198        }
199        model.removeAllElements();
200        for (Changeset cs: changesets) {
201            model.addElement(cs);
202        }
203        if (!changesets.isEmpty()) {
204            lstOpenChangesets.getSelectionModel().setSelectionInterval(0, changesets.size()-1);
205        }
206    }
207
208    /**
209     * Replies a collection with the changesets the user selected.
210     * Never null, but may be empty.
211     *
212     * @return a collection with the changesets the user selected.
213     */
214    public Collection<Changeset> getSelectedChangesets() {
215        return lstOpenChangesets.getSelectedValuesList();
216    }
217}