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 *
041 */
042public class CloseChangesetDialog extends JDialog {
043
044    /** the list */
045    private JList lstOpenChangesets;
046    /** true if the user canceled the dialog */
047    private boolean canceled;
048    /** the list model */
049    private DefaultListModel 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    public CloseChangesetDialog() {
113        super(JOptionPane.getFrameForComponent(Main.parent), ModalityType.DOCUMENT_MODAL);
114        build();
115    }
116
117    class CloseAction extends AbstractAction implements ListSelectionListener {
118        public CloseAction() {
119            putValue(NAME, tr("Close changesets"));
120            putValue(SMALL_ICON, ImageProvider.get("closechangeset"));
121            putValue(SHORT_DESCRIPTION, tr("Close the selected open changesets"));
122            refreshEnabledState();
123        }
124
125        @Override
126        public void actionPerformed(ActionEvent e) {
127            setCanceled(false);
128            setVisible(false);
129        }
130
131        protected void refreshEnabledState() {
132            setEnabled(lstOpenChangesets.getSelectedValues() != null && lstOpenChangesets.getSelectedValues().length > 0);
133        }
134
135        @Override
136        public void valueChanged(ListSelectionEvent e) {
137            refreshEnabledState();
138        }
139    }
140
141    class CancelAction extends AbstractAction {
142
143        public CancelAction() {
144            putValue(NAME, tr("Cancel"));
145            putValue(SMALL_ICON, ImageProvider.get("cancel"));
146            putValue(SHORT_DESCRIPTION, tr("Cancel closing of changesets"));
147        }
148
149        public void cancel() {
150            setCanceled(true);
151            setVisible(false);
152        }
153
154        @Override
155        public void actionPerformed(ActionEvent e) {
156            cancel();
157        }
158    }
159
160    class WindowEventHandler extends WindowAdapter {
161
162        @Override
163        public void windowActivated(WindowEvent arg0) {
164            btnCloseChangesets.requestFocusInWindow();
165        }
166
167        @Override
168        public void windowClosing(WindowEvent arg0) {
169            new CancelAction().cancel();
170        }
171
172    }
173
174    /**
175     * Replies true if this dialog was canceled
176     * @return true if this dialog was canceled
177     */
178    public boolean isCanceled() {
179        return canceled;
180    }
181
182    /**
183     * Sets whether this dialog is canceled
184     *
185     * @param canceled true, if this dialog is canceld
186     */
187    protected void setCanceled(boolean canceled) {
188        this.canceled = canceled;
189    }
190
191    /**
192     * Sets the collection of changesets to be displayed
193     *
194     * @param changesets the collection of changesets. Assumes an empty collection if null
195     */
196    public void setChangesets(Collection<Changeset> changesets) {
197        if (changesets == null) {
198            changesets = new ArrayList<Changeset>();
199        }
200        model.removeAllElements();
201        for (Changeset cs: changesets) {
202            model.addElement(cs);
203        }
204        if (!changesets.isEmpty()) {
205            lstOpenChangesets.getSelectionModel().setSelectionInterval(0, changesets.size()-1);
206        }
207    }
208
209    /**
210     * Replies a collection with the changesets the user selected.
211     * Never null, but may be empty.
212     *
213     * @return a collection with the changesets the user selected.
214     */
215    public Collection<Changeset> getSelectedChangesets() {
216        Object [] sel = lstOpenChangesets.getSelectedValues();
217        List<Changeset> ret = new ArrayList<Changeset>(sel.length);
218        for (Object o: sel) {
219            ret.add((Changeset)o);
220        }
221        return ret;
222    }
223}