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