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