001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.download;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.GridBagConstraints;
007import java.awt.GridBagLayout;
008import java.awt.Insets;
009import java.awt.event.ActionEvent;
010import java.awt.event.MouseAdapter;
011import java.awt.event.MouseEvent;
012import java.util.List;
013
014import javax.swing.AbstractAction;
015import javax.swing.DefaultListModel;
016import javax.swing.JButton;
017import javax.swing.JOptionPane;
018import javax.swing.JPanel;
019import javax.swing.JScrollPane;
020import javax.swing.SwingUtilities;
021import javax.swing.event.ListSelectionEvent;
022import javax.swing.event.ListSelectionListener;
023
024import org.openstreetmap.josm.Main;
025import org.openstreetmap.josm.data.Bounds;
026import org.openstreetmap.josm.gui.download.BookmarkList.Bookmark;
027import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
028import org.openstreetmap.josm.gui.widgets.JosmTextArea;
029import org.openstreetmap.josm.tools.ImageProvider;
030
031/**
032 * DownloadAreaSelector which manages a list of "bookmarks", i.e. a list of
033 * name download areas.
034 *
035 */
036public class BookmarkSelection implements DownloadSelection {
037
038    /** the currently selected download area. One can add bookmarks for this
039     * area, if not null
040     */
041    private Bounds currentArea;
042    /** the list of bookmarks */
043    private BookmarkList bookmarks;
044
045    /** the parent download GUI */
046    private DownloadDialog parent;
047
048    /** displays information about the current download area */
049    private final JMultilineLabel lblCurrentDownloadArea = new JMultilineLabel("");
050    private final JosmTextArea bboxDisplay = new JosmTextArea();
051    /** the add action */
052    private final AddAction actAdd = new AddAction();
053
054    /**
055     * Creates the panel with the action buttons on the left
056     *
057     * @return the panel with the action buttons on the left
058     */
059    protected JPanel buildButtonPanel() {
060        JPanel pnl = new JPanel(new GridBagLayout());
061        GridBagConstraints gc = new GridBagConstraints();
062        gc.gridy = 0;
063        RemoveAction removeAction = new RemoveAction();
064        bookmarks.addListSelectionListener(removeAction);
065        pnl.add(new JButton(removeAction), gc);
066
067        gc.gridy = 1;
068        RenameAction renameAction = new RenameAction();
069        bookmarks.addListSelectionListener(renameAction);
070        pnl.add(new JButton(renameAction), gc);
071
072        gc.fill = GridBagConstraints.BOTH;
073        gc.weightx = 1.0;
074        gc.weighty = 1.0;
075        gc.gridy = 3;
076        pnl.add(new JPanel(), gc); // just a filler
077        return pnl;
078    }
079
080    protected JPanel buildDownloadAreaAddPanel() {
081        JPanel pnl = new JPanel(new GridBagLayout());
082
083        GridBagConstraints gc = new GridBagConstraints();
084        gc.anchor = GridBagConstraints.NORTHWEST;
085        gc.insets = new Insets(5, 5, 5, 5);
086        pnl.add(lblCurrentDownloadArea, gc);
087
088        gc.weightx = 1.0;
089        gc.weighty = 1.0;
090        bboxDisplay.setEditable(false);
091        bboxDisplay.setBackground(pnl.getBackground());
092        bboxDisplay.addFocusListener(new BoundingBoxSelection.SelectAllOnFocusHandler(bboxDisplay));
093        pnl.add(bboxDisplay, gc);
094
095        gc.anchor = GridBagConstraints.NORTHEAST;
096        gc.fill = GridBagConstraints.HORIZONTAL;
097        gc.weightx = 0.0;
098        gc.weighty = 0.0;
099        gc.insets = new Insets(5, 5, 5, 5);
100        pnl.add(new JButton(actAdd), gc);
101        return pnl;
102    }
103
104    @Override
105    public void addGui(final DownloadDialog gui) {
106        JPanel dlg = new JPanel(new GridBagLayout());
107        if (gui != null)
108            gui.addDownloadAreaSelector(dlg, tr("Bookmarks"));
109        GridBagConstraints gc = new GridBagConstraints();
110
111        bookmarks = new BookmarkList();
112        bookmarks.getSelectionModel().addListSelectionListener(e -> {
113            Bookmark b = bookmarks.getSelectedValue();
114            if (b != null && gui != null) {
115                gui.boundingBoxChanged(b.getArea(), this);
116            }
117        });
118        bookmarks.addMouseListener(new DoubleClickAdapter());
119
120        gc.fill = GridBagConstraints.HORIZONTAL;
121        gc.weightx = 1.0;
122        gc.weighty = 0.0;
123        gc.gridwidth = 2;
124        dlg.add(buildDownloadAreaAddPanel(), gc);
125
126        gc.gridwidth = 1;
127        gc.gridx = 0;
128        gc.gridy = 1;
129        gc.fill = GridBagConstraints.VERTICAL;
130        gc.weightx = 0.0;
131        gc.weighty = 1.0;
132        dlg.add(buildButtonPanel(), gc);
133
134        gc.gridwidth = 1;
135        gc.gridx = 1;
136        gc.gridy = 1;
137        gc.fill = GridBagConstraints.BOTH;
138        gc.weightx = 1.0;
139        gc.weighty = 1.0;
140        gc.gridx = 1;
141        dlg.add(new JScrollPane(bookmarks), gc);
142
143        this.parent = gui;
144    }
145
146    protected void updateDownloadAreaLabel() {
147        if (currentArea == null) {
148            lblCurrentDownloadArea.setText(tr("<html>There is currently no download area selected.</html>"));
149        } else {
150            lblCurrentDownloadArea.setText(tr("<html><strong>Current download area</strong> (minlon, minlat, maxlon, maxlat): </html>"));
151            bboxDisplay.setText(currentArea.toBBox().toStringCSV(","));
152        }
153    }
154
155    /**
156     * Sets the current download area
157     *
158     * @param area the download area.
159     */
160    @Override
161    public void setDownloadArea(Bounds area) {
162        if (area == null)
163            return;
164        this.currentArea = area;
165        bookmarks.clearSelection();
166        updateDownloadAreaLabel();
167        actAdd.setEnabled(true);
168    }
169
170    /**
171     * The action to add a new bookmark for the current download area.
172     *
173     */
174    class AddAction extends AbstractAction {
175        AddAction() {
176            putValue(NAME, tr("Create bookmark"));
177            putValue(SMALL_ICON, ImageProvider.get("dialogs", "bookmark-new"));
178            putValue(SHORT_DESCRIPTION, tr("Add a bookmark for the currently selected download area"));
179        }
180
181        @Override
182        public void actionPerformed(ActionEvent e) {
183            if (currentArea == null) {
184                JOptionPane.showMessageDialog(
185                        Main.parent,
186                        tr("Currently, there is no download area selected. Please select an area first."),
187                        tr("Information"),
188                        JOptionPane.INFORMATION_MESSAGE
189                );
190                return;
191            }
192            Bookmark b = new Bookmark();
193            b.setName(
194                    JOptionPane.showInputDialog(
195                            Main.parent, tr("Please enter a name for the bookmarked download area."),
196                            tr("Name of location"),
197                            JOptionPane.QUESTION_MESSAGE)
198            );
199            b.setArea(currentArea);
200            if (b.getName() != null && !b.getName().isEmpty()) {
201                ((DefaultListModel<BookmarkList.Bookmark>) bookmarks.getModel()).addElement(b);
202                bookmarks.save();
203            }
204        }
205    }
206
207    class RemoveAction extends AbstractAction implements ListSelectionListener {
208        /**
209         * Constructs a new {@code RemoveAction}.
210         */
211        RemoveAction() {
212            putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
213            putValue(SHORT_DESCRIPTION, tr("Remove the currently selected bookmarks"));
214            updateEnabledState();
215        }
216
217        @Override
218        public void actionPerformed(ActionEvent e) {
219            List<Bookmark> sels = bookmarks.getSelectedValuesList();
220            if (sels == null || sels.isEmpty())
221                return;
222            for (Object sel: sels) {
223                ((DefaultListModel<Bookmark>) bookmarks.getModel()).removeElement(sel);
224            }
225            bookmarks.save();
226        }
227
228        protected final void updateEnabledState() {
229            setEnabled(bookmarks.getSelectedIndices().length > 0);
230        }
231
232        @Override
233        public void valueChanged(ListSelectionEvent e) {
234            updateEnabledState();
235        }
236    }
237
238    class RenameAction extends AbstractAction implements ListSelectionListener {
239        /**
240         * Constructs a new {@code RenameAction}.
241         */
242        RenameAction() {
243            putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
244            putValue(SHORT_DESCRIPTION, tr("Rename the currently selected bookmark"));
245            updateEnabledState();
246        }
247
248        @Override
249        public void actionPerformed(ActionEvent e) {
250            List<Bookmark> sels = bookmarks.getSelectedValuesList();
251            if (sels == null || sels.size() != 1)
252                return;
253            Bookmark b = sels.get(0);
254            Object value =
255                JOptionPane.showInputDialog(
256                        Main.parent, tr("Please enter a name for the bookmarked download area."),
257                        tr("Name of location"),
258                        JOptionPane.QUESTION_MESSAGE,
259                        null,
260                        null,
261                        b.getName()
262                );
263            if (value != null) {
264                b.setName(value.toString());
265                bookmarks.save();
266                bookmarks.repaint();
267            }
268        }
269
270        protected final void updateEnabledState() {
271            setEnabled(bookmarks.getSelectedIndices().length == 1);
272        }
273
274        @Override
275        public void valueChanged(ListSelectionEvent e) {
276            updateEnabledState();
277        }
278    }
279
280    class DoubleClickAdapter extends MouseAdapter {
281        @Override
282        public void mouseClicked(MouseEvent e) {
283            if (!(SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2))
284                return;
285            int idx = bookmarks.locationToIndex(e.getPoint());
286            if (idx < 0 || idx >= bookmarks.getModel().getSize())
287                return;
288            Bookmark b = bookmarks.getModel().getElementAt(idx);
289            parent.startDownload(b.getArea());
290        }
291    }
292}