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    /**
032     * The swing property name for the number of objects to upload
033     */
034    public static final String NUM_OBJECTS_TO_UPLOAD_PROP = UploadedObjectsSummaryPanel.class.getName() + ".numObjectsToUpload";
035
036    /** the list with the added primitives */
037    private PrimitiveList lstAdd;
038    private JLabel lblAdd;
039    private JScrollPane spAdd;
040    /** the list with the updated primitives */
041    private PrimitiveList lstUpdate;
042    private JLabel lblUpdate;
043    private JScrollPane spUpdate;
044    /** the list with the deleted primitives */
045    private PrimitiveList lstDelete;
046    private JLabel lblDelete;
047    private JScrollPane spDelete;
048
049    /**
050     * Constructs a new {@code UploadedObjectsSummaryPanel}.
051     */
052    public UploadedObjectsSummaryPanel() {
053        build();
054    }
055
056    protected void build() {
057        setLayout(new GridBagLayout());
058        PrimitiveRenderer renderer = new PrimitiveRenderer();
059        MouseAdapter mouseListener = new MouseAdapter() {
060            @Override
061            public void mouseClicked(MouseEvent evt) {
062                if (evt.getButton() == MouseEvent.BUTTON1 && evt.getClickCount() == 2) {
063                    PrimitiveList list = (PrimitiveList) evt.getSource();
064                    int index = list.locationToIndex(evt.getPoint());
065                    AutoScaleAction.zoomTo(Collections.singleton(list.getModel().getElementAt(index)));
066                }
067            }
068        };
069        // initialize the three lists for uploaded primitives, but don't add them to the dialog yet, see setUploadedPrimitives()
070        //
071        lstAdd = new PrimitiveList();
072        lstAdd.setCellRenderer(renderer);
073        lstAdd.addMouseListener(mouseListener);
074        lstAdd.setVisibleRowCount(Math.min(lstAdd.getModel().getSize(), 10));
075        spAdd = new JScrollPane(lstAdd);
076        lblAdd = new JLabel(tr("Objects to add:"));
077        lblAdd.setLabelFor(lstAdd);
078
079        lstUpdate = new PrimitiveList();
080        lstUpdate.setCellRenderer(renderer);
081        lstUpdate.addMouseListener(mouseListener);
082        lstUpdate.setVisibleRowCount(Math.min(lstUpdate.getModel().getSize(), 10));
083        spUpdate = new JScrollPane(lstUpdate);
084        lblUpdate = new JLabel(tr("Objects to modify:"));
085        lblUpdate.setLabelFor(lstUpdate);
086
087        lstDelete = new PrimitiveList();
088        lstDelete.setCellRenderer(renderer);
089        lstDelete.addMouseListener(mouseListener);
090        lstDelete.setVisibleRowCount(Math.min(lstDelete.getModel().getSize(), 10));
091        spDelete = new JScrollPane(lstDelete);
092        lblDelete = new JLabel(tr("Objects to delete:"));
093        lblDelete.setLabelFor(lstDelete);
094    }
095
096    /**
097     * Sets the collections of primitives which will be uploaded
098     *
099     * @param add  the collection of primitives to add
100     * @param update the collection of primitives to update
101     * @param delete the collection of primitives to delete
102     */
103    public void setUploadedPrimitives(List<OsmPrimitive> add, List<OsmPrimitive> update, List<OsmPrimitive> delete) {
104        lstAdd.getPrimitiveListModel().setPrimitives(add);
105        lstUpdate.getPrimitiveListModel().setPrimitives(update);
106        lstDelete.getPrimitiveListModel().setPrimitives(delete);
107
108        GridBagConstraints gcLabel = new GridBagConstraints();
109        gcLabel.fill = GridBagConstraints.HORIZONTAL;
110        gcLabel.weightx = 1.0;
111        gcLabel.weighty = 0.0;
112        gcLabel.anchor = GridBagConstraints.FIRST_LINE_START;
113
114        GridBagConstraints gcList = new GridBagConstraints();
115        gcList.fill = GridBagConstraints.BOTH;
116        gcList.weightx = 1.0;
117        gcList.weighty = 1.0;
118        gcList.anchor = GridBagConstraints.CENTER;
119        removeAll();
120        int y = -1;
121        if (!add.isEmpty()) {
122            y++;
123            gcLabel.gridy = y;
124            lblAdd.setText(trn("{0} object to add:", "{0} objects to add:", add.size(), add.size()));
125            add(lblAdd, gcLabel);
126            y++;
127            gcList.gridy = y;
128            add(spAdd, gcList);
129        }
130        if (!update.isEmpty()) {
131            y++;
132            gcLabel.gridy = y;
133            lblUpdate.setText(trn("{0} object to modify:", "{0} objects to modify:", update.size(), update.size()));
134            add(lblUpdate, gcLabel);
135            y++;
136            gcList.gridy = y;
137            add(spUpdate, gcList);
138        }
139        if (!delete.isEmpty()) {
140            y++;
141            gcLabel.gridy = y;
142            lblDelete.setText(trn("{0} object to delete:", "{0} objects to delete:", delete.size(), delete.size()));
143            add(lblDelete, gcLabel);
144            y++;
145            gcList.gridy = y;
146            add(spDelete, gcList);
147        }
148
149        firePropertyChange(NUM_OBJECTS_TO_UPLOAD_PROP, 0, getNumObjectsToUpload());
150    }
151
152    /**
153     * Replies the number of objects to upload
154     *
155     * @return the number of objects to upload
156     */
157    public int getNumObjectsToUpload() {
158        return lstAdd.getModel().getSize()
159        + lstUpdate.getModel().getSize()
160        + lstDelete.getModel().getSize();
161    }
162
163    /**
164     * A simple list of OSM primitives.
165     */
166    static class PrimitiveList extends JList<OsmPrimitive> {
167        /**
168         * Constructs a new {@code PrimitiveList}.
169         */
170        PrimitiveList() {
171            super(new PrimitiveListModel());
172        }
173
174        public PrimitiveListModel getPrimitiveListModel() {
175            return (PrimitiveListModel) getModel();
176        }
177    }
178
179    /**
180     * A list model for a list of OSM primitives.
181     */
182    static class PrimitiveListModel extends AbstractListModel<OsmPrimitive> {
183        private transient List<OsmPrimitive> primitives;
184
185        /**
186         * Constructs a new {@code PrimitiveListModel}.
187         */
188        PrimitiveListModel() {
189            primitives = new ArrayList<>();
190        }
191
192        PrimitiveListModel(List<OsmPrimitive> primitives) {
193            setPrimitives(primitives);
194        }
195
196        public void setPrimitives(List<OsmPrimitive> primitives) {
197            this.primitives = Optional.ofNullable(primitives).orElseGet(ArrayList::new);
198            fireContentsChanged(this, 0, getSize());
199        }
200
201        @Override
202        public OsmPrimitive getElementAt(int index) {
203            if (primitives == null) return null;
204            return primitives.get(index);
205        }
206
207        @Override
208        public int getSize() {
209            if (primitives == null) return 0;
210            return primitives.size();
211        }
212    }
213}