001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.io;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005import static org.openstreetmap.josm.tools.I18n.trn;
006
007import java.awt.GridBagConstraints;
008import java.awt.GridBagLayout;
009import java.awt.event.MouseAdapter;
010import java.awt.event.MouseEvent;
011import java.util.ArrayList;
012import java.util.Collections;
013import java.util.List;
014import java.util.Optional;
015
016import javax.swing.AbstractListModel;
017import javax.swing.JLabel;
018import javax.swing.JList;
019import javax.swing.JPanel;
020import javax.swing.JScrollPane;
021
022import org.openstreetmap.josm.actions.AutoScaleAction;
023import org.openstreetmap.josm.data.osm.OsmPrimitive;
024import org.openstreetmap.josm.gui.PrimitiveRenderer;
025
026/**
027 * This panel displays a summary of the objects to upload. It is displayed in the upper part of the {@link UploadDialog}.
028 * @since 2599
029 */
030public class UploadedObjectsSummaryPanel extends JPanel {
031    /** the list with the added primitives */
032    private PrimitiveList lstAdd;
033    private JLabel lblAdd;
034    private JScrollPane spAdd;
035    /** the list with the updated primitives */
036    private PrimitiveList lstUpdate;
037    private JLabel lblUpdate;
038    private JScrollPane spUpdate;
039    /** the list with the deleted primitives */
040    private PrimitiveList lstDelete;
041    private JLabel lblDelete;
042    private JScrollPane spDelete;
043
044    /**
045     * Constructs a new {@code UploadedObjectsSummaryPanel}.
046     */
047    public UploadedObjectsSummaryPanel() {
048        build();
049    }
050
051    protected void build() {
052        setLayout(new GridBagLayout());
053        PrimitiveRenderer renderer = new PrimitiveRenderer();
054        MouseAdapter mouseListener = new MouseAdapter() {
055            @Override
056            public void mouseClicked(MouseEvent evt) {
057                if (evt.getButton() == MouseEvent.BUTTON1 && evt.getClickCount() == 2) {
058                    PrimitiveList list = (PrimitiveList) evt.getSource();
059                    int index = list.locationToIndex(evt.getPoint());
060                    AutoScaleAction.zoomTo(Collections.singleton(list.getModel().getElementAt(index)));
061                }
062            }
063        };
064        // initialize the three lists for uploaded primitives, but don't add them to the dialog yet, see setUploadedPrimitives()
065        //
066        lstAdd = new PrimitiveList();
067        lstAdd.setCellRenderer(renderer);
068        lstAdd.addMouseListener(mouseListener);
069        lstAdd.setVisibleRowCount(Math.min(lstAdd.getModel().getSize(), 10));
070        spAdd = new JScrollPane(lstAdd);
071        lblAdd = new JLabel(tr("Objects to add:"));
072        lblAdd.setLabelFor(lstAdd);
073
074        lstUpdate = new PrimitiveList();
075        lstUpdate.setCellRenderer(renderer);
076        lstUpdate.addMouseListener(mouseListener);
077        lstUpdate.setVisibleRowCount(Math.min(lstUpdate.getModel().getSize(), 10));
078        spUpdate = new JScrollPane(lstUpdate);
079        lblUpdate = new JLabel(tr("Objects to modify:"));
080        lblUpdate.setLabelFor(lstUpdate);
081
082        lstDelete = new PrimitiveList();
083        lstDelete.setCellRenderer(renderer);
084        lstDelete.addMouseListener(mouseListener);
085        lstDelete.setVisibleRowCount(Math.min(lstDelete.getModel().getSize(), 10));
086        spDelete = new JScrollPane(lstDelete);
087        lblDelete = new JLabel(tr("Objects to delete:"));
088        lblDelete.setLabelFor(lstDelete);
089    }
090
091    /**
092     * Sets the collections of primitives which will be uploaded
093     *
094     * @param add  the collection of primitives to add
095     * @param update the collection of primitives to update
096     * @param delete the collection of primitives to delete
097     */
098    public void setUploadedPrimitives(List<OsmPrimitive> add, List<OsmPrimitive> update, List<OsmPrimitive> delete) {
099        lstAdd.getPrimitiveListModel().setPrimitives(add);
100        lstUpdate.getPrimitiveListModel().setPrimitives(update);
101        lstDelete.getPrimitiveListModel().setPrimitives(delete);
102
103        GridBagConstraints gcLabel = new GridBagConstraints();
104        gcLabel.fill = GridBagConstraints.HORIZONTAL;
105        gcLabel.weightx = 1.0;
106        gcLabel.weighty = 0.0;
107        gcLabel.anchor = GridBagConstraints.FIRST_LINE_START;
108
109        GridBagConstraints gcList = new GridBagConstraints();
110        gcList.fill = GridBagConstraints.BOTH;
111        gcList.weightx = 1.0;
112        gcList.weighty = 1.0;
113        gcList.anchor = GridBagConstraints.CENTER;
114        removeAll();
115        int y = -1;
116        if (!add.isEmpty()) {
117            y++;
118            gcLabel.gridy = y;
119            lblAdd.setText(trn("{0} object to add:", "{0} objects to add:", add.size(), add.size()));
120            add(lblAdd, gcLabel);
121            y++;
122            gcList.gridy = y;
123            add(spAdd, gcList);
124        }
125        if (!update.isEmpty()) {
126            y++;
127            gcLabel.gridy = y;
128            lblUpdate.setText(trn("{0} object to modify:", "{0} objects to modify:", update.size(), update.size()));
129            add(lblUpdate, gcLabel);
130            y++;
131            gcList.gridy = y;
132            add(spUpdate, gcList);
133        }
134        if (!delete.isEmpty()) {
135            y++;
136            gcLabel.gridy = y;
137            lblDelete.setText(trn("{0} object to delete:", "{0} objects to delete:", delete.size(), delete.size()));
138            add(lblDelete, gcLabel);
139            y++;
140            gcList.gridy = y;
141            add(spDelete, gcList);
142        }
143        revalidate();
144    }
145
146    /**
147     * Replies the number of objects to upload
148     *
149     * @return the number of objects to upload
150     */
151    public int getNumObjectsToUpload() {
152        return lstAdd.getModel().getSize()
153        + lstUpdate.getModel().getSize()
154        + lstDelete.getModel().getSize();
155    }
156
157    /**
158     * A simple list of OSM primitives.
159     */
160    static class PrimitiveList extends JList<OsmPrimitive> {
161        /**
162         * Constructs a new {@code PrimitiveList}.
163         */
164        PrimitiveList() {
165            super(new PrimitiveListModel());
166        }
167
168        public PrimitiveListModel getPrimitiveListModel() {
169            return (PrimitiveListModel) getModel();
170        }
171    }
172
173    /**
174     * A list model for a list of OSM primitives.
175     */
176    static class PrimitiveListModel extends AbstractListModel<OsmPrimitive> {
177        private transient List<OsmPrimitive> primitives;
178
179        /**
180         * Constructs a new {@code PrimitiveListModel}.
181         */
182        PrimitiveListModel() {
183            primitives = new ArrayList<>();
184        }
185
186        PrimitiveListModel(List<OsmPrimitive> primitives) {
187            setPrimitives(primitives);
188        }
189
190        public void setPrimitives(List<OsmPrimitive> primitives) {
191            this.primitives = Optional.ofNullable(primitives).orElseGet(ArrayList::new);
192            fireContentsChanged(this, 0, getSize());
193        }
194
195        @Override
196        public OsmPrimitive getElementAt(int index) {
197            if (primitives == null) return null;
198            return primitives.get(index);
199        }
200
201        @Override
202        public int getSize() {
203            if (primitives == null) return 0;
204            return primitives.size();
205        }
206    }
207}