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