001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.dialogs.relation;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.BorderLayout;
007import java.awt.FlowLayout;
008import java.awt.Rectangle;
009import java.awt.event.ActionEvent;
010import java.awt.event.MouseAdapter;
011import java.awt.event.MouseEvent;
012import java.util.Collections;
013import java.util.List;
014import java.util.stream.Collectors;
015
016import javax.swing.AbstractAction;
017import javax.swing.JButton;
018import javax.swing.JCheckBox;
019import javax.swing.JList;
020import javax.swing.JPanel;
021import javax.swing.JScrollPane;
022import javax.swing.ListSelectionModel;
023import javax.swing.event.ListDataEvent;
024import javax.swing.event.ListDataListener;
025import javax.swing.event.ListSelectionEvent;
026import javax.swing.event.ListSelectionListener;
027
028import org.openstreetmap.josm.actions.downloadtasks.DownloadReferrersTask;
029import org.openstreetmap.josm.data.osm.OsmPrimitive;
030import org.openstreetmap.josm.data.osm.PrimitiveId;
031import org.openstreetmap.josm.data.osm.Relation;
032import org.openstreetmap.josm.gui.MainApplication;
033import org.openstreetmap.josm.gui.PrimitiveRenderer;
034import org.openstreetmap.josm.gui.io.DownloadPrimitivesTask;
035import org.openstreetmap.josm.gui.layer.OsmDataLayer;
036import org.openstreetmap.josm.gui.util.GuiHelper;
037import org.openstreetmap.josm.tools.ImageProvider;
038
039/**
040 * This is browser for a list of relations which refer to another relations.
041 * @since 1806
042 */
043public class ReferringRelationsBrowser extends JPanel {
044
045    /** the list of relations */
046    private JList<Relation> referrers;
047    private final ReferringRelationsBrowserModel model;
048    private final transient OsmDataLayer layer;
049    private final JCheckBox cbReadFull = new JCheckBox(tr("including immediate children of parent relations"));
050    private EditAction editAction;
051
052    /**
053     * Constructs a new {@code ReferringRelationsBrowser}.
054     * @param layer OSM data layer
055     * @param model referring relations browser model
056     */
057    public ReferringRelationsBrowser(OsmDataLayer layer, ReferringRelationsBrowserModel model) {
058        this.model = model;
059        this.layer = layer;
060        build();
061    }
062
063    /**
064     * build the GUI
065     */
066    protected void build() {
067        setLayout(new BorderLayout());
068        referrers = new JList<>(model);
069        referrers.setCellRenderer(new PrimitiveRenderer());
070        add(new JScrollPane(referrers), BorderLayout.CENTER);
071        referrers.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
072        referrers.addMouseListener(new MouseAdapter() {
073            @Override
074            public void mouseClicked(MouseEvent e) {
075                if (e.getClickCount() == 2
076                    && !e.isAltDown() && !e.isAltGraphDown() && !e.isControlDown() && !e.isMetaDown() && !e.isShiftDown()) {
077                    Rectangle cellBounds = referrers.getCellBounds(referrers.getSelectedIndex(), referrers.getSelectedIndex());
078                    if (cellBounds != null && cellBounds.contains(e.getPoint())) {
079                        editAction.actionPerformed(new ActionEvent(e.getSource(), ActionEvent.ACTION_PERFORMED, null));
080                    }
081                }
082            }
083        });
084
085        JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
086
087        ReloadAction reloadAction = new ReloadAction();
088        referrers.getModel().addListDataListener(reloadAction);
089        pnl.add(new JButton(reloadAction));
090        pnl.add(cbReadFull);
091
092        editAction = new EditAction();
093        referrers.getSelectionModel().addListSelectionListener(editAction);
094        pnl.add(new JButton(editAction));
095        add(pnl, BorderLayout.SOUTH);
096    }
097
098    /**
099     * Initializes the model with layer data.
100     */
101    public void init() {
102        model.populate(getLayer().data);
103    }
104
105    protected OsmDataLayer getLayer() {
106        return layer;
107    }
108
109    /**
110     * Action for loading the parent relations of a relation
111     *
112     */
113    class ReloadAction extends AbstractAction implements ListDataListener {
114        ReloadAction() {
115            putValue(SHORT_DESCRIPTION, tr("Load parent relations"));
116            new ImageProvider("dialogs", "refresh").getResource().attachImageIcon(this);
117            putValue(NAME, tr("Reload"));
118            refreshEnabled();
119        }
120
121        protected void refreshEnabled() {
122            setEnabled(model.canReload());
123        }
124
125        @Override
126        public void actionPerformed(ActionEvent e) {
127            DownloadReferrersTask task = new DownloadReferrersTask(getLayer(), Collections.singleton(model.getRelation()));
128            MainApplication.worker.submit(task);
129            MainApplication.worker.submit(() -> {
130                if (cbReadFull.isSelected() && !task.getProgressMonitor().isCanceled()) {
131                    // download all members of parents
132                    List<PrimitiveId> parentsChildren = model.getRelation().referrers(Relation.class)
133                            .collect(Collectors.toSet()).stream().flatMap(r -> r.getMemberPrimitives().stream())
134                            .distinct().map(OsmPrimitive::getPrimitiveId).distinct().collect(Collectors.toList());
135                    new DownloadPrimitivesTask(getLayer(), parentsChildren, false).run();
136                }
137            });
138            GuiHelper.executeByMainWorkerInEDT(() ->
139                model.populate(model.getRelation().getReferrers().stream()
140                        .filter(Relation.class::isInstance)
141                        .map(Relation.class::cast)
142                        .collect(Collectors.toList()))
143                );
144        }
145
146        @Override
147        public void contentsChanged(ListDataEvent e) {
148            refreshEnabled();
149        }
150
151        @Override
152        public void intervalAdded(ListDataEvent e) {
153            refreshEnabled();
154        }
155
156        @Override
157        public void intervalRemoved(ListDataEvent e) {
158            refreshEnabled();
159        }
160    }
161
162    /**
163     * Action for editing the currently selected relation
164     *
165     */
166    class EditAction extends AbstractAction implements ListSelectionListener {
167        EditAction() {
168            putValue(SHORT_DESCRIPTION, tr("Edit the currently selected relation"));
169            new ImageProvider("dialogs", "edit").getResource().attachImageIcon(this);
170            putValue(NAME, tr("Edit"));
171            refreshEnabled();
172        }
173
174        protected void refreshEnabled() {
175            setEnabled(referrers.getSelectionModel().getMinSelectionIndex() >= 0);
176        }
177
178        @Override
179        public void actionPerformed(ActionEvent e) {
180            run();
181        }
182
183        public void run() {
184            int idx = referrers.getSelectedIndex();
185            if (idx < 0)
186                return;
187            Relation r = model.getElementAt(idx);
188            if (r == null)
189                return;
190            RelationEditor editor = RelationEditor.getEditor(getLayer(), r, null);
191            editor.setVisible(true);
192        }
193
194        @Override
195        public void valueChanged(ListSelectionEvent e) {
196            refreshEnabled();
197        }
198    }
199}